Android Open Source - speedofsound Sound Service






From Project

Back to project page speedofsound.

License

The source code is released under:

GNU General Public License

If you think the Android project speedofsound listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package net.codechunk.speedofsound.service;
/*  ww  w .  j ava  2 s .com*/
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;

import net.codechunk.speedofsound.R;
import net.codechunk.speedofsound.SongTracker;
import net.codechunk.speedofsound.SpeedActivity;
import net.codechunk.speedofsound.util.AppPreferences;
import net.codechunk.speedofsound.util.AverageSpeed;

/**
 * The main sound control service.
 *
 * Responsible for adjusting the volume based on the current speed. Can be
 * started and stopped externally, but is largely independent.
 */
public class SoundService extends Service {
  private static final String TAG = "SoundService";

  /**
   * Intent extra to set the tracking state.
   */
  public static final String SET_TRACKING_STATE = "set-tracking-state";

  /**
   * Broadcast to notify that tracking has started or stopped.
   */
  public static final String TRACKING_STATE_BROADCAST = "tracking-state-changed";

  /**
   * Location, speed, and sound update broadcast.
   */
  public static final String LOCATION_UPDATE_BROADCAST = "location-update";

  /**
   * The current tracking state.
   */
  private boolean tracking = false;

  private LocalBroadcastManager localBroadcastManager;
  private SoundServiceManager soundServiceManager = new SoundServiceManager();

  private SharedPreferences settings;
  private VolumeThread volumeThread = null;
  private LocationManager locationManager;
  private LocalBinder binder = new LocalBinder();
  private VolumeConversion volumeConversion;
  private SongTracker songTracker;

  /**
   * Start up the service and initialize some values. Does not start tracking.
   */
  @Override
  public void onCreate() {
    Log.d(TAG, "Service starting up");

    // set up preferences
    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    this.settings = PreferenceManager.getDefaultSharedPreferences(this);
    AppPreferences.runUpgrade(this);
    AppPreferences.updateNativeSpeeds(settings);

    // register handlers & audio
    this.localBroadcastManager = LocalBroadcastManager.getInstance(this);
    this.locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    this.volumeConversion = new VolumeConversion();
    this.volumeConversion.onSharedPreferenceChanged(this.settings, null); // set initial
    this.songTracker = SongTracker.getInstance(this);

    // activation broadcasts
    IntentFilter activationFilter = this.soundServiceManager.activationIntents();
    this.registerReceiver(this.soundServiceManager, activationFilter);
  }

