-
-
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...