Here is my fully functional code in which when I press the button, the button gets disabled and the countdown timer gets started and whenever it gets over button gets enabled. My problem is that if I leave that activity the process resets.
My question is how that can be done in the background so even if I close the application the timer runs in the background?
package com.mycompany.myapp;
import android.app.*;
import android.os.*;
import android.widget.*;
import android.view.View.*;
import android.view.*;
public class MainActivity extends Activity {
Button btnCountdown;
TextView tvCountdown;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnCountdown = findViewById(R.id.btnCountdown);
tvCountdown = findViewById(R.id.tvCountdown);
btnCountdown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Timer();
btnCountdown.setEnabled(false);
}
});
}
private void Timer() {
new CountDownTimer(30*1000,1000) {
#Override
public void onTick(long millisUntilFinished) {
long second = (millisUntilFinished / 1000) % 60;
long minutes = (millisUntilFinished / (1000*60)) % 60;
tvCountdown.setText(minutes + ":" + second);
}
#Override
public void onFinish() {
tvCountdown.setText("Fin");
btnCountdown.setEnabled(true);
}
}.start();
}
}
Add to your AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
BroadcastReceiver.java
public class BroadcastReceiver extends AppCompatActivity {
TextView tvTimer, tvTimerRunningState, tvTimerFinishedState;
private static final String TAG = "CountdownTimer";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_receiver);
}
public void handleStartTimer(View view) {
Intent intent = new Intent(this, BroadcastService.class);
intent.putExtra("inputExtra", "");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(this, intent);
} else {
this.startService(intent);
}
Log.i(TAG, "timerStarted");
}
public void handleCancelTimer (View view) {
Intent intent = new Intent(this, BroadcastService.class);
stopService(intent);
}
/* CountDown */
final private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateGUI(intent);
}
};
#Override
public void onResume() {
super.onResume();
registerReceiver(broadcastReceiver, new IntentFilter(BroadcastService.COUNTDOWN_BR));
Log.i(TAG, "Registered broadcast receiver");
}
#Override
public void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
Log.i(TAG, "Unregistered broadcast receiver");
}
#Override
public void onStop() {
try {
unregisterReceiver(broadcastReceiver);
} catch (Exception e) {
// Receiver was probably already stopped in onPause()
}
super.onStop();
}
private void updateGUI(Intent intent) {
if (intent.getExtras() != null) {
long millisUntilFinished = intent.getLongExtra("countdown", 0);
long seconds = (millisUntilFinished / 1000) % 60;
long minutes = (millisUntilFinished / (1000*60)) % 60;
long hours = (millisUntilFinished / (1000*60*60)) % 60;
String time = (hours + " : " + minutes + " : " + seconds);
tvTimer = findViewById(R.id.tvTimer);
tvTimer.setText(time);
boolean countdownTimerRunning = intent.getBooleanExtra("countdownTimerRunning", false);
tvTimerRunningState = findViewById(R.id.tvTimerRunningState);
if (countdownTimerRunning) {
tvTimerRunningState.setText("CountdownTimerRunning");
} else {
tvTimer.setText("0 : 0 : 0");
tvTimerRunningState.setText("CountdownTimerNotRunning");
}
boolean countdownTimerFinished = intent.getBooleanExtra("countdownTimerFinished", false);
tvTimerFinishedState = findViewById(R.id.tvTimerFinishedState);
if (countdownTimerFinished) {
tvTimerFinishedState.setText("Finished");
} else {
tvTimerFinishedState.setText("Unfinished");
}
}
}
activity_broadcast_receiver.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="#+id/btnStartJob"
android:onClick="handleStartTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Timer" />
<Button
android:id="#+id/btnStopJob"
android:onClick="handleCancelTimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Cancel Timer" />
<TextView
android:id="#+id/tvTimer"
android:text="0 : 0 : 0"
android:gravity="center"
android:textSize="30sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/tvTimerFinishedState"
android:gravity="center"
android:textSize="20sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/tvTimerRunningState"
android:gravity="center"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
BroadcastService.java
public class BroadcastService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
private final static String TAG = "BroadcastService";
public static final String COUNTDOWN_BR = "your.package.name";
Intent bi = new Intent(COUNTDOWN_BR);
CountDownTimer cdt = null;
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "Starting timer...");
cdt = new CountDownTimer(30000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
bi.putExtra("countdown", millisUntilFinished);
bi.putExtra("countdownTimerRunning", true);
bi.putExtra("countdownTimerFinished", false);
sendBroadcast(bi);
}
#Override
public void onFinish() {
Log.i(TAG, "Timer finished");
bi.putExtra("countdownTimerFinished", true);
sendBroadcast(bi);
stopForeground(true);
stopSelf();
}
}; cdt.start();
}
#Override
public void onDestroy() {
cdt.cancel();
Log.i(TAG, "Timer cancelled");
bi.putExtra("countdownTimerRunning", false);
sendBroadcast(bi);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
/* Notification */
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, BroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
/* NotificationBuilder */
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent arg0) {
return null;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
Related
See I'm trying to get User input for time and using the user input trying to run the CountDownTimer function in Android studio 4.2.2
The problem is :-
case-1
when I pass Variable name in place of millisInFuture attribute of countDown timer function and trying to set textview accordingly , the TextView doesn't get set up anything.
public void timer() {
CountDownTimer countDownTimer = new CountDownTimer(**timeValueIntent**, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// mediaPlayer.setLooping(true);
//mediaPlayer.start();
timerValueTextView.setText(String.valueOf(millisUntilFinished / 1000) + "s");
}
#Override
public void onFinish() {
Toast.makeText(getApplicationContext(), "Timed out", Toast.LENGTH_SHORT).show();
restartButton.setVisibility(View.VISIBLE);
restartButton.setEnabled(true);
quitButton.setVisibility(View.VISIBLE);
setBtnCond(true);
//mediaPlayer.stop();
}
}.start();
}`
case-2
But when I pass int value like 1000 etc in millisInFuture attribute of CountDownTimer and set up the textView , it gets successfully set up.
public void timer() {
CountDownTimer countDownTimer = new CountDownTimer(**300000**, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// mediaPlayer.setLooping(true);
//mediaPlayer.start();
timerValueTextView.setText(String.valueOf(millisUntilFinished / 1000) + "s");
}
#Override
public void onFinish() {
Toast.makeText(getApplicationContext(), "Timed out", Toast.LENGTH_SHORT).show();
restartButton.setVisibility(View.VISIBLE);
restartButton.setEnabled(true);
quitButton.setVisibility(View.VISIBLE);
setBtnCond(true);
//mediaPlayer.stop();
}
}.start();
}`
Can anyone please help to let me pass variable name instead of a hardcoded integer value in MillisInFuture attribute of CountDownTimer function.!!?
`
I have some source code similar to your questions just implement your project.
Init Variables
private static final long START_TIME_IN_MILLIS = 600000;
private TextView mTextViewCountDown;
private Button mButtonStartPause;
private Button mButtonReset;
private CountDownTimer mCountDownTimer;
private boolean mTimerRunning;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
onCreate
//Init
mTextViewCountDown = findViewById(R.id.text_view_countdown);
mButtonStartPause = findViewById(R.id.button_start_pause);
mButtonReset = findViewById(R.id.button_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mTimerRunning) {
pauseTimer(); //pauseFunction
} else {
startTimer(); //StartFunction
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resetTimer(); // resetFunction
}
});
updateCountDownText(); //updateEverySecond
startTimer()
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
mTimerRunning = false;
mButtonStartPause.setText("Start");
mButtonStartPause.setVisibility(View.INVISIBLE);
mButtonReset.setVisibility(View.VISIBLE);
}
}.start();
mTimerRunning = true;
mButtonStartPause.setText("pause");
mButtonReset.setVisibility(View.INVISIBLE);
pauseTimer()
mCountDownTimer.cancel();
mTimerRunning = false;
mButtonStartPause.setText("Start");
mButtonReset.setVisibility(View.VISIBLE);
resetTimer()
mTimeLeftInMillis = START_TIME_IN_MILLIS;
updateCountDownText();
mButtonReset.setVisibility(View.INVISIBLE);
mButtonStartPause.setVisibility(View.VISIBLE);
updateCountDownText()
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
and Finally XML
<TextView
android:id="#+id/text_view_countdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="00:00"
android:textColor="#android:color/black"
android:textSize="60sp" />
<Button
android:id="#+id/button_start_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/text_view_countdown"
android:layout_centerHorizontal="true"
android:text="start" />
<Button
android:id="#+id/button_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/text_view_countdown"
android:layout_marginStart="11dp"
android:layout_toEndOf="#+id/button_start_pause"
android:text="reset"
android:visibility="invisible"
tools:visibility="visible" />
I have some troubles with creating a way to send notifications foreground when the timer hits 00:00.
I mean, i want that when the timer ends, it sends a notification event if the app is closed.
I already found a way to show notifications and to make a timer who works even if the app is closed.
But when i put the function to send notification at the end of the timer it only works when the app is opened.
There is my MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
notificationManager = NotificationManagerCompat.from(this);
mTextViewCountDown = findViewById(R.id.timerTv);
mButtonStartPause = findViewById(R.id.btn_start_pause);
mButtonReset = findViewById(R.id.btn_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mTimerRunning) {
pauseTimer();
} else {
startTimer();
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resetTimer();
}
});
}
private void startTimer() {
mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
mTimeLeftInMillis=0;
updateCountDownText();
mTimerRunning = false;
updateButtons();
Notification notification = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_money)
.setContentTitle("Test 1")
.setContentText("Important Message")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.build();
notificationManager.notify(1, notification);
}
}.start();
mTimerRunning = true;
updateButtons();
}
private void pauseTimer() {
mCountDownTimer.cancel();
mTimerRunning = false;
updateButtons();
}
private void resetTimer() {
mTimeLeftInMillis = START_TIME_IN_MILLIS;
updateCountDownText();
updateButtons();
}
private void updateCountDownText() {
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
}
private void updateButtons() {
if (mTimerRunning) {
mButtonReset.setVisibility(View.INVISIBLE);
mButtonStartPause.setText("Pause");
} else {
mButtonStartPause.setText("Start");
if (mTimeLeftInMillis < 1000) {
mButtonStartPause.setVisibility(View.INVISIBLE);
} else {
mButtonStartPause.setVisibility(View.VISIBLE);
}
if (mTimeLeftInMillis < START_TIME_IN_MILLIS) {
mButtonReset.setVisibility(View.VISIBLE);
} else {
mButtonReset.setVisibility(View.INVISIBLE);
}
}
}
#Override
public void onStop() {
super.onStop();
SharedPreferences prefs = getSharedPreferences("pref", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong("millisLeft", mTimeLeftInMillis);
editor.putBoolean("timerRunning", mTimerRunning);
editor.putLong("endTime", mEndTime);
editor.apply();
if (mCountDownTimer != null) {
mCountDownTimer.cancel();
}
}
#Override
public void onStart() {
super.onStart();
SharedPreferences prefs = getSharedPreferences("pref", MODE_PRIVATE);
mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
mTimerRunning = prefs.getBoolean("timerRunning", false);
updateCountDownText();
updateButtons();
if (mTimerRunning) {
mEndTime = prefs.getLong("endTime", 0);
mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
if (mTimeLeftInMillis < 0) {
mTimeLeftInMillis = 0;
mTimerRunning = false;
updateCountDownText();
updateButtons();
} else {
startTimer();
}
}
}
}
App Java Class
public class App extends Application {
public static final String CHANNEL_1_ID = "channel1";
#Override
public void onCreate() {
super.onCreate();
createNotificationChannels();
}
private void createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel1 = new NotificationChannel(
CHANNEL_1_ID,
"Channel 1",
NotificationManager.IMPORTANCE_HIGH
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel1);
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.krisix.notificationtest">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".ExampleService"/>
</application>
</manifest>
Thank you for your help !
Have a nice day
Krisix
Thank you for your help sorry i'm a beginner so i'm not sure i understand all the things you told me.
So do you mean something like this ?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
notificationManager = NotificationManagerCompat.from(this);
mTextViewCountDown = findViewById(R.id.timerTv);
mButtonStartPause = findViewById(R.id.btn_start_pause);
mButtonReset = findViewById(R.id.btn_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mTimerRunning) {
pauseTimer();
} else {
startTimer();
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resetTimer();
}
});
}
private void startTimer() {
mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
#Override
public void onFinish() {
mTimeLeftInMillis=0;
updateCountDownText();
mTimerRunning = false;
updateButtons();
if(mTimeLeftInMillis==0){
callNotification();
}
}
}.start();
mTimerRunning = true;
updateButtons();
}
private void pauseTimer() {
mCountDownTimer.cancel();
mTimerRunning = false;
updateButtons();
}
private void resetTimer() {
mTimeLeftInMillis = START_TIME_IN_MILLIS;
updateCountDownText();
updateButtons();
}
private void updateCountDownText() {
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
}
private void updateButtons() {
if (mTimerRunning) {
mButtonReset.setVisibility(View.INVISIBLE);
mButtonStartPause.setText("Pause");
} else {
mButtonStartPause.setText("Start");
if (mTimeLeftInMillis < 1000) {
mButtonStartPause.setVisibility(View.INVISIBLE);
} else {
mButtonStartPause.setVisibility(View.VISIBLE);
}
if (mTimeLeftInMillis < START_TIME_IN_MILLIS) {
mButtonReset.setVisibility(View.VISIBLE);
} else {
mButtonReset.setVisibility(View.INVISIBLE);
}
}
}
#Override
public void onStop() {
super.onStop();
SharedPreferences prefs = getSharedPreferences("pref", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong("millisLeft", mTimeLeftInMillis);
editor.putBoolean("timerRunning", mTimerRunning);
editor.putLong("endTime", mEndTime);
editor.apply();
if (mCountDownTimer != null) {
mCountDownTimer.cancel();
}
}
#Override
public void onStart() {
super.onStart();
SharedPreferences prefs = getSharedPreferences("pref", MODE_PRIVATE);
mTimeLeftInMillis = prefs.getLong("millisLeft", START_TIME_IN_MILLIS);
mTimerRunning = prefs.getBoolean("timerRunning", false);
updateCountDownText();
updateButtons();
if (mTimerRunning) {
mEndTime = prefs.getLong("endTime", 0);
mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
if (mTimeLeftInMillis < 0) {
mTimeLeftInMillis = 0;
mTimerRunning = false;
updateCountDownText();
updateButtons();
} else {
startTimer();
}
}
}
public void callNotification(){
Notification notification = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_money)
.setContentTitle("Test 1")
.setContentText("Important Message")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.build();
notificationManager.notify(1, notification);
}
}
I created a notification to show released movie and today's reminder. I have method for repeating alarm, but the notification only shows up whenever I turn "ON" the alarm by SwitchPreferences. What I want here is to show notifications every single day and every movie's that being release when I just set the alarm to "ON" once. From my case here, I have to set the alarm on and on to get the notifications, but it supposed to call the notifications or request the data when the onReceive() function in BroadcastReceiver() class being called. Summarize, I just want to turn ON the alarm once and then the method will check the data from api if it's match to the date requested every single day
Here is my AppCompatPref.java:
public class AppCompatPref extends PreferenceActivity {
private AppCompatDelegate mAppCompatDelegate;
#Override
protected void onCreate(Bundle savedInstanceState) {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
public void invalidateOptionsMenu() {
getDelegate().invalidateOptionsMenu();
}
private AppCompatDelegate getDelegate() {
if (mAppCompatDelegate == null) {
mAppCompatDelegate = AppCompatDelegate.create(this, null);
}
return mAppCompatDelegate;
}
public void setSupportActionBar(#Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
}
#Override
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
}
#Override
public void setContentView(#LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
#Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
#Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
#Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().addContentView(view, params);
}
#Override
protected void onTitleChanged(CharSequence title, int color) {
super.onTitleChanged(title, color);
getDelegate().setTitle(title);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
#Override
protected void onPostResume() {
super.onPostResume();
getDelegate().onPostResume();
}
#Override
protected void onStop() {
super.onStop();
getDelegate().onStop();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getDelegate().onConfigurationChanged(newConfig);
}
#Override
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
}
Here's my MovieDailyReminder:
public class MovieDailyReceiver extends BroadcastReceiver {
private void sendNotification(Context context, String title, String desc, int id) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
Intent mIntent = new Intent(context, MainActivity.class);
PendingIntent mPendingIntent = PendingIntent.getActivity(context, id, mIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
Uri uriTone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_notifications_black_24dp)
.setContentTitle(title)
.setContentText(desc)
.setContentIntent(mPendingIntent)
.setColor(ContextCompat.getColor(context, android.R.color.transparent))
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setSound(uriTone);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel mNotificationChannel = new NotificationChannel(
"11001", "NOTIFICATION_CHANNEL_NAME",
NotificationManager.IMPORTANCE_HIGH);
mNotificationChannel.enableLights(true);
mNotificationChannel.setLightColor(Color.YELLOW);
mNotificationChannel.enableVibration(true);
mNotificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
builder.setChannelId("11001");
mNotificationManager.createNotificationChannel(mNotificationChannel);
}
mNotificationManager.notify(id, builder.build());
}
private static PendingIntent getPendingIntent(Context context) {
Intent mIntent = new Intent(context, MovieDailyReceiver.class);
return PendingIntent.getBroadcast(context, 1001, mIntent, PendingIntent.FLAG_CANCEL_CURRENT);
}
public void setAlarm(Context context) {
cancelAlarm(context);
AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Calendar mCalendar = Calendar.getInstance();
mCalendar.set(Calendar.HOUR_OF_DAY, 7);
mCalendar.set(Calendar.MINUTE, 0);
mCalendar.set(Calendar.SECOND, 0);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mAlarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,mCalendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY,getPendingIntent(context));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,mCalendar.getTimeInMillis(), getPendingIntent(context));
}
Toast.makeText(context, "Daily Notif ON", Toast.LENGTH_SHORT).show();
}
public void cancelAlarm(Context context) {
AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.cancel(getPendingIntent(context));
Toast.makeText(context, "Daily Notif OFF", Toast.LENGTH_SHORT).show();
}
#Override
public void onReceive(Context context, Intent mIntent) {
sendNotification(context, context.getString(R.string.DailyTitle),
context.getString(R.string.DailyCheck), 1001);
}
}
Here's my MovieUpcomingReminder:
public class MovieUpcomingReceiver extends BroadcastReceiver {
private static int mNotifId = 2000;
private void sendNotification(Context context, String title, String mDesc, int id, ResultsItem mMovieResult) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
Uri uriTone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_notifications_black_24dp)
.setContentTitle(title)
.setContentText(mDesc)
.setColor(ContextCompat.getColor(context, android.R.color.transparent))
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setSound(uriTone);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel("11011",
"NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.YELLOW);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
builder.setChannelId("11011");
notificationManager.createNotificationChannel(notificationChannel);
}
notificationManager.notify(id, builder.build());
}
private static PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, MovieUpcomingReceiver.class);
return PendingIntent.getBroadcast(context, 1011, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
public void setAlarm(Context context, List<ResultsItem> mMovieResults) {
int delay = 0;
for (ResultsItem movie : mMovieResults) {
cancelAlarm(context);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MovieUpcomingReceiver.class);
intent.putExtra("movietitle", movie.getTitle());
intent.putExtra("movieid", movie.getId());
intent.putExtra("movieposter", movie.getPhoto());
intent.putExtra("moviemDescription", movie.getOverview());
intent.putExtra("moviedate", movie.getReleaseDate());
intent.putExtra("id", mNotifId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
100, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
alarmManager.setInexactRepeating(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis() + delay,
AlarmManager.INTERVAL_DAY,
pendingIntent
);
} else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis() + delay, pendingIntent);
}
mNotifId += 1;
delay += 3000;
}
Toast.makeText(context, "Upcoming Notif ON", Toast.LENGTH_SHORT).show();
}
#Override
public void onReceive(Context context, Intent intent) {
String mMovieTitle = intent.getStringExtra("movietitle");
int id = intent.getIntExtra("id", 0);
ResultsItem mMovieResult = new ResultsItem();
String mDesc = context.getString(R.string.todayRelease) + " : " +mMovieTitle;
sendNotification(context, context.getString(R.string.app_name), mDesc, id, mMovieResult);
}
public void cancelAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(getPendingIntent(context));
Toast.makeText(context, "Upcoming Notif OFF", Toast.LENGTH_SHORT).show();
}
}
Here's my SettingPref:
public class SettingPref extends AppCompatPref {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MainPreferenceFragment()).commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
public static class MainPreferenceFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
private RequestQueue mRequestQueue;
List<ResultsItem> mNotifList;
MovieDailyReceiver mMovieDailyReceiver = new MovieDailyReceiver();
MovieUpcomingReceiver mMovieUpcomingReceiver = new MovieUpcomingReceiver();
SwitchPreference mSwitchReminder;
SwitchPreference mSwitchToday;
public class GetMovieTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... strings) {
getData(strings[0]);
return null;
}
}
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
boolean value = (boolean) newValue;
if (key.equals(getString(R.string.todayreminder))) {
if (value) {
mMovieDailyReceiver.setAlarm(getActivity());
} else {
mMovieDailyReceiver.cancelAlarm(getActivity());
}
} else {
if (value) {
setReleaseAlarm();
} else {
mMovieUpcomingReceiver.cancelAlarm(getActivity());
}
}
return true;
}
private void setReleaseAlarm() {
Date today = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String todaysDate = dateFormat.format(today);
MainPreferenceFragment.GetMovieTask getDataAsync = new MainPreferenceFragment.GetMovieTask();
getDataAsync.execute("https://api.themoviedb.org/3/discover/movie?api_key=80077c53d53b7215a6c2f2120d065f81&primary_release_date.gte="+todaysDate+"&primary_release_date.lte="+todaysDate);
}
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
mNotifList = new ArrayList<>();
mRequestQueue = Volley.newRequestQueue(getActivity());
mSwitchReminder = (SwitchPreference) findPreference(getString(R.string.todayreminder));
mSwitchReminder.setOnPreferenceChangeListener(this);
mSwitchToday = (SwitchPreference) findPreference(getString(R.string.todayRelease));
mSwitchToday.setOnPreferenceChangeListener(this);
Preference myPref = findPreference(getString(R.string.languagesets));
myPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
startActivity(new Intent(Settings.ACTION_LOCALE_SETTINGS));
return true;
}
});
}
public void getData(String url) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
Date date = new Date();
final String today = dateFormat.format(date);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("results");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject data = jsonArray.getJSONObject(i);
ResultsItem movieItem = new ResultsItem();
movieItem.setTitle(data.getString("title"));
movieItem.setReleaseDate(data.getString("release_date"));
movieItem.setTitle(data.getString("title"));
movieItem.setOverview(data.getString("overview"));
movieItem.setOverview(data.getString("poster_path"));
if (data.getString("release_date").equals(today)) {
mNotifList.add(movieItem);
}
}
mMovieUpcomingReceiver.setAlarm(getActivity(), mNotifList);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
mRequestQueue.add(request);
}
}
}
I am making an android app that can send notifications even if the app is closed. So I added a NotificationService to call when the app is destroyed. But I get an error: System services not available to Activities before onCreate() when I force the app to stop.
This is the MainActivity.java:
public class MainActivity extends AppCompatActivity {
private Button button1;
private Button button2;
private Button button3;
private Button button4;
private static final String CHANNEL_ID = "telus";
private static final String CHANNEL_NAME = "telus app";
private static final String CHANNEL_DESC = "telus app notification";
private int count = 1;
Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1=(Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivity_web();
}
});
button2=(Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openActivationPage();
}
});
button3=(Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
openMysql();
}
});
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(CHANNEL_DESC);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
displayNotification();
}
});
class sendnotif extends TimerTask {
public void run() {
displayNotification();
}
}
Timer timer = new Timer();
timer.schedule(new sendnotif(), 0, 5000);
}
public void displayNotification(){
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_alarm)
.setContentTitle("Titre de la notification")
.setContentText("C'est le text de la notification")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
;
NotificationManagerCompat mNotifMgr = NotificationManagerCompat.from(this);
mNotifMgr.notify( 1, mBuilder.build());
}
public void openActivity_web(){
Intent intent = new Intent(this, WebPage.class);
startActivity(intent);
}
public void openActivationPage(){
Intent intent = new Intent(this, ActivationPage.class);
startActivity(intent);
}
public void openMysql(){
Intent intent = new Intent(this, mysql_display.class);
startActivity(intent);
}
protected void onDestroy(){
super.onDestroy();
startService(new Intent(this, NotificationService.class));
}
}
and this is the NotificationService.java:
public class NotificationService extends Service {
Timer timer;
TimerTask timerTask;
String TAG = "Timers";
int Your_X_SECS = 5;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onCreate() {
Log.e(TAG, "onCreate");
}
#Override
public void onDestroy() {
Log.e(TAG, "onDestroy");
stoptimertask();
super.onDestroy();
}
//we are going to use a handler to be able to run in our TimerTask
final Handler handler = new Handler();
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, after the first 5000ms the TimerTask will run every 10000ms
timer.schedule(timerTask, 5000, Your_X_SECS * 1000); //
//timer.schedule(timerTask, 5000,1000); //
}
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
//use a handler to run a toast that shows the current timestamp
handler.post(new Runnable() {
public void run() {
MainActivity notif = new MainActivity();
notif.displayNotification();
}
});
}
};
}
}
This is the error log when I try to manually stop the app:
2019 - 07 - 16 20: 10: 23.079 23334 - 23334 / com.example.testapp E / AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testapp, PID: 23334
java.lang.IllegalStateException: System services not available to Activities before onCreate()
at android.app.Activity.getSystemService(Activity.java: 5581)
at androidx.core.app.NotificationManagerCompat. < init > (NotificationManagerCompat.java: 158)
at androidx.core.app.NotificationManagerCompat.from(NotificationManagerCompat.java: 153)
at com.example.testapp.MainActivity.displayNotification(MainActivity.java: 103)
at com.example.testapp.NotificationService$1$1.run(NotificationService.java: 89)
at android.os.Handler.handleCallback(Handler.java: 751)
at android.os.Handler.dispatchMessage(Handler.java: 95)
at android.os.Looper.loop(Looper.java: 154)
at android.app.ActivityThread.main(ActivityThread.java: 6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java: 866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java: 756)
Thank you for your help.
I trying to set an alarm with date and time. Everything is fine, but after I set the alarm, the notification that I want it appears before the time (in minute) that I set before.
* ViewNote.java:
private DatePicker datePicker;
private TimePicker timePicker;
private Calendar dateTime;
private final static int REMINDER_RQS_CODE = 1;
...
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_view_note, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId())
{
case R.id.set_reminder:
datePicker = (DatePicker) findViewById(R.id.datePicker);
timePicker = (TimePicker) findViewById(R.id.timePicker);
Calendar now = Calendar.getInstance();
if(datePicker != null)
{
datePicker.init(Calendar.YEAR,
Calendar.MONTH,
Calendar.DAY_OF_MONTH,
null);
}
if(timePicker != null)
{
timePicker.setCurrentHour(now.get(Calendar.HOUR_OF_DAY));
timePicker.setCurrentMinute(now.get(Calendar.MINUTE));
}
AlertDialog.Builder setDateDialog = new AlertDialog.Builder(this)
.setView(R.layout.date_picker_dialog)
.setTitle("Pick date")
.setIcon(R.drawable.context_menu)
.setPositiveButton("Next", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dateTime = Calendar.getInstance();
if(datePicker != null)
{
dateTime.set(datePicker.getYear(),
datePicker.getMonth(),
datePicker.getDayOfMonth());
}
AlertDialog.Builder setTimeDialog = new AlertDialog.Builder(ViewNote.this)
.setView(R.layout.time_picker_dialog)
.setTitle("Pick time")
.setIcon(R.drawable.context_menu)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dateTime = Calendar.getInstance();
if(timePicker != null)
{
dateTime.set(timePicker.getCurrentHour(),
timePicker.getCurrentMinute(),
00);
}
setReminder(dateTime);
}
});
setTimeDialog.show();
}
});
setDateDialog.show();
return true;
}
return super.onOptionsItemSelected(item);
}
private void setReminder(Calendar dateTime)
{
Intent intent = new Intent(ViewNote.this, RemainderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ViewNote.this, REMINDER_RQS_CODE, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, dateTime.getTimeInMillis(), pendingIntent);
}
* date_picker_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<DatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/datePicker"
android:layout_gravity="center_horizontal" />
</LinearLayout>
* time_picker_dialog.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TimePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/timePicker"
android:layout_gravity="center_horizontal" />
</LinearLayout>
* ReminderReceiver.java:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class RemainderReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
/*Intent intent1 = new Intent(context, MyReminderService.class);
context.startService(intent1);*/
Log.i("ReminderReceiver", "onReceive method called");
try
{
Utils.generateNotification(context);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
* MyReminderService.java:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
public class MyReminderService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
* AndroidManifest.xml:
...
...
<uses-permission android:name="android.permission.WAKE_LOCK" />
...
...
<service android:name=".MyReminderService"
android:enabled="true" />
<receiver android:name=".RemainderReceiver"/>
</application>
</manifest>
To me it seems you call the wrong method of Calendar in onClick of the "PositiveButton". There you call:
dateTime.set(
timePicker.getCurrentHour(),
timePicker.getCurrentMinute(),
00);
It does not set the hour, minute and millisecond. Instead this method of java.util.Calendar is called which results in a nonsense value:
public final void set(int year,
int month,
int date)
See here: https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#set%28int,%20int,%20int%29
You can fix this by converting the time of the TimePicker into milliseconds and add that to the time of "dateTime" like below.
dateTime = Calendar.getInstance();
if(timePicker != null)
{
dateTime.setTimeInMillis(
dateTime.getTimeInMillis()
+ (timePicker.getCurrentHour() * 360000)
+ (timePicker.getCurrentMinute() * 60000));
}
setReminder(dateTime);
Of course 1 hour is 60 minutes which are 360 seconds which are 360000 milliseconds, and 1 minute is 60 seconds which are 60000 milliseconds.