I'm Making an app which has 5 different notifications at different times every day. it works perfectly but after beta testing some users are complaining that some alarm fire again at wrong times I haven't faced this problem before and I don't know how to trace the problem so I can fix it. This is how I create the alarm:
Manager Class:(which has all the functions of the alarm)
public static Integer DEFAULT_SILENT_DURATION = 20 * 60 * 1000;
public static Integer DEFAULT_SILENT_START= 2 * 60 * 1000;
public Manager(Context applicationContext) {
this.context = applicationContext;
}
public static void acquireScreen(Context context) {
PowerManager pm = (PowerManager) context.getApplicationContext()
.getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = pm
.newWakeLock(
(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
| PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP),
"TAG");
wakeLock.acquire();
Log.v("Check Manager acquireScreen","YES");
}
public static void releaseScreen(Context context) {
KeyguardManager keyguardManager = (KeyguardManager) context
.getApplicationContext().getSystemService(
Context.KEYGUARD_SERVICE);
KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("TAG");
keyguardLock.disableKeyguard();
Log.v("Check Manager releaseScreen","YES");
}
public static void initPrayerAlarm(Service service,
Class<PrayerReceiver> receiver) {
Manager.prayerService = service; // we may need it ?
Manager.prayerIntet = new Intent(service, receiver);
Manager.prayerPendingIntent = PendingIntent
.getBroadcast(service, 1234432, Manager.prayerIntet,
PendingIntent.FLAG_UPDATE_CURRENT);
Manager.prayerAlarmManager = (AlarmManager) service
.getSystemService(Context.ALARM_SERVICE);
Manager.prayerAlarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + 1000, Manager.prayerPendingIntent);
Log.v("Check Manager initPrayerAlarm",""+System.currentTimeMillis() + 1000);
}
public static void updatePrayerAlarm(long newTimeInterval) {
Manager.prayerAlarmManager.set(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + newTimeInterval,
Manager.prayerPendingIntent);
Log.v("Check Manager updatePrayerAlarm",""+System.currentTimeMillis() + newTimeInterval);
}
public void restartPrayerService(Activity activty) {
Intent intent = new Intent(activty, PrayerService.class);
context.startService(intent);
Log.v("Check Manager restartPrayerService","YES");
}
In my MainActivity I call Manager restartPrayerService function
In PrayerService I call as below:
Manager.initPrayerState(this);
Manager.initPrayerAlarm(this, PrayerReceiver.class);
and then register the receiver.
PrayerReceiver:
public class PrayerReceiver extends BroadcastReceiver {
static PrayerState prayerState;
private AudioManager am;
private Context context;
private SharedPreferences pref;
private Editor editor;
private int silentDuration;
private int silentStart ;
private int delayMilliSeconds = 1000 * 60; // one minute by default.
private Object obj;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
pref = PreferenceManager.getDefaultSharedPreferences(this.context);
Manager m = new Manager(context);
Preference p = m.getPreference();
this.silentDuration = p.getSilentDuration();
this.silentStart = p.getSilentStart();
editor = pref.edit();
try {
prayerState = Manager.getPrayerState();
am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
switch (prayerState.getCurrentState()) {
case PrayerState.WAITING_AZAN:
{
Log.v("Check PrayerReceiver PrayerState","WAITING_AZAN");
onWaitingAzan();
}
break;
case PrayerState.DOING_AZAN:
{
Log.v("Check PrayerReceiver PrayerState","DOING_AZAN");
onDoingAzan();
}
break;
case PrayerState.WAITING_PRAYER:
{
Log.v("Check PrayerReceiver PrayerState","WAITING_PRAYER");
onWaitingPrayer();
}
break;
}
} catch (Exception e) {
// TODO Auto-generated catch block
Log.v("Check PrayerReceiver PrayerState","ERROR");
}
}
public int getDelayMilliSeconds() {
return delayMilliSeconds;
}
public void setDelayMilliSeconds(int delayMilliSeconds) {
this.delayMilliSeconds = delayMilliSeconds;
}
private void onWaitingAzan() {
try {
boolean isRingerModeChangedToSilent = pref.getBoolean(
"isRingerModeChangedToSilent", false);
if (isRingerModeChangedToSilent == true) {
am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
editor.putBoolean("isRingerModeChangedToSilent", false);
editor.commit();
}
// What is the remaining time until the next prayer ?
Date date = new Date();
int dd = date.getDate();
int mm = date.getMonth() + 1;
int yy = date.getYear() + 1900;
int h = date.getHours();
int m = date.getMinutes();
int s = date.getSeconds();
int nearestPrayerTime = Manager.computeNearestPrayerTime(context,
h, m, s, yy, mm, dd);
int deffTime = TimeHelper.different((h * 3600 + m * 60 + s),
nearestPrayerTime);
deffTime = deffTime * 1000; // to milliseconds
// ok , come back after X seconds to do the Azan
prayerState.setNextState(PrayerState.DOING_AZAN);
Manager.updatePrayerAlarm(deffTime);
} catch (Exception e) {
Log.v("Check PrayerReceiver onWaitingAzan","ERROR");
}
}
private void onDoingAzan() {
prayerState.setNextState(PrayerState.WAITING_PRAYER);
int delay = this.silentStart;
if(delay < 2000*60)
delay =2000*60; // two minutes - at lease
Log.v("Check PrayerReceiver onDoingAzan","delay "+delay);
Manager.playAzanNotification(context);
Manager.updatePrayerAlarm(delay);
}
private void onWaitingPrayer() {
Manager manager = new Manager(this.context);
Preference preference = manager.getPreference();
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
if(am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL && preference.isAutoSilentDisabled()==false ){
am.setRingerMode(AudioManager.RINGER_MODE_SILENT);
editor.putBoolean("isRingerModeChangedToSilent", true);
editor.commit();
}
this.delayMilliSeconds = silentDuration;
prayerState.setNextState(PrayerState.WAITING_AZAN);
Manager.updatePrayerAlarm(delayMilliSeconds);
}
Can some one please help me,exactly what am I doing wrong?
Manager.prayerAlarmManager.set is not set exact on the specified time on api 19 and above. That is probably why "some" users complain.
Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
On api 19 and above you need setExact to schedule at a specific time
You will need something like this:
if(android.os.Build.VERSION.SDK_INT >= 19) {
// setExact
}
else {
//set
}
Related
My app is a tracking app where I want to track user location in a service and then display every location in a fragment.
I have a problem with background service: my service is stopped after some minutes (sometimes < 10 minutes, sometimes > 30 minutes).
I start my location service in this fragment and everything goes fine when I use my app.
The problem is when I close (not kill) my app and start something else on my phone (or just block it), after a few minutes my service is stopped and I don't know why.
I show a foreground notification when my service is running but android system still stops it, I notice this because my notification disappear and my app restarts.
Here is my code for the service:
public class LocationService extends Service {
private static final String TAG = "LocationService";
public static final String NEW_VALUE_INTENT_ACTION = "service_new_value";
public static final String INTENT_LATITUDE = "latitude";
public static final String INTENT_LONGITUDE = "longitude";
public static final String INTENT_SPEED = "speed";
public static final String INTENT_ACCURACY = "accuracy";
private static final long TWO_MINUTES = 1000 * 60 * 2;
private static final long INTERVAL = 5000;
private static final long FASTEST_INTERVAL = 2000;
private int ONGOING_NOTIFICATION = 1111;
private Location currentLocation;
NotificationCompat.Builder builder = null;
private LocationRequest mLocationRequest;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.d(LocationService.TAG, "LocationService ---> onCreate()");
super.onCreate();
}
#Override
public void onDestroy() {
Log.d(LocationService.TAG, "LocationService ---> onDestroy()");
super.onDestroy();
stopForeground(true);
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(LocationService.TAG, "LocationService ---> onStartCommand()");
startServiceTask();
return super.onStartCommand(intent, flags, startId);
}
#Override
public boolean onUnbind(Intent intent) {
Log.d(LocationService.TAG, "LocationService ---> onUnbind()");
return super.onUnbind(intent);
}
private void startServiceTask() {
Log.d(LocationService.TAG, "GpsService ---> Starting ...");
setServiceAsForeground();
initFusedLocationProvider();
Log.d(LocationService.TAG, "MyStartedService ---> Starting ...");
}
private void initFusedLocationProvider() {
mFusedLocationClient = new FusedLocationProviderClient(this);
mLocationRequest = new LocationRequest();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
if (currentLocation == null)
currentLocation = locationResult.getLastLocation();
else if (isBetterLocation(locationResult.getLastLocation(), currentLocation)) {
Log.d(TAG, "onLocationChanged(): Updating Location ... " + currentLocation.getProvider());
currentLocation = locationResult.getLastLocation();
}
notifyValueUpdate();
} }, getMainLooper());
}
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use
// the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be
// worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation
.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and
// accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate
&& isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
private void setServiceAsForeground() {
Log.d(LocationService.TAG, "GpsService ---> setServiceAsForeground()");
// Prepare the intent triggered if the notification is selected
Intent intent = new Intent(this, LocationService.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel notificationChannel = new NotificationChannel("ID", "Name", importance);
notificationManager.createNotificationChannel(notificationChannel);
builder = new NotificationCompat.Builder(getApplicationContext(), notificationChannel.getId());
} else {
builder = new NotificationCompat.Builder(getApplicationContext());
}
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
v.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE));
} else {
//deprecated in API 26
v.vibrate(500);
}
// Build the notification
// Use NotificationCompat.Builder instead of just Notification.Builder to support older Android versions
Notification notification = builder.setContentTitle("MyMovements")
.setContentText("Running ...")
.setSmallIcon(getNotificationIcon())
.setBadgeIconType(getNotificationIcon())
.setContentIntent(pIntent)
.setAutoCancel(true).build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(ONGOING_NOTIFICATION, notification, FOREGROUND_SERVICE_TYPE_LOCATION);
}
else {
startForeground(ONGOING_NOTIFICATION,notification);
}
}
private int getNotificationIcon(){
SharedPreferences settings = this.getSharedPreferences(MainActivity.PREFERENCE,0);
String icon = settings.getString(MapFragment.NOTIFICATION_PREF, " ");
switch (icon) {
case "Walking":
return R.drawable.walk;
case "Public Transportation":
return R.drawable.bus;
case "Driving":
return R.drawable.car;
default:
return 0;
}
}
private void updateNotification(){
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String conc = "Running...\n" + "Lat: " + currentLocation.getLatitude() + " " + "Lng: " + currentLocation.getLongitude();
Notification notification = builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(conc))
.setContentText(conc).build();
notificationManager.notify(ONGOING_NOTIFICATION, notification);
}
private void notifyValueUpdate(){
Log.d(TAG, "MyStartedService ---> notifyValueUpdate()");
if(currentLocation != null) {
updateNotification();
Log.d(LocationService.TAG, currentLocation.getProvider());
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(NEW_VALUE_INTENT_ACTION);
Bundle bundle = new Bundle();
bundle.putDouble(INTENT_LATITUDE, currentLocation.getLatitude());
bundle.putDouble(INTENT_LONGITUDE, currentLocation.getLongitude());
bundle.putDouble(INTENT_SPEED, currentLocation.getSpeed()*3.6);
bundle.putDouble(INTENT_ACCURACY, currentLocation.getAccuracy());
broadcastIntent.putExtras(bundle);
LocalBroadcastManager.getInstance(this).sendBroadcast(broadcastIntent);
}
}
}
I started my service in OnServiceCommand() -> startServiceTask() -> setServiceAsForeground()
I will really appreciate any tips for my code (this is a scholar project and this is my first app, so probably I am doing something wrong).
Thanks to everyone
I making a timer in my android app. I have a scenario where I dont need to stop the timer even if the app is closed. If the timer is running and user closes the app and reopen it after sometime. He should see the latest time on the timer. But currently I am not able to show the latest time. I am only able to show the time when the user killed the app. I am storing the time of the timer and when the user open the app I am putting the stored time back to the timer. But I want to show the latest time. Here what I have done till now. I am using chronometer widget.
MainActivity.class:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getSimpleName();
Chronometer tvTextView;
Button btnStart, btnStop;
private int state = 0; //0 means stop state,1 means play, 2 means pause
SharedPreferences sharedPreferences;
private boolean running = false;
private long pauseOffSet = -1;
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTextView = findViewById(R.id.textview);
progressBar = findViewById(R.id.puzzleProgressBar);
btnStart = findViewById(R.id.button1);
btnStop = findViewById(R.id.button2);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
sharedPreferences = getSharedPreferences("myprefs", MODE_PRIVATE);
state = sharedPreferences.getInt("state", 0);
tvTextView.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
#Override
public void onChronometerTick(Chronometer chronometer) {
long time = SystemClock.elapsedRealtime() - chronometer.getBase();
pauseOffSet=time;
Log.e(TAG,"pauseOffSet "+pauseOffSet);
if (time >= 79200000) {
tvTextView.setBase(SystemClock.elapsedRealtime());
tvTextView.stop();
running = false;
progressBar.setProgress(0);
} else {
chronometer.setText(setFormat(time));
int convertTime = (int) time;
progressBar.setProgress(convertTime);
}
}
});
if (state == 1) { // its in play mode
running = true;
tvTextView.setBase(SystemClock.elapsedRealtime() - sharedPreferences.getLong("milli", 0));
tvTextView.start();
} else if (state == 2) { //its in pause mode
running = false;
pauseOffSet = sharedPreferences.getLong("milli", -1);
long time = SystemClock.elapsedRealtime() - pauseOffSet;
tvTextView.setBase(time);
int convertTime = (int) pauseOffSet;
progressBar.setProgress(convertTime);
} else {
running = false;
tvTextView.setBase(SystemClock.elapsedRealtime());
}
}
public void onClick(View v) {
if (btnStart == v) {
if (!running) {
if (pauseOffSet != -1) {
pauseOffSet = sharedPreferences.getLong("milli", -1);
}
tvTextView.setBase(SystemClock.elapsedRealtime() - pauseOffSet);
tvTextView.start();
state = 1;
pauseOffSet = 0;
running = true;
}
} else if (btnStop == v) {
if (running) {
tvTextView.stop();
pauseOffSet = SystemClock.elapsedRealtime() - tvTextView.getBase();
state = 2;
running = false;
}
}
}
#Override
protected void onStop() {
super.onStop();
sharedPreferences.edit().putLong("milli", pauseOffSet).commit();
sharedPreferences.edit().putInt("state", state).commit();
}
#Override
protected void onDestroy() {
Log.e(TAG, "onDestroy called, state: " + state);
super.onDestroy();
}
String setFormat(long time) {
int h = (int) (time / 3600000);
int m = (int) (time - h * 3600000) / 60000;
int s = (int) (time - h * 3600000 - m * 60000) / 1000;
String hh = h < 10 ? "0" + h : h + "";
String mm = m < 10 ? "0" + m : m + "";
String ss = s < 10 ? "0" + s : s + "";
return hh + ":" + mm + ":" + ss;
}
}
Check out my app on Google Play:
Code has been updated to use suggestions given by answer, however, still running into some problems...
I am using a BroadCast Receiver within a service to get metadata from Spotify (an external application). However, Spotify is unique in the fact that it sends the following intent-actions:
metadatachanged which contains the song's artist, track, length, etc.
playbackstatechanged which contains a boolean value for "playing" and an integer value for the current position in the song.
However, I have been running into some problems. I have written the following code:
public class BackgroundService extends Service {
AudioManager am;
int positionInMs;
double eps = 5000;
String trackId = null;
String lastTrackId = null;
int lastPlayTime = 0;
int addSeconds = 0;
int trackLengthInSec = 0;
boolean playing = false;
boolean timerStarted = false;
void startTimer(){
timerStarted = true;
new Timer(true).scheduleAtFixedRate(
new TimerTask() {
#Override
public void run() {
if(playing) {
if(lastTrackId == null) {
lastTrackId = trackId;
}else if (!lastTrackId.equals(trackId)) {
addSeconds = 1;
playing = true;
lastPlayTime = positionInMs;
// Track changed. Reset counter.
}else{
addSeconds++; // Increment counter.
}
checkMuteStatus(trackLengthInSec, lastPlayTime + addSeconds * 1000 // multiply by 1000 as we're counting in seconds
);
}
Log.e("-------:", "Last Play Time: " + String.valueOf(lastPlayTime));
Log.e("-------:", "Track Length: " + String.valueOf(trackLengthInSec));
Log.e("-------:", "Current Position: " + String.valueOf(lastPlayTime + addSeconds * 1000));
}
},
1000, // Wait a second before first run
1000 // Runs every second
);
}
#Override
public void onCreate() {
super.onCreate();
am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
IntentFilter filter;
filter = new IntentFilter();
filter.addAction("com.spotify.music.playbackstatechanged");
filter.addAction("com.spotify.music.metadatachanged");
filter.addAction("com.spotify.music.queuechanged");
Notification notification = new Notification();
startForeground(1, notification);
registerReceiver(receiver, filter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) throws NullPointerException {
long timeSentInMs = intent.getLongExtra("timeSent", 0L);
String action = intent.getAction();
if (action.equals(BroadcastTypes.METADATA_CHANGED)) {
trackId = intent.getStringExtra("id");
String artistName = intent.getStringExtra("artist");
String albumName = intent.getStringExtra("album");
String trackName = intent.getStringExtra("track");
trackLengthInSec = intent.getIntExtra("length", 0);
Log.e("STATUS:", trackName);
Log.e("STATUS:", artistName);
Log.e("STATUS:", albumName);
Log.e("STATUS:", "TRACK LENGTH: " + String.valueOf(trackLengthInSec));
Log.e("STATUS:", trackId);
Log.e("STATUS:", "POSITION: " + String.valueOf(positionInMs));
if(!timerStarted) {
startTimer();
}
//---------------------- IF PAUSED OR SONG CHANGED---------------------
} else if (action.equals(BroadcastTypes.PLAYBACK_STATE_CHANGED)) {
playing = intent.getBooleanExtra("playing", false);
positionInMs = intent.getIntExtra("playbackPosition", 0);
lastPlayTime = positionInMs;
addSeconds = 0; // Reset counter as we've now got the current position.
if(!timerStarted) {
startTimer();
}
}
}
};
void checkMuteStatus(double trackLength, double currentTime) {
if (Math.abs(trackLength - currentTime) < eps) {
mute(am);
Log.e("STATUS:", "MUTED");
addSeconds = 0;
lastPlayTime = 0;
} else {
unMute(am);
Log.e("STATUS:", "NOT MUTED");
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("STATUS:", "In onDestroy");
unregisterReceiver(receiver);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
static final class BroadcastTypes {
static final String SPOTIFY_PACKAGE = "com.spotify.music";
static final String PLAYBACK_STATE_CHANGED = SPOTIFY_PACKAGE + ".playbackstatechanged";
static final String METADATA_CHANGED = SPOTIFY_PACKAGE + ".metadatachanged";
}
public void mute(AudioManager audioManager){
audioManager.setStreamMute(audioManager.STREAM_SYSTEM, true);
audioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, true);
audioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
audioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
audioManager.setStreamMute(AudioManager.STREAM_RING, true);
}
public void unMute(AudioManager audioManager){
audioManager.setStreamMute(audioManager.STREAM_SYSTEM,false);
audioManager.setStreamMute(AudioManager.STREAM_NOTIFICATION, false);
audioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
audioManager.setStreamMute(AudioManager.STREAM_MUSIC, false);
audioManager.setStreamMute(AudioManager.STREAM_RING, false);
}
}
Which all works perfectly in getting the data, etc.
Essentially I am attempting to mute a user's phone when the length of the song subtracted from the current playback time in the song is less than a certain number. This basically allows me to have the device muted when an ad is playing, and unmuted when a new song is started. The only problem I consistently come across is that I can only access the current position in the song when the play state is changed. This essentially means that in order for my app to check whether it should mute the device, Spotify must first be paused so that the current playback time is updated.
So my question is, how can I keep my current playback time up-to-date so that my muting functionality is able to work? Is there a way I can implement a "timer" of sorts which kicks in after I receive a value for the current playback time and then increments the time until next update?
You could start a timer that executes every second when a song is played and use it to keep track of the elapsed seconds since.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) throws NullPointerException {
long timeSentInMs = intent.getLongExtra("timeSent", 0L);
String action = intent.getAction();
if (action.equals(BroadcastTypes.METADATA_CHANGED)) {
trackId = intent.getStringExtra("id");
String artistName = intent.getStringExtra("artist");
String albumName = intent.getStringExtra("album");
String trackName = intent.getStringExtra("track");
trackLengthInSec = intent.getIntExtra("length", 0);
Log.e("STATUS:", trackName);
Log.e("STATUS:", artistName);
Log.e("STATUS:", albumName);
Log.e("STATUS:", "TRACK LENGTH: " + String.valueOf(trackLengthInSec));
Log.e("STATUS:", trackId);
Log.e("STATUS:", "POSITION: " + String.valueOf(positionInMs));
// the new track id is always updated just before the next song is played
// so we can safely assume that the ad has finished playing and it's time to unmute
unMute(am);
// reset & resume the timer
lastPlayTime = 0;
addSeconds = 0;
currentlyAdPlaying = false;
if(!timerStarted)
startTimer();
//---------------------- IF PAUSED OR SONG CHANGED---------------------
} else if (action.equals(BroadcastTypes.PLAYBACK_STATE_CHANGED)) {
playing = intent.getBooleanExtra("playing", false);
positionInMs = intent.getIntExtra("playbackPosition", 0);
// this is just needed to correct an eventually existing offset from the real time.
lastPlayTime = positionInMs;
addSeconds = 0; // Reset counter as we've now got the current position.
checkMuteStatus(trackLengthInSec, positionInMs);
}
}
};
String trackId = null;
String lastTrackId = null;
int lastPlayTime = 0;
int addSeconds = 0;
int trackLengthInSec = 0;
boolean playing = false;
boolean timerStarted = false;
boolean currentlyAdPlaying = true;
void startTimer(){
timerStarted = true;
new Timer(true).scheduleAtFixedRate(
new TimerTask() {
#Override
public void run() {
if(playing && !currentlyAdPlaying) {
if(lastTrackId == null)
lastTrackId = trackId;
if(lastTrackId != trackId){
lastTrackId = trackId;
addSeconds = 1; // Track changed. Reset counter.
} else {
addSeconds++; // Increment counter.
}
checkMuteStatus(
trackLengthInSec,
lastPlayTime + addSeconds * 1000
);
}
}
},
1000, // Wait a second before first run
1000 // Runs every second
);
}
void checkMuteStatus(double trackLength, double currentTime) {
// trackLength is in seconds, but currentTime in MS.
// So we should be changing trackLength to MS as well, right?
trackLength *= 1000;
if (Math.abs(trackLength - currentTime) < eps) {
mute(am);
Log.e("STATUS:", "MUTED");
// pause the timer, for an ad is playing.
currentlyAdPlaying = true;
} else {
unMute(am);
Log.e("STATUS:", "NOT MUTED");
}
}
Basically the timer runs every second (starting when an ad has finished playing) and then increments a counter as long as the song isn't paused. On reaching the end of the song the timer is suspended as an ad will be played next. Once the ad has finished playing, the timer is reset and resumed again.
With this method you might be off by half a second or so, consider decreasing the timer interval if necessary to get a higher resolution (don't forget to adjust the multiplier though).
Edit:
The code above won't notice the user changing the track mid-song, as no broadcasts are sent on that occasion. A workaround might be to add a muting button using notifications (eg. in combination with FLAG_NO_CLEAR and FLAG_ONGOING_EVENT).
I am trying to run a simple clock in my activity, the purpose of the project is to create a secondary thread to run our clock on and update our main UI thread using a handler. I thought I had it working but I guess I was looking at something wrong. Either way, here is my code
public class MainActivity extends Activity implements Runnable{
/** VARIABLES **/
private LinearLayout currView;
private TextView clock;
private Typeface icelandFace;
private Calendar cal;
// clock time values
private int clockMins;
private int clockHour;
private int timeOfDay; // am/pm
private String currTimeString;
// for alarm clock
boolean startAlarm = false;
private long alarmStartTime = -1;
private final int DESIRED_ALARM_DURATION = 5;
// clock thread handler, to communicate between our main ui and our secondary ui
// - note to self - rather unclear on the explanation for the suppression for the handler
private Handler handler = new Handler();
// runnable variable for the secondary thread to actually run on
private final Runnable clockRunnable = new Runnable(){
public void run(){
updateUI();
}
};
/** OVERRIDDEN CLASS METHODS **/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
currView = (LinearLayout) findViewById(R.id.main_activity);
clock = (TextView) findViewById(R.id.clock_view);
clock.setTextSize(clock.getTextSize() * 2);
icelandFace = Typeface.createFromAsset(getAssets(), "font/Iceland-Regular.ttf");
clock.setTypeface(icelandFace);
cal = Calendar.getInstance();
// Create New Thread (to run our clock on)
Thread clockThread = new Thread(){
public void run() {
keepTime();
clockMins = cal.get(Calendar.MINUTE);
clockHour = cal.get(Calendar.HOUR);
timeOfDay = cal.get(Calendar.AM_PM);
// Implement the handler for the thread
// NOTE: recall, Message contains data and is passed to our handler
//Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
// start with hours
String timeString = clockHour + ":";
// add minutes
if(clockMins < 10){
timeString = timeString + "0" + clockMins + " ";
}
else{
timeString = timeString + clockMins + " ";
}
// set time of day
if(timeOfDay == 0){
timeString = timeString + "am";
}
else{
timeString = timeString + "pm";
}
// Optional bundle stuff
//bundle.putString("time_string", timeString);
//message.setData(bundle);
Log.d("Runnable", "Tracking time # " + timeString);
currTimeString = timeString;
handler.post(clockRunnable);
}
private void keepTime(){
clockMins = cal.get(Calendar.MINUTE);
clockHour = cal.get(Calendar.HOUR);
timeOfDay = cal.get(Calendar.AM_PM);
checkAlarm(clockHour, clockMins, timeOfDay);
}
private void checkAlarm(int hour, int min, int timeOfDay){
SharedPreferences alarmPref = getSharedPreferences("alarm_preferences", MODE_PRIVATE);
int alarmHour = alarmPref.getInt("alarm_hour", -1);
int alarmMin = alarmPref.getInt("alarm_min", -1);
int alarmTimeOfDay = alarmPref.getInt("alarm_time_of_day", 0);
boolean alarmOn = alarmPref.getBoolean("alarm_on", true);
if(hour == alarmHour && min == alarmMin && timeOfDay == alarmTimeOfDay && alarmOn == true
&& alarmStartTime != -1){
startAlarm = true;
}
return;
}
};
clockThread.start();
}
//TODO: Setup the alarm via the menu that inflates here
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.alarm, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.alarm_settings:
startActivity(new Intent(this, AlarmPopup.class));
Toast.makeText(this, "Open Up Alarm Dialog", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void run()
{
while (clockRunnable != null)
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e) { };
handler.post(clockRunnable);
}
}
/** PRIVATE METHODS FOR SIMPLIFICATION **/
private void updateUI(){
if(startAlarm == true){
//AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmStartTime = cal.get(Calendar.SECOND);
currView.setBackgroundColor(Color.RED);
}
else if((cal.get(Calendar.SECOND) - alarmStartTime) > DESIRED_ALARM_DURATION){
alarmStartTime = -1;
startAlarm = false;
currView.setBackgroundColor(Color.WHITE);
}
clock.setText(currTimeString);
}
Obviously I am missing something as far as grasping what is going on, but I can't tell what. Create a background thread, run your processes, pass it through the handler to control the variables, and send those variables through your UI update. I used http://developer.android.com/guide/faq/commontasks.html to gain that understanding.
Instead of making the activity implement Runnable, try this:
TimerTask task = new TimerTask() {
#Override
public void run() {
handler.post(clockRunnable);
}
};
Timer timer = new Timer();
timer.schedule(task, 0, 1000);
Even though I would use a different approach registering a BroadcastReceiver to receive tick actions from the system (every second).
Regards.
I want to implement a schedule function in my android project. So I Googled for an Alarm manager program but I can`t Resolve Problem.
I want set alarm with custom time that users Determine. I wrote 2 class for service and BroadcastReciver. But alarm is inccorect for example i set 2 min for alarm.but app Arbitrary is work .eg 2 min or 3 or 5 or 1min....
mainActivity:
SharedPreferences preferences1 = getSharedPreferences("min", 0);
String min1 = preferences1.getString("minute", "");
if (min1.length() <= 0) {
s = 120000;
} else {
s = Long.parseLong(min1);
s = s * 60000;
}
t = new Timer();
// Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
// Called each time when 1000 milliseconds (1 second)
// (the period parameter)
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10);
Intent intent = new Intent(CategoryListActivity.this,
Service_class.class);
PendingIntent pintent = PendingIntent.getService(
CategoryListActivity.this, 0, intent, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis(), s, pintent);
Log.e("Timer", t.toString());
}
},
// Set how long before to start calling the TimerTask (in
// milliseconds)
0,
// Set the amount of time between each execution (in
// milliseconds)
s);
service_class.java :
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
dbflash = new DatabaseFlashCard(getApplicationContext());
db = dbflash.getReadableDatabase();
SharedPreferences preferences_notify = getSharedPreferences("notify", 0);
Calendar c = Calendar.getInstance();
String time_one = preferences_notify.getString("time_first", "");
String time_two = preferences_notify.getString("time_second", "");
int hour = c.get(Calendar.HOUR_OF_DAY);
if (time_one.length() <= 0) {
time_one = "21";
}
if (time_two.length() <= 0) {
time_two = "5";
}
int timeFirst = Integer.parseInt(time_one);
int timeSecond = Integer.parseInt(time_two);
if (hour < timeFirst && hour > timeSecond) {
// nothing
} else {
// notify and alert
preferences_language = getSharedPreferences("language_sql", 0);
editor_language = preferences_language.edit();
String language = preferences_language.getString("language", "");
String sql="SELECT * FROM " + dbflash.TABLE
+ " where hide='0' ORDER BY RANDOM() LIMIT 1";
if(language.length()>0)
{
if(language.equalsIgnoreCase("0"))
{
sql="SELECT * FROM " + dbflash.TABLE
+ " where hide='0' ORDER BY RANDOM() LIMIT 1";
}
else
{
sql="SELECT * FROM " + dbflash.TABLE
+ " where ids="+language+" and hide='0' ORDER BY RANDOM() LIMIT 1";
}
Cursor cursors = db.rawQuery(sql, null);
for (int i = 0; i < cursors.getCount(); i++) {
cursors.moveToNext();
ID = cursors.getString(cursors.getColumnIndex("ID"));
farsi = cursors.getString(cursors.getColumnIndex("farsi"));
english = cursors.getString(cursors.getColumnIndex("english"));
question = cursors
.getString(cursors.getColumnIndex("question"));
count = cursors.getString(cursors.getColumnIndex("count"));
}
preferences_status = getSharedPreferences("notify_status", 0);
String status = preferences_status.getString("status", "");
SharedPreferences.Editor editor_status;
editor_status = preferences_status.edit();
if (status.length() <= 0) {
status="1";
editor_status.putString("status", "1");
editor_status.commit();
Log.e("Notification Status ", "Enable Now..for First");
} else if(status.equalsIgnoreCase("0")) {
Log.e("Notification Status ", "disable");
}
else
{
Log.e("Notification Status ", "enable");
if (english==null || english=="") {
} else {
SharedPreferences preferences = getSharedPreferences("music", 0);
String address=preferences.getString("address", "");
if(address.length()<=0)
{
mp = MediaPlayer.create(getApplicationContext(), R.raw.ring);
mp.start();
}
else
{
mp = MediaPlayer.create(getApplicationContext(),Uri.parse(address));
mp.start();
}
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent contentIntent = new Intent(this,
QuestionActivity.class);
contentIntent.putExtra("ID", ID);
contentIntent.putExtra("english", english);
contentIntent.putExtra("farsi", farsi);
contentIntent.putExtra("count", count);
contentIntent.putExtra("question", question);
Notification notification = new Notification(
R.drawable.icon, "یاد آوری",
System.currentTimeMillis());
notification.setLatestEventInfo(this, english, "",
PendingIntent.getActivity(this.getBaseContext(), 0,
contentIntent,
PendingIntent.FLAG_CANCEL_CURRENT));
notification.flags = Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(SIMPLE_NOTFICATION_ID,
notification);
}
}
}
}
return START_STICKY;
}
and Myreceiver
public class Myreceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Intent serviceLauncher = new Intent(context, Service_class.class);
context.startService(serviceLauncher);
}
Android Manifest :
<service android:name=".Service_class" />
<receiver
android:name="com.pttat.flashcard.services.Myreceiver"
android:process=":remote" />
how to change my code for Resolve my Problem?
Do I need to change calling alarm manager in Oncreate?
thanks