Harikrishnan CV
Harikrishnan CV

Reputation: 911

Updating activity UI from bounded service

I am trying to make an app that calculates taxi/auto fare. The app will run as a background service and will keep updating the ride data like location,fare,distance etc in the background even after the user exits the app. The service will stop when user presses a stop button. Now here my problem is, each time the activty is resumed/restarted its resetting to initial state-as if the service stopped. How do I continuously update the UI so that the activity keeps updated so that whenever user comes to the page updated data is shown. I am runnng a STICKY service.

This is my service

public class LocationManager extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private static final String TAG = LocationManager.class.getSimpleName();
    double last_lat = -1000.0f;
    double last_lon = -1000.0f;
    double dist_total = 0.0f;
    double fare_total = 0.0f;
    long start_time = -1;
    private GoogleApiClient mGoogleApiClient;
    private Context mContext;
    private LocationRequest mLocationRequest;
    private boolean mToStartUpdates = false;
    private boolean isInited = false;
    private long mLastLocationMillis = 0;
    private SharedPreferences settings;
    String rideTime = "00h:00m:00s";
    private IBinder mBinder = new TukTukMeterBinder();
    private Timer timer = new Timer();

    public LocationManager(){}
    public void init(boolean startUpdates) {

        mToStartUpdates = startUpdates;
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
        }
    }
    public class TukTukMeterBinder extends Binder{
         LocationManager getBinder(){
            return LocationManager.this;
        }
    }

    public double getDistanceTraveled(){
        return dist_total;
    }
    public double getFare_total(){
        return fare_total;
    }
    public double getLast_lat(){
        return last_lat;
    }
    public double getLast_lon(){
        return last_lon;
    }
    public String getRideTime(){
        return rideTime;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        init(true);
    }

    @Override
    public void onConnected(Bundle bundle) {
        LogUtil.i("GoogleApiClient connection has Connected");
        isInited = true;
        if (mToStartUpdates && RequirementHelper.isLocationEnabled(mContext)) {
            createLocationRequest();
        } else {
            createLocationRequestDialog();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        LogUtil.i("Could not connect to googleApiClient" + i);
        if (mGoogleApiClient != null) {
            mGoogleApiClient.reconnect();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.getAction().equals(AppConstants.ACTION.STARTFOREGROUND_ACTION)) {
            LogUtil.i("Received Start Foreground Intent ");
            buildNotification();
            start_time = System.currentTimeMillis();
            mHandler.postDelayed(mUpdateTimeTask,1000);
        } else if (intent.getAction().equals(AppConstants.ACTION.STOPFOREGROUND_ACTION)) {
            stopForeground(true);
            stopSelf();
            mHandler.removeCallbacks(mUpdateTimeTask);
        }
        return START_STICKY;
    }

    private void buildNotification() {

        Intent notificationIntent = new Intent(this, TukTukHomeActivity.class);
        notificationIntent.setAction(AppConstants.ACTION.MAIN_ACTION);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                notificationIntent, 0);

        Bitmap icon = BitmapFactory.decodeResource(getResources(),
                R.mipmap.ic_launcher);

        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("TukTuk Meter")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(
                        Bitmap.createScaledBitmap(icon, 128, 128, false))
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .build();
        startForeground(AppConstants.NOTIFICATION_ID.FOREGROUND_SERVICE,
                notification);

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        isInited = false;
    }

    /**
     * This method will automatically creates a dialog for automatically turning on GPS without navigating to settings activity.
     */
    public void createLocationRequestDialog() {
        mLocationRequest = LocationRequest.create();
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        builder.setAlwaysShow(true);

        PendingResult<LocationSettingsResult> result =
                LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
        result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
            @Override
            public void onResult(LocationSettingsResult result) {
                final Status status = result.getStatus();
                final LocationSettingsStates state = result.getLocationSettingsStates();
                LogUtil.d("onResult state:[" + state + "]");
                LogUtil.d("onResult status:[" + status + "]");

                switch (status.getStatusCode()) {
                    case LocationSettingsStatusCodes.SUCCESS://Already have a location.

                        if (RequirementHelper.isLocationEnabled(mContext)) {
                            createLocationRequest();
                            break;
                        } else {

                        }//$fallthrough without break
                    case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                        try {
                            LogUtil.d("showing request loccation dialog");
                            // Show the dialog by calling startResolutionForResult(),
                            // and check the result in onActivityResult().
                            status.startResolutionForResult(((Activity) mContext), TukTukHomeActivity.REQUEST_ENABLE_GPS);
                        } catch (IntentSender.SendIntentException e) {
                            // Ignore the error.
                        }
                    case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                        // Location settings are not satisfied. However, we have no way to fix the
                        // settings so we won't show the diaLogUtil.

                        break;
                }
            }
        });
    }

    /**
     * The dialog to be shown to turn on location is currently disabled.
     */
    public void createLocationRequest() {
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(10 * 1000);

        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        builder.setAlwaysShow(true);
        requestLocationUpdates();
    }

    public void requestLocationUpdates() {
        if (mGoogleApiClient.isConnected() && RequirementHelper.hasAnyLocationPermission(mContext)) {
            LogUtil.d("requestLocationUpdates");
            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, LocationManager.this);
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        LogUtil.d("Fetched AdsLocation" + location);
        if (location != null) {
           // DataStorePrefManager.getInstance(mContext).saveLastKnownLocation(location);

            mLastLocationMillis = SystemClock.elapsedRealtime();
            settings = PreferenceManager.getDefaultSharedPreferences(mContext);

            double min_fare = settings.getFloat(DataStorePrefManager.KEY_BASE_FARE, 0.0f);
            double min_dist = settings.getFloat(DataStorePrefManager.KEY_MIN_DISTANCE, 0.00f);
            double rate_per_km = settings.getFloat(DataStorePrefManager.KEY_KM_FARE, 0.00f);
            Log.i(TAG, location.getLatitude() + " , " + location.getLongitude());
            if (last_lat < -90 || last_lon < -180) {
                last_lat = location.getLatitude();
                last_lon = location.getLongitude();
            } else {
                double lat1 = Math.toRadians(last_lat);
                double lon1 = Math.toRadians(last_lon);

                double lat2 = Math.toRadians(location.getLatitude());
                double lon2 = Math.toRadians(location.getLongitude());

                double R = 6371.0f;
                double dLat = (lat2 - lat1);
                double dLon = (lon2 - lon1);
                double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                        Math.cos(lat1) * Math.cos(lat2) *
                                Math.sin(dLon / 2) * Math.sin(dLon / 2);
                double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                double d = R * c;


                if (d > 0.05 && location.getAccuracy() < 50)    // Add only if delta > 50 m and uncertainty < 50m
                {
                    dist_total += d;

                    if (dist_total > min_dist) {
                        fare_total = min_fare + (dist_total - min_dist) * rate_per_km;
                    } else {
                        fare_total = min_fare;
                    }

                    Log.i("Distance", Double.toString(dist_total));

                    last_lat = location.getLatitude();
                    last_lon = location.getLongitude();

                }
            }
            DecimalFormat df = new DecimalFormat("#.0");
            df.format(fare_total);
            df.format(dist_total);
        } else return;

    }

    public void onDestroy() {
        isInited = false;
        mHandler.removeCallbacks(mUpdateTimeTask);
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    public boolean isInited() {
        return isInited;
    }

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    private Runnable mUpdateTimeTask = new TimerTask() {
        @Override
        public void run() {
            int hrs = 0,min = 0,sec= 0;
            if(start_time != -1)
            {
                int interval = (int) (System.currentTimeMillis() - start_time)/1000;
                sec = interval%60;
                min = interval/60;
                hrs = interval/3600;

                rideTime = String.format("%02dh:%02dm:%02ds", hrs,min,sec);

            }
            if(isInited){
                mHandler.postDelayed(this,1000);
            }

        }};

}

