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.
Related
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'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
}
I'm trying to save a serialized timer object, and retrieve it. The timer needs to be restored exactly how it was when it was created.
The timer works beautifully, but when the app is destroyed so is my timer with all its data.
EDIT: debug log says FileNotFoundException: open failed (Read only File system)
I have the uses-permission in my manifest
I'm not attempting to write to an SD card, I want to create the file on the user's android locally.
.......
My timer class implements serialized
And OnCreate My app try to connect to a objectinputstream, and a fileinputstream; retrieve the object, cast it to a Timer and assign it.
The timer is stored every time it's updated.
TIMER CLASS CODE
package com.example.theworkingbutton;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import android.widget.Button;
public class Timer implements Serializable {
private static final long serialVersionUID = 1L;
public Timer(int timerState){
this.timerState = timerState;
}
public int timerState = 0;
public long timerStart = 0;
public long timerEnd = 0;
public long timeAccumulated = 0;
public long totalSeconds = 0;
public long hours = 0;
public long minutes = 0;
public long seconds = 0;
public String realTimeSeconds = "null";
public String realTimeMinutes = "null";
public String realTimeHours = "null";
public String timeString = "No time avalible";
Button button;
public void preform(){
if(timerState == 0){
timerStart = System.nanoTime();
timerState = 1;
} else if (timerState == 1) {
timerEnd = System.nanoTime();
timeAccumulated = timerEnd - timerStart + timeAccumulated;
totalSeconds = TimeUnit.SECONDS.convert(timeAccumulated, TimeUnit.NANOSECONDS);
hours = (totalSeconds / 3600);
minutes = (totalSeconds % 3600) / 60;
seconds = (totalSeconds % 60);
realTimeHours = Long.toString(hours);
realTimeSeconds = Long.toString(seconds);
realTimeMinutes = Long.toString(minutes);
timeString = "Hours: " + realTimeHours + " Minutes: " + realTimeMinutes + " Seconds: " + realTimeSeconds;
timerState = 0;
}
}
}
End Timer class code
ONCREATE Code
#Override
protected void onCreate(Bundle savedInstanceState){
//make screen
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
locManager =(LocationManager)getSystemService(Context.LOCATION_SERVICE);
locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L,
500.0f, locationListener);
//get buttons / turn them red
workingButton = (Button) findViewById(R.id.timer_button);
officeButton = (Button) findViewById(R.id.office_button);
drivingButton = (Button) findViewById(R.id.drive_button);
showingButton = (Button) findViewById(R.id.showing_button);
prospectingButton = (Button) findViewById(R.id.prospect_button);
listingButton = (Button) findViewById(R.id.listing_button);
listingButton.getBackground().setColorFilter(0xffffff00, PorterDuff.Mode.MULTIPLY);
workingButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
drivingButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
officeButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
showingButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
prospectingButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
//set up our map
GoogleMap monthlyMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
Location location = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null) {
location.getLatitude();
location.getLongitude();
}
monthlyMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
monthlyMap.setMyLocationEnabled(true);
monthlyMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(),location.getLongitude()), 15 ));
try{
#SuppressWarnings("resource")
ObjectInputStream is = new ObjectInputStream(new FileInputStream("TheWorkingButtonSaves.txt"));
Timer timerOne = (Timer) is.readObject();
workingTimer = timerOne;}
catch (Exception e){
e.printStackTrace();
}
}
END ON CREATE CODE
How im trying to save
Timer workingTimer;
public void startWorking(View view){
if (workingTimer == null){
workingTimer = new Timer(working);}
//Layout Views
workingButton = (Button) findViewById(R.id.timer_button);
TextView amountOfTime = (TextView) findViewById(R.id.time_spent);
if(drivingTimer.timerState == 1){
drivingSomewhere(findViewById(R.id.drive_button));
}
workingTimer.preform();
// Button Colors
if (workingTimer.timerState == 0 ){ //(TIMERS OFF)
workingButton = (Button) findViewById(R.id.timer_button);
workingButton.getBackground().setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
}
else { //(TIMERS ON)
workingButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
}
//save
try {
FileOutputStream fs = new FileOutputStream ("TheWorkingButtonSaves.txt");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(workingTimer);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
//Display Time to user
amountOfTime.setText(workingTimer.timeString);
}
END HOW IM TRYING TO SAVE
You can save the value of the timer by using SharedPreferences.
SharedPreferences saveTimer= getSharedPreferences("saveTimer", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor= timeSaved.edit();
// There's also putLong and putFloat
editor.putInt("saveTimer", INT_NAME_1);
editor.commit();
You then retrieve it using
SharedPreferences getTimer = getSharedPreferences("saveTimer", Activity.MODE_PRIVATE);
int INT_NAME_2 = getTimer.getInt("saveTimer", 0);
Now you have an int (or float or long) value that you can put back and start from where you started. (You can also use this to retrieve the values of whatever you saved in other activities)
See the below link if it helps. In my opinion it's better to let the platform handle the serializaion/deserialization instead of us doing it.
Handling runtime changes
I'm trying to implement a CountDownTimer in an Android Application. This timer will, while running, countdown from a value, than reset, than countdown from a different value. Switching back and force between values until either a set number of rounds have elapsed or the stop button has been pressed. I can get the CountDownTimer samples to work, but I guess I'm missing something here. Below is the applicable button press code;
CounterState state = CounterState.WORKOUT;
private WorkoutTimer workoutTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.workout_stopwatch);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Set up OnClickListeners
((Button) findViewById(R.id.start_button)).setOnClickListener(this);
((Button) findViewById(R.id.stop_button)).setOnClickListener(this);
((Button) findViewById(R.id.reset_button)).setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.start_button:
if (!timer_running) {
timer_running = true;
Log.d(TAG, "clicked on Start Button");
// If the state is unknown, set it to Workout first
int State = state.getStateValue();
if (State == 0) {
state.setStateValue(1);
}
workoutTimer.start();
}
break;
case R.id.stop_button:
Log.d(TAG, "clicked on Stop Button");
if (timer_running); {
timer_running = false;
workoutTimer.cancel();
}
break;
private class WorkoutTimer extends CountDownTimer{
public WorkoutTimer(long interval) {
super(getThisTime(), interval);
Log.d(TAG, "WorkoutTimer Constructed...");
}
TextView digital_display = (TextView) findViewById(R.id.digital_display);
TextView numOfRounds = (TextView) findViewById(R.id.number_of_rounds);
public void onFinish() {
int State = state.getStateValue();
int roundsLeft = 0;
if (State == 1) {
state.setStateValue(2);
} else {
state.setStateValue(1);
}
decrementRounds();
try {
roundsLeft = Integer.parseInt(numOfRounds.getText().toString());
} catch(NumberFormatException nfe) {
roundsLeft = 999;
}
if (roundsLeft > 0 || roundsLeft != 999) {
workoutTimer.start();
}
}
public void onTick(long millisUntilFinished) {
final long minutes_left = ((millisUntilFinished / 1000) / 60);
final long seconds_left = (millisUntilFinished / 1000) - (minutes_left * 60);
final long millis_left = millisUntilFinished % 100;
String time_left = String.format("%02d:%02d.d", minutes_left, seconds_left,
millis_left);
digital_display.setText(time_left);
}
}
private long getThisTime() {
long time = 0;
TextView workout_time = (TextView) findViewById(R.id.workout_time);
TextView rest_time = (TextView) findViewById(R.id.rest_time);
switch(state) {
case WORKOUT:
try {
time = Integer.parseInt(workout_time.getText().toString());
} catch(NumberFormatException nfe) {
time = 999;
}
// time = 90;
Log.d(TAG, "Workout time = " + time);
break;
case REST:
try {
time = Integer.parseInt(rest_time.getText().toString());
} catch(NumberFormatException nfe) {
time = 999;
}
// time = 30;
Log.d(TAG, "Rest time = " + time);
break;
case UNKNOWN:
time = 0;
break;
}
return time;
}
Everything starts up okay, but crashes when I click either button. If I comment out my calls to the workoutTimer, no crash. I never see my log in the constructor of the workoutTimer class, so obviously I'm missing something here. Any help would be appreciated.
-Ian
You have not initialized your workoutTimer. You need to add the following line in your onCreate method.
workoutTimer = new WorkoutTimer(...);