me.trashout.service.TrashHunterService.java Source code

Java tutorial

Introduction

Here is the source code for me.trashout.service.TrashHunterService.java

Source

/*
 * TrashOut is an environmental project that teaches people how to recycle
 * and showcases the worst way of handling waste - illegal dumping.All you need is a smart phone.
 * 
 * 
 * There are 10 types of programmers - those who are helping TrashOut and those who are not.
 * Clean up our code, so we can clean up our planet.
 * Get in touch with us: help@trashout.ngo
 * 
 * Copyright 2017 TrashOut, n.f.
 * 
 * This file is part of the TrashOut project.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * See the GNU General Public License for more details: <https://www.gnu.org/licenses/>.
 */

package me.trashout.service;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.google.android.gms.maps.model.LatLng;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import me.trashout.Configuration;
import me.trashout.R;
import me.trashout.activity.MainActivity;
import me.trashout.activity.base.BaseActivity;
import me.trashout.api.ApiServer;
import me.trashout.api.param.ApiGetTrashListParam;
import me.trashout.fragment.TrashListFragment;
import me.trashout.model.Constants;
import me.trashout.model.Trash;
import me.trashout.model.TrashHunterState;
import me.trashout.model.serialize.TrashSizeSerializer;
import me.trashout.model.serialize.TrashStatusSerializer;
import me.trashout.model.serialize.TrashTypeSerializer;
import me.trashout.utils.ApplicationLifecycleHandler;
import me.trashout.utils.PositionUtils;
import me.trashout.utils.PreferencesHandler;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * @author Miroslav Cupalka
 * @package me.trashout.service
 * @since 01.03.2017
 */
public class TrashHunterService extends Service {

    private static final int NOTIFICATION_ID = 47787;

    public static final String BUNDLE_LAST_POSITION = "BUNDLE_LAST_POSITION";

    private LocationManager locationManager;
    private LocationListener locationListener;
    private TrashHunterState trashHunterState;

    private HashMap<Long, Trash> lastTrashList;
    private ApiServer mApiServer;

    private Handler stopSelfHandler = new Handler();