And this is my Activity

public class TukTukHomeActivity extends AppCompatActivity implements View.OnClickListener, NavigationView.OnNavigationItemSelectedListener {

LocationManager mLocationManager;
public static final int REQUEST_ENABLE_GPS = 100;
boolean bound = false;
TextView rideDistance, totalFare, rideTotalTime, mapsTv;
private DrawerLayout mDrawerLayout;
double distance = 0;
double fareTotal = 0;
String rideTime = "00h:00m:00s";
boolean isRunning = false;

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        LocationManager.TukTukMeterBinder mBinder = (LocationManager.TukTukMeterBinder) service;
        mLocationManager = mBinder.getBinder();
        bound = true;
        initUI();
        displayDistance();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        bound = false;
        mLocationManager = null;
    }
};


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_home);
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    initUI();
    findViewById(R.id.startRide).setOnClickListener(this);
    toolbar.setNavigationIcon(R.drawable.menu);
    toolbar.setNavigationOnClickListener(this);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, mDrawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);
    mDrawerLayout.addDrawerListener(toggle);
    toggle.syncState();
    NavigationView navigationView = (NavigationView) findViewById(R.id.navigation);
    navigationView.setNavigationItemSelectedListener(this);
}
private void initUI(){
    rideDistance = (TextView)findViewById(R.id.rideDistance) ;
    totalFare = (TextView)findViewById(R.id.fareTotal);
    rideTotalTime = (TextView)findViewById(R.id.rideTime) ;
    mapsTv = (TextView)findViewById(R.id.openMaps);
    mapsTv.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.startRide:
            startMeterService();
            break;
        case R.id.drawerLayout:
            mDrawerLayout.openDrawer(GravityCompat.START);
            break;
        case R.id.openMaps:
            startActivity(new Intent(this,TukTukMaps.class));
            break;
    }
}