  /**
   * Handle a start command.
   *
   * Return sticky mode to tell Android to keep the service active.
   */
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "Start command received");

    // register pref watching
    this.settings.registerOnSharedPreferenceChangeListener(this.volumeConversion);

    // check if we've been commanded to start or stop tracking
    if (intent != null) {
      Bundle extras = intent.getExtras();
      if (extras != null && extras.containsKey(SoundService.SET_TRACKING_STATE)) {
        Log.v(TAG, "Commanded to change state");
        if (extras.getBoolean(SoundService.SET_TRACKING_STATE)) {
          this.startTracking();
        } else {
          this.stopTracking();
        }
      }
    }

    return START_STICKY;
  }

  /**
   * Service shut-down.
   */
  @Override
  public void onDestroy() {
    Log.d(TAG, "Service shutting down");

    this.settings.unregisterOnSharedPreferenceChangeListener(this.volumeConversion);
  }

  public boolean isTracking() {
    return this.tracking;
  }

  /**
   * Start tracking. Find the best location provider (likely GPS), create an
   * ongoing notification, and request location updates.
   */
  public void startTracking() {
    // ignore requests when we're already tracking
    if (this.tracking) {
      return;
    }

    // request updates
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_FINE);
    String provider = this.locationManager.getBestProvider(criteria, true);
    if (provider != null) {
      this.locationManager.requestLocationUpdates(provider, 0, 0, this.locationUpdater);
    } else {
      Toast toast = Toast.makeText(this, this.getString(R.string.no_location_providers), Toast.LENGTH_LONG);
      toast.show();
      return;
    }

    // start a new route
    this.songTracker.startRoute();

    // start up the volume thread
    if (this.volumeThread == null) {
      this.volumeThread = new VolumeThread(this);
      this.volumeThread.start();
    }

    // show the notification
    startForeground(R.string.notification_text, getNotification());

    // let everyone know
    Intent intent = new Intent(SoundService.TRACKING_STATE_BROADCAST);
    intent.putExtra("tracking", true);
    SoundService.this.localBroadcastManager.sendBroadcast(intent);

    this.tracking = true;
    Log.d(TAG, "Tracking started with location provider " + provider);
  }

  /**
   * Build a fancy-pants notification.
   */
  private Notification getNotification() {
    // force foreground with an ongoing notification
    Intent notificationIntent = new Intent(this, SpeedActivity.class);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setContentTitle(getString(R.string.app_name));
    builder.setContentText(getString(R.string.notification_text));
    builder.setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
    builder.setTicker(getString(R.string.ticker_text));
    builder.setSmallIcon(R.drawable.ic_notification);
    builder.setWhen(System.currentTimeMillis());

    // 4.1+ actions
    Intent stopIntent = new Intent(this, SoundService.class);
    stopIntent.putExtra(SoundService.SET_TRACKING_STATE, false);
    builder.addAction(R.drawable.ic_stop, getString(R.string.stop),
        PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_ONE_SHOT));
    return builder.build();
  }

  /**
   * Stop tracking. Remove the location updates and notification.
   */
  public void stopTracking() {
    // don't do anything if we're not tracking
    if (!this.tracking) {
      return;
    }

    // shut off the volume thread
    if (this.volumeThread != null) {
      this.volumeThread.interrupt();
      this.volumeThread = null;
    }

    // end the current route
    this.songTracker.endRoute();

    // disable location updates
    this.locationManager.removeUpdates(this.locationUpdater);

    // remove notification and go to background
    stopForeground(true);

    // let everyone know
    Intent intent = new Intent(SoundService.TRACKING_STATE_BROADCAST);
    intent.putExtra("tracking", false);
    SoundService.this.localBroadcastManager.sendBroadcast(intent);

    this.tracking = false;
    Log.d(TAG, "Tracking stopped");
  }

  /**
   * Custom location listener. Triggers volume changes based on the current
   * average speed.
   */
  private LocationListener locationUpdater = new LocationListener() {
    private Location previousLocation = null;

    /**
     * Change the volume based on the current average speed. If speed is not
     * available from the current location provider, calculate it from the
     * previous location. After updating the average and updating the
     * volume, send out a broadcast notifying of the changes.
     */
    public void onLocationChanged(Location location) {
      // some stupid phones occasionally send a null location.
      // who does that, seriously.
      if (location == null)
        return;

      // use the GPS-provided speed if available
      float speed;
      if (location.hasSpeed()) {
        speed = location.getSpeed();
      } else {
        // speed fall-back (mostly for the emulator)
        if (this.previousLocation != null) {
          // get the distance between this and the previous update
          float meters = previousLocation.distanceTo(location);
          float timeDelta = location.getTime() - previousLocation.getTime();

          Log.v(TAG, "Location distance: " + meters);

          // convert to meters/second
          speed = 1000 * meters / timeDelta;
        } else {
          speed = 0;
        }

        this.previousLocation = location;
      }

      float volume = SoundService.this.volumeConversion.speedToVolume(speed);
      SoundService.this.volumeThread.setTargetVolume(volume);

      // send out a local broadcast with the details
      Intent intent = new Intent(SoundService.LOCATION_UPDATE_BROADCAST);
      intent.putExtra("location", location);
      intent.putExtra("speed", speed);
      intent.putExtra("volumePercent", (int) (volume * 100));
      SoundService.this.localBroadcastManager.sendBroadcast(intent);
    }

    public void onProviderDisabled(String provider) {
    }

    public void onProviderEnabled(String provider) {
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {
    }
  };

  /**
   * Service-level access for external classes and activities.
   */
  public class LocalBinder extends Binder {
    /**
     * Return the service associated with this binder.
     */
    public SoundService getService() {
      return SoundService.this;
    }
  }

  /**
   * Return the binder associated with this service.
   */
  @Override
  public IBinder onBind(Intent intent) {
    return this.binder;
  }

}




Java Source Code List

net.codechunk.speedofsound.LocaleActivity.java
net.codechunk.speedofsound.MapperActivity.java
net.codechunk.speedofsound.PreferencesActivity.java
net.codechunk.speedofsound.SongTracker.java
net.codechunk.speedofsound.SpeedActivity.java
net.codechunk.speedofsound.players.AndroidMusicPlayer.java
net.codechunk.speedofsound.players.BasePlayer.java
net.codechunk.speedofsound.players.HTCPlayer.java
net.codechunk.speedofsound.players.LastFmAPIPlayer.java
net.codechunk.speedofsound.players.SLSAPIPlayer.java
net.codechunk.speedofsound.players.SamsungPlayer.java
net.codechunk.speedofsound.players.WinampPlayer.java
net.codechunk.speedofsound.service.SoundServiceManager.java
net.codechunk.speedofsound.service.SoundService.java
net.codechunk.speedofsound.service.VolumeConversion.java
net.codechunk.speedofsound.service.VolumeThread.java
net.codechunk.speedofsound.util.AppPreferences.java
net.codechunk.speedofsound.util.AverageSpeed.java
net.codechunk.speedofsound.util.ColorCreator.java
net.codechunk.speedofsound.util.SliderPreference.java
net.codechunk.speedofsound.util.SongInfo.java
net.codechunk.speedofsound.util.SpeedConversions.java
net.codechunk.speedofsound.util.SpeedSliderPreference.java