-
-
Save mjohnsullivan/403149218ecb480e7759 to your computer and use it in GitHub Desktop.
// | |
// Copyright 2015 Google Inc. All Rights Reserved. | |
// | |
// Licensed under the Apache License, Version 2.0 (the "License"); | |
// you may not use this file except in compliance with the License. | |
// You may obtain a copy of the License at | |
// | |
// http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// Unless required by applicable law or agreed to in writing, software | |
// distributed under the License is distributed on an "AS IS" BASIS, | |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
// See the License for the specific language governing permissions and | |
// limitations under the License. | |
package com.example.google.timerservice; | |
import android.app.Notification; | |
import android.app.PendingIntent; | |
import android.app.Service; | |
import android.content.ComponentName; | |
import android.content.Intent; | |
import android.content.ServiceConnection; | |
import android.os.Binder; | |
import android.os.Bundle; | |
import android.os.Handler; | |
import android.os.IBinder; | |
import android.os.Message; | |
import android.support.v4.app.NotificationCompat; | |
import android.support.v7.app.AppCompatActivity; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.TextView; | |
import java.lang.ref.WeakReference; | |
/** | |
* Example activity to manage a long-running timer, which survives the destruction of the activity | |
* by using a foreground service and notification | |
* | |
* Add the following to the manifest: | |
* <service android:name=".MainActivity$TimerService" android:exported="false" /> | |
*/ | |
public class TimerActivity extends AppCompatActivity { | |
private static final String TAG = TimerActivity.class.getSimpleName(); | |
private TimerService timerService; | |
private boolean serviceBound; | |
private Button timerButton; | |
private TextView timerTextView; | |
// Handler to update the UI every second when the timer is running | |
private final Handler mUpdateTimeHandler = new UIUpdateHandler(this); | |
// Message type for the handler | |
private final static int MSG_UPDATE_TIME = 0; | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
timerButton = (Button)findViewById(R.id.timer_button); | |
timerTextView = (TextView)findViewById(R.id.timer_text_view); | |
} | |
@Override | |
protected void onStart() { | |
super.onStart(); | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Starting and binding service"); | |
} | |
Intent i = new Intent(this, TimerService.class); | |
startService(i); | |
bindService(i, mConnection, 0); | |
} | |
@Override | |
protected void onStop() { | |
super.onStop(); | |
updateUIStopRun(); | |
if (serviceBound) { | |
// If a timer is active, foreground the service, otherwise kill the service | |
if (timerService.isTimerRunning()) { | |
timerService.foreground(); | |
} | |
else { | |
stopService(new Intent(this, TimerService.class)); | |
} | |
// Unbind the service | |
unbindService(mConnection); | |
serviceBound = false; | |
} | |
} | |
public void runButtonClick(View v) { | |
if (serviceBound && !timerService.isTimerRunning()) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Starting timer"); | |
} | |
timerService.startTimer(); | |
updateUIStartRun(); | |
} | |
else if (serviceBound && timerService.isTimerRunning()) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Stopping timer"); | |
} | |
timerService.stopTimer(); | |
updateUIStopRun(); | |
} | |
} | |
/** | |
* Updates the UI when a run starts | |
*/ | |
private void updateUIStartRun() { | |
mUpdateTimeHandler.sendEmptyMessage(MSG_UPDATE_TIME); | |
timerButton.setText(R.string.timer_stop_button); | |
} | |
/** | |
* Updates the UI when a run stops | |
*/ | |
private void updateUIStopRun() { | |
mUpdateTimeHandler.removeMessages(MSG_UPDATE_TIME); | |
timerButton.setText(R.string.timer_start_button); | |
} | |
/** | |
* Updates the timer readout in the UI; the service must be bound | |
*/ | |
private void updateUITimer() { | |
if (serviceBound) { | |
timerTextView.setText(timerService.elapsedTime() + " seconds"); | |
} | |
} | |
/** | |
* Callback for service binding, passed to bindService() | |
*/ | |
private ServiceConnection mConnection = new ServiceConnection() { | |
@Override | |
public void onServiceConnected(ComponentName className, IBinder service) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Service bound"); | |
} | |
TimerService.RunServiceBinder binder = (TimerService.RunServiceBinder) service; | |
timerService = binder.getService(); | |
serviceBound = true; | |
// Ensure the service is not in the foreground when bound | |
timerService.background(); | |
// Update the UI if the service is already running the timer | |
if (timerService.isTimerRunning()) { | |
updateUIStartRun(); | |
} | |
} | |
@Override | |
public void onServiceDisconnected(ComponentName name) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Service disconnect"); | |
} | |
serviceBound = false; | |
} | |
}; | |
/** | |
* When the timer is running, use this handler to update | |
* the UI every second to show timer progress | |
*/ | |
static class UIUpdateHandler extends Handler { | |
private final static int UPDATE_RATE_MS = 1000; | |
private final WeakReference<TimerActivity> activity; | |
UIUpdateHandler(TimerActivity activity) { | |
this.activity = new WeakReference<>(activity); | |
} | |
@Override | |
public void handleMessage(Message message) { | |
if (MSG_UPDATE_TIME == message.what) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "updating time"); | |
} | |
activity.get().updateUITimer(); | |
sendEmptyMessageDelayed(MSG_UPDATE_TIME, UPDATE_RATE_MS); | |
} | |
} | |
} | |
/** | |
* Timer service tracks the start and end time of timer; service can be placed into the | |
* foreground to prevent it being killed when the activity goes away | |
*/ | |
public static class TimerService extends Service { | |
private static final String TAG = TimerService.class.getSimpleName(); | |
// Start and end times in milliseconds | |
private long startTime, endTime; | |
// Is the service tracking time? | |
private boolean isTimerRunning; | |
// Foreground notification id | |
private static final int NOTIFICATION_ID = 1; | |
// Service binder | |
private final IBinder serviceBinder = new RunServiceBinder(); | |
public class RunServiceBinder extends Binder { | |
TimerService getService() { | |
return TimerService.this; | |
} | |
} | |
@Override | |
public void onCreate() { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Creating service"); | |
} | |
startTime = 0; | |
endTime = 0; | |
isTimerRunning = false; | |
} | |
@Override | |
public int onStartCommand(Intent intent, int flags, int startId) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Starting service"); | |
} | |
return Service.START_STICKY; | |
} | |
@Override | |
public IBinder onBind(Intent intent) { | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Binding service"); | |
} | |
return serviceBinder; | |
} | |
@Override | |
public void onDestroy() { | |
super.onDestroy(); | |
if (Log.isLoggable(TAG, Log.VERBOSE)) { | |
Log.v(TAG, "Destroying service"); | |
} | |
} | |
/** | |
* Starts the timer | |
*/ | |
public void startTimer() { | |
if (!isTimerRunning) { | |
startTime = System.currentTimeMillis(); | |
isTimerRunning = true; | |
} | |
else { | |
Log.e(TAG, "startTimer request for an already running timer"); | |
} | |
} | |
/** | |
* Stops the timer | |
*/ | |
public void stopTimer() { | |
if (isTimerRunning) { | |
endTime = System.currentTimeMillis(); | |
isTimerRunning = false; | |
} | |
else { | |
Log.e(TAG, "stopTimer request for a timer that isn't running"); | |
} | |
} | |
/** | |
* @return whether the timer is running | |
*/ | |
public boolean isTimerRunning() { | |
return isTimerRunning; | |
} | |
/** | |
* Returns the elapsed time | |
* | |
* @return the elapsed time in seconds | |
*/ | |
public long elapsedTime() { | |
// If the timer is running, the end time will be zero | |
return endTime > startTime ? | |
(endTime - startTime) / 1000 : | |
(System.currentTimeMillis() - startTime) / 1000; | |
} | |
/** | |
* Place the service into the foreground | |
*/ | |
public void foreground() { | |
startForeground(NOTIFICATION_ID, createNotification()); | |
} | |
/** | |
* Return the service to the background | |
*/ | |
public void background() { | |
stopForeground(true); | |
} | |
/** | |
* Creates a notification for placing the service into the foreground | |
* | |
* @return a notification for interacting with the service when in the foreground | |
*/ | |
private Notification createNotification() { | |
NotificationCompat.Builder builder = new NotificationCompat.Builder(this) | |
.setContentTitle("Timer Active") | |
.setContentText("Tap to return to the timer") | |
.setSmallIcon(R.mipmap.ic_launcher); | |
Intent resultIntent = new Intent(this, TimerActivity.class); | |
PendingIntent resultPendingIntent = | |
PendingIntent.getActivity(this, 0, resultIntent, | |
PendingIntent.FLAG_UPDATE_CURRENT); | |
builder.setContentIntent(resultPendingIntent); | |
return builder.build(); | |
} | |
} | |
} |
Don't forget to register the service in Manifest.
<service android:name=".TimerActivity$TimerService" />
worst code
Working Great...
how to use it i need the full code
I like this approach, because it works fine. I have tested for 6 hours, and it didn't stop, or slow down. Thanks ;)
Great Code, But! Only works with small changes in Android 8.1 and above.
-
Grant permission
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-
Create a notification channel like this
` @RequiresApi(Build.VERSION_CODES.O) private fun createNotificationChannel(channelId: String, channelName: String) : String { val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE) chan.lightColor = getColor(R.color.grayTextDarker) chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager service.createNotificationChannel(chan) return channelId } `
-
Check version
` private fun createNotification() : Notification { val channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { createNotificationChannel("my_service", "My Background Service") } else { "" } val builder = NotificationCompat.Builder(this, channelId) .setContentTitle("Timer Active") .setContentText("Tap to return to the timer") .setSmallIcon(R.mipmap.ic_launcher) val resultIntent = Intent(this, LessonActivity::class.java) val resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT) builder.setContentIntent(resultPendingIntent) return builder.build() }`
Solution from here:
https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
As you can see above, this code was written 5 year ago, when Android 6.0 was released, so I think we can "forgive" this little thing for the writer ;)
New Update?
Working Great...