@Override
public void onBackPressed() {
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawerLayout);
    if (drawer.isDrawerOpen(GravityCompat.START)) {
        drawer.closeDrawer(GravityCompat.START);
    } else {
        super.onBackPressed();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.items, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        startActivity(new Intent(TukTukHomeActivity.this, TukTukSettings.class));
        return true;
    }
    if(id == R.id.navigation){
        mDrawerLayout.openDrawer(GravityCompat.START);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

private void startMeterService() {
    Intent startIntent = new Intent(this, LocationManager.class);
    startIntent.setAction(AppConstants.ACTION.STARTFOREGROUND_ACTION);
    startService(startIntent);
    bindService(startIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
    displayDistance();
}

@Override
protected void onResume() {
    super.onResume();
    initUI();
    displayDistance();
}

private void displayDistance() {
    final Handler handler = new Handler();
    handler.post(new Runnable() {
        @Override
        public void run() {

            if (mLocationManager != null) {
                distance = mLocationManager.getDistanceTraveled();
                fareTotal = mLocationManager.getFare_total();
                rideTime = mLocationManager.getRideTime();
            }
            rideDistance.setText(String.valueOf(distance)+"km");
            totalFare.setText(getResources().getString(R.string.min_fare_symbol)+String.valueOf(fareTotal));
            rideTotalTime.setText(String.valueOf(rideTime));
            handler.postDelayed(this, 1000);
        }
    });
}

@Override
protected void onStop() {
    super.onStop();
    if (bound) {
        unbindService(mServiceConnection);
        bound = false;
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case REQUEST_ENABLE_GPS:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    startMeterService();
                    break;
                case Activity.RESULT_CANCELED:
                    break;
            }
            break;
    }
}

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.about_us:
            startActivity(new Intent(TukTukHomeActivity.this,AboutUs.class));
            mDrawerLayout.closeDrawers();
            return true;
    }
    return true;
}

}

Upvotes: 0

Views: 400

Answers (2)

albeee
albeee

Reputation: 1472

  1. Try to use the preference settings and save your values in service class.
  2. Add logs to see whether you're receiving location updates and your values are getting printed or not.
  3. handler.postDelayed(this, 1000); may not be required if your service is updating the values.

Upvotes: 0

Aditya Desai
Aditya Desai

Reputation: 425

To send data from service to activity

Intent i = new Intent();
i.setAction(SOME_ACTION_NAME);
i.setExtra(KEY,VALUE);
context.sendBroadcast(i);

Use a broadcase reciever inside the activity

BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(SOME_ACTION_NAME) {
           //read your extra from intent
            }
        }
 }

add your action name to an intent filter and register the broadcast reciever.

Upvotes: 1

Related Questions