com.kyloth.serleena.sensors.BackgroundLocationManager.java Source code

Java tutorial

Introduction

Here is the source code for com.kyloth.serleena.sensors.BackgroundLocationManager.java

Source

///////////////////////////////////////////////////////////////////////////////
// 
// This file is part of Serleena.
// 
// The MIT License (MIT)
//
// Copyright (C) 2015 Antonio Cavestro, Gabriele Pozzan, Matteo Lisotto, 
//   Nicola Mometto, Filippo Sestini, Tobia Tesan, Sebastiano Valle.    
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

/**
 * Name: BackgroundLocationManager.java
 * Package: com.kyloth.serleena.sensors
 * Author: Filippo Sestini
 *
 * History:
 * Version  Programmer        Changes
 * 1.0.0    Filippo Sestini   Creazione file e scrittura
 *                            codice e documentazione Javadoc
 */

package com.kyloth.serleena.sensors;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.content.WakefulBroadcastReceiver;

import com.kyloth.serleena.common.GeoPoint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * Implementa IBackgroundLocationManager.
 *
 * @use Viene utilizzato da LocationReachedManager, dietro interfaccia, per ricevere aggiornamenti sulla posizione utente in background, anche se il dispositivo  il sleep mode.
 * @field myResultReceiver : ServiceResultReceiver Oggetto utilizzato per ricevere risultati da un servizio.
 * @field am : AlarmManager Gestore degli allarmi di Android.
 * @field context : Context Contesto dell'applicazione.
 * @field pendingIntent : PendingIntent Oggetto rappresentante un allarme schedulato.
 * @field location : GeoPoint Ultima posizione utente rilevata.
 * @field updateInterval : int Intervallo di aggiornamento della posizione.
 * @field observers : List<ILocationObserver> Observers collegati all'istanza.
 * @author Filippo Sestini <sestini.filippo@gmail.com>
 * @version 1.0.0
 */
public class BackgroundLocationManager extends WakefulBroadcastReceiver
        implements IBackgroundLocationManager, ServiceResultReceiver.Receiver {

    private ServiceResultReceiver myResultReceiver;
    private AlarmManager am;
    private Context context;
    private List<ILocationObserver> observers;

    private PendingIntent pendingIntent;
    private GeoPoint location;
    private int updateInterval;

    /**
     * Crea un nuovo oggetto BackgroundLocationManager.
     *
     * @param context Contesto dal quale creare l'oggetto.
     * @param am Gestore degli allarmi di Android.
     * @param updateInterval Intervallo ogni quanto il gestore deve
     *         svegliare il sistema e richiedere
     *         aggiornamenti sulla posizione, in
     *         secondi.
     */
    public BackgroundLocationManager(Context context, AlarmManager am, int updateInterval) {
        if (context == null)
            throw new IllegalArgumentException("Illegal null context");
        if (am == null)
            throw new IllegalArgumentException("Illegal null alarm manager");
        if (updateInterval < 60)
            throw new IllegalArgumentException("Illegal interval");

        this.updateInterval = updateInterval;
        myResultReceiver = new ServiceResultReceiver(new Handler());
        myResultReceiver.setReceiver(this);
        this.am = am;
        this.context = context;
        this.observers = new ArrayList<>();
    }

    /**
     * Implementa IBackgroundLocationManager.attachObserver().
     *
     * Se vi  almeno un Observer registrato, viene avviato l'aggiornamento
     * sulla posizione.
     *
     * @param observer Observer da registrare. Se null, viene sollevata
     *                 un'eccezione IllegalArgumentException.
     */
    @Override
    public synchronized void attachObserver(ILocationObserver observer) {
        if (observer == null)
            throw new IllegalArgumentException("Illegal null observer");

        if (!observers.contains(observer)) {
            this.observers.add(observer);
            if (observers.size() == 1)
                acquireResources();
        }
    }

    /**
     * Implementa IBackgroundLocationManager.detachObserver().
     *
     * Se non vi  alcun observer registrato, l'aggiornamento viene disattivato.
     *
     * @param observer Observer la cui registrazione deve essere cancellata.
     *                 Se null, viene sollevata un'eccezione
     *                 IllegalArgumentException.
     */
    @Override
    public synchronized void detachObserver(ILocationObserver observer) {
        if (observer == null)
            throw new IllegalArgumentException("Illegal null observer");

        if (observers.size() == 1 && this.observers.contains(observer))
            releaseResources();

        this.observers.remove(observer);
    }

    /**
     * Implementa IBackgroundLocationManager.notifyObservers().
     */
    @Override
    public void notifyObservers() {
        List<ILocationObserver> copy = new ArrayList<>(this.observers);
        if (location != null)
            for (ILocationObserver o : copy)
                o.onLocationUpdate(location);
    }

    /**
     * Ridefinisce WakefulBroadcastReceiver.onReceive().
     *
     * Viene chiamato dal sistema ad ogni tick dell'allarme, svegliando il
     * dispositivo dalla sleep mode se necessario.
     *
     * Ad ogni evento di allarme, viene avviato un servizio per il recupero
     * in background della posizione utente.
     *
     * @param context Contesto nel quale avviene la ricezione.
     * @param intent Intent ricevuto.
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, LocationService.class);
        service.putExtra("receiverTag", myResultReceiver);
        startWakefulService(context, service);
    }

    /**
     * Implementa ServiceResultReceiver.Receiver.onReceiveResult().
     *
     * Viene utilizzato dal servizio avviato per segnalare i risultati
     * relativi alla posizione utente. Questi dati vengono segnalari poi agli
     * observer.
     *
     * @param resultCode Codice del risultato.
     * @param resultData Dati del risultato.
     */
    @Override
    public void onReceiveResult(int resultCode, Bundle resultData) {
        this.location = new GeoPoint(resultData.getDouble("latitude"), resultData.getDouble("longitude"));
        notifyObservers();
    }

    private void acquireResources() {
        context.registerReceiver(this, new IntentFilter("SERLEENA_ALARM"));

        Intent intentToFire = new Intent("SERLEENA_ALARM");

        if (pendingIntent != null)
            am.cancel(pendingIntent);

        pendingIntent = PendingIntent.getBroadcast(context, new Random().nextInt(), intentToFire,
                PendingIntent.FLAG_CANCEL_CURRENT);

        am.setInexactRepeating(AlarmManager.RTC_WAKEUP, 0, updateInterval * 1000, pendingIntent);
    }

    private void releaseResources() {
        am.cancel(pendingIntent);
        pendingIntent = null;
        context.stopService(new Intent(context, LocationService.class));
        context.unregisterReceiver(this);
    }

}