    public int onStartCommand(Intent intent, int flags, int startId) {

        if (intent != null) {
            LatLng lastPosition = intent.getParcelableExtra(BUNDLE_LAST_POSITION);
            Log.d("TrashHunter", "onStartCommand - lastPosition = " + lastPosition);

            if (lastPosition != null) {
                try {
                    makeUseOfNewLocation(lastPosition);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //        createNotification(1, PreferencesHandler.getTrashHunterState(getBaseContext()).getAreaSize());
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("TrashHunter", "onCreate - this = " + this.toString());
        try {
            updateListener(true, null);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        setupLocationListener();
    }

    private void setupLocationListener() {

        trashHunterState = PreferencesHandler.getTrashHunterState(getBaseContext());
        Log.d("TrashHunter", "setupLocationListener: " + trashHunterState);

        if (trashHunterState != null && trashHunterState.isTrashHunterActive()) {

            stopSelfHandler.postDelayed(stopSelfRunable, trashHunterState.getTrashHunterRemainingTime());

            locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);

            locationListener = new LocationListener() {
                public void onLocationChanged(Location location) {
                    try {
                        makeUseOfNewLocation(new LatLng(location.getLatitude(), location.getLongitude()));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                public void onStatusChanged(String provider, int status, Bundle extras) {
                    Log.d("TrashHunter", "onStatusChanged: provider = " + provider + ", status = " + status);
                }

                public void onProviderEnabled(String provider) {
                    Log.d("TrashHunter", "onProviderEnabled: " + provider);
                }

                public void onProviderDisabled(String provider) {
                    Log.d("TrashHunter", "onProviderDisabled: " + provider);
                }
            };

            if (ActivityCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(this,
                            Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                    trashHunterState.getUpdateStatusTime(), 0, locationListener);
        } else {
            Toast.makeText(this, "Trash hunter not activate.", Toast.LENGTH_SHORT).show();
            this.stopSelf();
        }
    }

    private void makeUseOfNewLocation(LatLng location) throws IOException {
        Log.d("TrashHunter", "makeUseOfNewLocation: new location = " + location + " - this = " + this.toString());
        Log.d("TrashHunter", "makeUseOfNewLocation: "
                + PositionUtils.centerPointAndRadiusToBounds(location, trashHunterState.getAreaSize()));

        Call<List<Trash>> call = getApiServer()
                .getTrashList(ApiGetTrashListParam.getTrashHunterOptions(location, trashHunterState.getAreaSize()));
        call.enqueue(new Callback<List<Trash>>() {
            @Override
            public void onResponse(Call<List<Trash>> call, Response<List<Trash>> response) {
                if (response.isSuccessful()) {

                    int trashChangeCount = 0;
                    if ((trashChangeCount = getTrashListChangeCount(response.body())) > 0
                            && !isApplicationOnTop()) {
                        createNotification(trashChangeCount, trashHunterState.getAreaSize());
                    }

                    try {
                        updateListener(false, response.body().size());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void onFailure(Call<List<Trash>> call, Throwable t) {
            }
        });

    }

    private int getTrashListChangeCount(List<Trash> trashList) {

        if (lastTrashList == null) {
            lastTrashList = new HashMap<>();

            for (Trash trash : trashList) {
                lastTrashList.put(trash.getId(), trash);
            }

            return trashList.size();
        }

        HashMap<Long, Trash> newTrashList = new HashMap<>(trashList.size());

        int changeItemCount = 0;
        int addItemCount = 0;

        for (Trash trash : trashList) {
            Trash oldTrash = lastTrashList.get(trash.getId());

            if (oldTrash == null) {
                addItemCount++;
            } else if (!oldTrash.getStatus().equals(trash.getStatus())) {
                changeItemCount++;
            }

            newTrashList.put(trash.getId(), trash);
        }

        lastTrashList.clear();
        lastTrashList.putAll(newTrashList);

        Log.d("TrashHunter",
                "checkTrashListChange: changeItemCount = " + changeItemCount + ", addItemCount = " + addItemCount);
        return changeItemCount + addItemCount;
    }

    private void updateListener(final boolean changeSate, final Integer changeTrashCount) throws RemoteException {
        if (onTrashHunterChangeListeners != null && !onTrashHunterChangeListeners.isEmpty()) {
            if (changeSate) {
                for (ITrashHunterChangeListener onTrashHunterChangeListener : onTrashHunterChangeListeners) {
                    onTrashHunterChangeListener.onTrashHunterStateChange();
                }
            }

            if (changeTrashCount != null) {
                for (ITrashHunterChangeListener onTrashHunterChangeListener : onTrashHunterChangeListeners) {
                    onTrashHunterChangeListener.onTrashHunterTrashCountChange(changeTrashCount);
                }
            }
        }
    }

    private boolean isApplicationOnTop() {
        return ApplicationLifecycleHandler.isApplicationVisible();
    }

    private void createNotification(int trashCount, int trashHunterArea) {
        Log.d("TrashHunter", ".....createNotification..... trashHunterArea - " + trashHunterArea);

        Intent viewIntent = BaseActivity.generateIntent(this, TrashListFragment.class.getName(),
                TrashListFragment.generateBundle(true, trashHunterArea), MainActivity.class);

        viewIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        viewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        PendingIntent viewPendingIntent = PendingIntent.getActivity(this, 0, viewIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);

        Context context = getBaseContext();
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
                .setSmallIcon(getNotificationIcon())
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary))
                .setContentTitle(context.getString(R.string.app_name))
                .setContentText(String.format(getString(R.string.notification_new_trash_formatter), trashCount))
                .setAutoCancel(true).setContentIntent(viewPendingIntent);

        NotificationManager mNotificationManager = (NotificationManager) context
                .getSystemService(Context.NOTIFICATION_SERVICE);

        Notification notif = mBuilder.build();
        notif.defaults |= Notification.DEFAULT_SOUND;
        notif.defaults |= Notification.DEFAULT_VIBRATE;

        mNotificationManager.notify(NOTIFICATION_ID, notif);
    }

    private int getNotificationIcon() {
        boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
        return useWhiteIcon ? R.drawable.ic_notification : R.mipmap.ic_launcher;
    }

    private Runnable stopSelfRunable = new Runnable() {
        @Override
        public void run() {
            Log.d("TrashHunter", "run: stopSelf");
            if (locationManager != null) {
                try {
                    locationManager.removeUpdates(locationListener);
                } catch (SecurityException e) {
                }
            }
            TrashHunterService.this.stopSelf();
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("TrashHunter", "onDestroy: ");

        if (locationManager != null) {
            if (ContextCompat.checkSelfPermission(getApplicationContext(),
                    Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                    || ContextCompat.checkSelfPermission(getApplicationContext(),
                            Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                locationManager.removeUpdates(locationListener);
            }
        }

        if (stopSelfHandler != null) {
            stopSelfHandler.removeCallbacks(stopSelfRunable);
        }
    }

    // Prepare api server
    private ApiServer getApiServer() {
        if (mApiServer == null) {
            Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
                    .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
                    .registerTypeAdapter(Constants.TrashSize.class, new TrashSizeSerializer())
                    .registerTypeAdapter(Constants.TrashStatus.class, new TrashStatusSerializer())
                    .registerTypeAdapter(Constants.TrashType.class, new TrashTypeSerializer()).create();

            Retrofit ra = new Retrofit.Builder().baseUrl(Configuration.API_BASE_URL).client(getOkHttpClient())
                    .addConverterFactory(GsonConverterFactory.create(gson)).build();

            mApiServer = ra.create(ApiServer.class);
        }
        return mApiServer;
    }

    private OkHttpClient getOkHttpClient() {

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient okHttpClient = new OkHttpClient.Builder().addNetworkInterceptor(new StethoInterceptor())
                .addInterceptor(logging).addInterceptor(new Interceptor() {
                    @Override
                    public okhttp3.Response intercept(Chain chain) throws IOException {

                        String firebaseToken = PreferencesHandler.getFirebaseToken(getBaseContext());

                        Request.Builder newRequestBuilder = chain.request().newBuilder().addHeader("Accept",
                                "application/json");

                        if (!TextUtils.isEmpty(firebaseToken)) {
                            newRequestBuilder.addHeader("X-Token", firebaseToken);
                        }

                        return chain.proceed(newRequestBuilder.build());
                    }
                }).build();

        return okHttpClient;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return iRemoteServiceBinder;
    }

    private List<ITrashHunterChangeListener> onTrashHunterChangeListeners = new ArrayList<ITrashHunterChangeListener>();

    private final IRemoteService.Stub iRemoteServiceBinder = new IRemoteService.Stub() {

        @Override
        public void addOnTrashHunterChangeListener(ITrashHunterChangeListener listener) throws RemoteException {
            onTrashHunterChangeListeners.add(listener);
        }

        @Override
        public void removeOnTrashHunterChangeListener(ITrashHunterChangeListener listener) throws RemoteException {
            onTrashHunterChangeListeners.remove(listener);
        }
    };
}