I create easy first my App in Android Studio but i dont know why my App not work in background.
The application works very well if it is on all the time, but when I hide it, it stops working. What am I doing wrong? I try used Intent for background ready but.. i think here is my problem :<
"MojaKlasa" is empty class. If I replace "MojaKlasa" with "MainActivity" in Intent, the application starts, but when the condition from Timer is met, it stops working and turns off.
My code:
package com.example.vicky.notificationexample
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import android.widget.RemoteViews
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import org.jetbrains.anko.uiThread
import java.net.URL
import java.nio.charset.Charset
import java.util.*
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
lateinit var notificationManager : NotificationManager
lateinit var notificationChannel : NotificationChannel
lateinit var builder : Notification.Builder
private val channelId = "com.example.vicky.notificationexample"
private val description = "Test notification"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val intent = Intent(this,MojaKlasa::class.java)
val pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
val contentView = RemoteViews(packageName,R.layout.notification_layout)
contentView.setTextViewText(R.id.tv_title,"Test")
contentView.setTextViewText(R.id.tv_content,"Notify")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel = NotificationChannel(channelId,description,NotificationManager.IMPORTANCE_HIGH)
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.GREEN
notificationChannel.enableVibration(false)
notificationManager.createNotificationChannel(notificationChannel)
builder = Notification.Builder(this,channelId)
.setContent(contentView)
.setSmallIcon(R.drawable.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(this.resources,R.drawable.ic_launcher))
.setContentIntent(pendingIntent)
}else{
builder = Notification.Builder(this)
.setContent(contentView)
.setSmallIcon(R.drawable.ic_launcher_round)
.setLargeIcon(BitmapFactory.decodeResource(this.resources,R.drawable.ic_launcher))
.setContentIntent(pendingIntent)
}
val executor = Executors.newScheduledThreadPool(5)
doAsync(executorService = executor){
val result = URL("https://linkdomain/app2.php").readText()
uiThread {
toast(result)
var stare:Int = result.toInt()
Timer().scheduleAtFixedRate(object : TimerTask() {
#SuppressLint("SimpleDateFormat", "ServiceCast")
override fun run() {
val url = URL("https://linkdomain/app.php").readText(Charset.forName("ISO-8859-1"))
url.toInt();
if (url > stare.toString()) {
stare = stare.toInt()+1
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (vibrator.hasVibrator()) { // Vibrator availability checking
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)) // New vibrate method for API Level 26 or higher
} else {
vibrator.vibrate(500) // Vibrate method for below API Level 26
}
}
notificationManager.notify(stare.toInt(),builder.build())
}
//Log.d("TAG", "url "+url)
//Log.d("TAG", "cos "+stare)
}
}, 0, 1000) //put here time 1000 milliseconds=1 second
}
}
}
fun buTestUpdateText2 (view: View) {
val destination = Intent(this#MainActivity.baseContext, MainActivity::class.java)
.also { it.putExtra("KEY", 0) }
startActivity(destination)
}
}
Related
this is my code it only shows a simple notification how can i make it also show a pop up or heads-up notification.
sorry if this is so simple help if you know something
Notification.kt
`
package com.example.notifkos
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
const val notificationID = 1
const val channelID = "channel1"
const val titleExtra = "titleExtra"
const val messageExtra = "messageExtra"
class Notification : BroadcastReceiver()
{
override fun onReceive(context: Context, intent: Intent)
{
val importance = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NotificationManager.IMPORTANCE_HIGH
} else {
TODO("VERSION.SDK_INT < N")
}
val notification = NotificationCompat.Builder(context, channelID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(intent.getStringExtra(titleExtra))
.setContentText(intent.getStringExtra(messageExtra))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.notify(notificationID, notification)
}
Mainactivity.kt
package com.example.notifkos
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.example.notifkos.databinding.ActivityMainBinding
import java.util.*
class MainActivity : AppCompatActivity()
{
private lateinit var binding : ActivityMainBinding
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
}
binding.submitButton.setOnClickListener { scheduleNotification() }
}
#RequiresApi(Build.VERSION_CODES.M)
private fun scheduleNotification()
{
val intent = Intent(applicationContext, Notification::class.java)
val title = binding.titleET.text.toString()
val message = binding.messageET.text.toString()
intent.putExtra(titleExtra, title)
intent.putExtra(messageExtra, message)
val pendingIntent = PendingIntent.getBroadcast(
applicationContext,
notificationID,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
val time = getTime()
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
time,
pendingIntent
)
showAlert(time, title, message)
}
private fun showAlert(time: Long, title: String, message: String)
{
val importance = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
NotificationManager.IMPORTANCE_HIGH
} else {
TODO("VERSION.SDK_INT < N")
}
val date = Date(time)
val dateFormat = android.text.format.DateFormat.getLongDateFormat(applicationContext)
val timeFormat = android.text.format.DateFormat.getTimeFormat(applicationContext)
AlertDialog.Builder(this)
.setTitle("Notification Scheduled")
.setMessage(
"Title: " + title +
"\nMessage: " + message +
"\nAt: " + dateFormat.format(date) + " " + timeFormat.format(date))
.setPositiveButton("Okay"){_,_ ->}
.show()
}
#RequiresApi(Build.VERSION_CODES.M)
private fun getTime(): Long
{
val minute = binding.timePicker.minute
val hour = binding.timePicker.hour
val day = binding.datePicker.dayOfMonth
val month = binding.datePicker.month
val year = binding.datePicker.year
val calendar = Calendar.getInstance()
calendar.set(year, month, day, hour, minute)
return calendar.timeInMillis
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel()
{
val name = "Notif Channel"
val desc = "A Description of the Channel"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(channelID, name, importance)
channel.description = desc
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
`
Well, how about adding more details. Like, what are you trying to accomplish, how are you doing it, what difficulties you have found with the approach, another approaches you have tried, etc. In summary, without code isn't a good question neither is with just code. Search for the balance!
there was nothing i could get my hands on
i have read android stuio docs so please dont tell me to read that please
This is my code:
package com.example.notifsimple
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
val CHNANNEL_ID = "channel_99"
val id = 123456
#RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnNotif.setOnClickListener{
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(CHNANNEL_ID, "اسم الفناه", importance) //
val notification = NotificationCompat.Builder(this,CHNANNEL_ID)//
.setSmallIcon(R.drawable.android)
.setContentTitle("New Animal")
.setContentText("a new Cowala has been dicovered in the Amazons!")
.build()
//val intent = Intent(this,MainActivity::class.java)
// val pendingIntent = PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT)
// notification.contentIntent = pendingIntent
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.createNotificationChannel(mChannel)
mNotificationManager.notify(id, notification)
}
}
}
and the Code that i COMMENTED is the problem. when I UNcomment it the app will just give me the notification above but then exit app and tell me that "The app stopped working" and when I click on the notification doesn't do anything
Try this:
class MainActivity : AppCompatActivity() {
val CHNANNEL_ID = "channel_99"
val id = 123456
#RequiresApi(Build.VERSION_CODES.O)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnNotif.setOnClickListener{
val importance = NotificationManager.IMPORTANCE_HIGH
val mChannel = NotificationChannel(CHNANNEL_ID, "اسم الفناه", importance) //
val intent = Intent(applicationContext,MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(applicationContext,0,intent,PendingIntent.FLAG_UPDATE_CURRENT) // use applicationContext, not `this`
val notification = NotificationCompat.Builder(this,CHNANNEL_ID)//
.setSmallIcon(R.drawable.android)
.setContentTitle("New Animal")
.setContentText("a new Cowala has been dicovered in the Amazons!")
.setContentIntent(pendingIntent) // this is how I set the pending intent and it works
.build()
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mNotificationManager.createNotificationChannel(mChannel)
mNotificationManager.notify(id, notification)
}
}
}
I am using the following code with the notification where the images are from the drawable. But, I want to use the image URL to use the images that I have in my Firebase Storage. How can I do that. I tried adding the URL and that gives me an error.
val bitmap = BitmapFactory.decodeResource(applicationContext.resources,R.drawable.product_image)
val bitmapLargeIcon = BitmapFactory.decodeResource(applicationContext.resources,R.drawable.comp_logo)
This is the complete code.
private fun sendNotification(title: String, message: String) {
val intent: Intent = Intent(this, SplashActivity::class.java).apply {
flags=Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0,intent, 0)
val bitmap = BitmapFactory.decodeResource(applicationContext.resources,R.drawable.product_image)
val bitmapLargeIcon = BitmapFactory.decodeResource(applicationContext.resources,R.drawable.comp_logo)
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.product_image)
.setContentTitle(title)
.setContentText(message)
.setLargeIcon(bitmapLargeIcon)
.setStyle(NotificationCompat.BigPictureStyle().bigPicture(bitmap))
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
with(NotificationManagerCompat.from(this)) {
notify(notificationId, builder.build())
}
}
You can try this:
package com.tutorialsbuzz.notificationimgload
import android.app.NotificationChannel
import android.app.NotificationManager
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import java.util.concurrent.atomic.AtomicInteger
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val notifyBtn = findViewById<Button>(R.id.notifyBtn)
notifyBtn.setOnClickListener { v: View? ->
createNotification(
"Hey Hello!!",
"How are you?",
resources.getString(R.string.notification_channel)
)
}
}
private fun createNotification(
title: String, content: String,
channedId: String
) {
val notificationBuilder = NotificationCompat.Builder(applicationContext, channedId)
.setSmallIcon(R.drawable.ic_notifications_active)
.setAutoCancel(true)
.setLights(Color.BLUE, 500, 500)
.setVibrate(longArrayOf(500, 500, 500))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentTitle(title)
.setContentText(content)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
// Since android Oreo notification channel is needed.
val notificationManager = NotificationManagerCompat.from(this#MainActivity)
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channedId,
channedId,
NotificationManager.IMPORTANCE_HIGH
)
channel.lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
notificationManager.createNotificationChannel(channel)
}
val imageUrl =
"https://i.pinimg.com/originals/1b/7e/4e/1b7e4eac8558ad54d6fe94ed4e14cb84.jpg"
Glide.with(applicationContext)
.asBitmap()
.load(imageUrl)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
notificationBuilder.setLargeIcon(resource) //largeIcon
notificationBuilder.setStyle(
NotificationCompat.BigPictureStyle().bigPicture(resource)
) //Big Picture
val notification = notificationBuilder.build()
notificationManager.notify(NotificationID.iD, notification)
}
override fun onLoadCleared(placeholder: Drawable?) {}
override fun onLoadFailed(errorDrawable: Drawable?) {
super.onLoadFailed(errorDrawable)
}
})
}
internal object NotificationID {
private val c = AtomicInteger(100)
val iD: Int
get() = c.incrementAndGet()
}
}
I created a worker that runs on connecting audio jack but I can't stop the running worker on audio jack disconnection which is triggered by Broadcast Receiver.
I want to keep track of headset usage limit with the help of worker which will only trigger when there is headset connection and will stop the worker whenever the audio jack/headset is detached.
Here is the Broadcast Reciver Code
package com.xanjit.focusly.broadcast_receivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.work.*
import com.xanjit.focusly.workers.GetHeadsetUsageTimeWorker
import java.sql.Timestamp
import java.time.Duration
class AudioInputBroadcastReceiver : BroadcastReceiver() {
// lateinit var workManager: WorkManager
// lateinit var workRequest: WorkRequest
#RequiresApi(Build.VERSION_CODES.O)
override fun onReceive(context: Context?, intent: Intent?) {
val isConnected = intent!!.getIntExtra("state", 0) === 1
val workManager = WorkManager.getInstance(context!!)
if (isConnected) {
var startTime = System.currentTimeMillis()
Toast.makeText(context,"Connected",Toast.LENGTH_SHORT).show()
val data = Data.Builder()
data.putLong("startTime", startTime)
val workRequest = OneTimeWorkRequest.Builder(
GetHeadsetUsageTimeWorker::class.java,
).setInputData(data.build())
.build()
workManager.enqueue(workRequest)
} else {
Toast.makeText(context,"Disconnected",Toast.LENGTH_SHORT).show()
// val data = Data.Builder()
//
// data.putBoolean("isStopped", true)
// val workRequest = OneTimeWorkRequest.Builder(
// GetHeadsetUsageTimeWorker::class.java,
// ).setInputData(data.build()).build()
workManager.cancelAllWork()
}
}
override fun peekService(myContext: Context?, service: Intent?): IBinder {
return super.peekService(myContext, service)
}
}
Worker Code
package com.xanjit.focusly.workers
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.BitmapFactory
import android.media.AudioAttributes
import android.net.Uri
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.work.ListenableWorker
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.xanjit.focusly.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.sql.Timestamp
import kotlin.time.seconds
var TAG = "EarGuard"
class GetHeadsetUsageTimeWorker(context: Context, workerParams: WorkerParameters) : Worker(
context,
workerParams,
) {
#RequiresApi(Build.VERSION_CODES.O)
override fun doWork(): Result {
// Log.d("EarGuard", "StartTime Worker")
var startTime = inputData.getLong("startTime", System.currentTimeMillis())
// var stopped = inputData.getBoolean("isStopped", false)
// if (!isStopped and !stopped) {
// Log.d("EarGuard", "StartTime Worker" + startTime)
Log.d(TAG, isStopped.toString())
Thread {
while (!isStopped) {
Thread.sleep(1000)
var usageTime = System.currentTimeMillis()
usageTime -= startTime
Log.d(TAG, usageTime.toString())
if ((usageTime / 1000) == 5L) {
CoroutineScope(Dispatchers.Main).launch(Dispatchers.Main)
{
// Toast.makeText(
// applicationContext,
// "Time limit exceeded", Toast.LENGTH_LONG
// ).show()
val notificationChannel = NotificationChannel(
"123",
"EarGuard",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel.description =
"You have exceeded the maximum usage limit of listening on headset"
notificationChannel.name = "EarGuard"
notificationChannel.lockscreenVisibility = 1
notificationChannel.shouldShowLights()
notificationChannel.enableVibration(true)
notificationChannel.setSound(
Uri.parse("R.raw.sad"),
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH).build()
)
notificationChannel.shouldVibrate()
var notificationManager =
applicationContext.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(notificationChannel)
notificationManager.notify(
123, NotificationCompat.Builder(
applicationContext, "123"
).setSmallIcon(R.drawable.ic_launcher_foreground)
.setLargeIcon(
BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.ic_launcher_foreground
)
)
.setSound(
Uri.parse("R.raw.sad")
)
.setContentTitle("EarGuard")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentText("You have exceeded the maximum usage limit of listening on headset")
.setAutoCancel(false)
.setChannelId("123")
.build()
)
}
break
}
}
}.start()
// }
return Result.success()
}
// #SuppressLint("RestrictedApi")
// override fun isRunInForeground(): Boolean {
// return super.isRunInForeground()
// }
override fun onStopped() {
Log.d(TAG, "Stopped Worker")
super.onStopped()
}
}
MainActivity.kt
package com.xanjit.focusly
import android.content.*
import android.net.Uri
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkRequest
import com.xanjit.focusly.broadcast_receivers.AudioInputBroadcastReceiver
import com.xanjit.focusly.services.CustomService
import com.xanjit.focusly.workers.GetHeadsetUsageTimeWorker
import java.lang.Exception
class MainActivity : ComponentActivity() {
lateinit var receiver: BroadcastReceiver
var TAG = "EarGuard"
lateinit var workManager: WorkManager
lateinit var workRequest: WorkRequest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
receiver = AudioInputBroadcastReceiver()
setContent {
Surface() {
Scaffold(
topBar = {
TopAppBar() {
}
}
) {
}
}
}
}
override fun onStart() {
super.onStart()
var intentFilter = IntentFilter("android.intent.action.HEADSET_PLUG");
registerReceiver(receiver, intentFilter)
}
}
You could try keep you workRequest.id on your AudioInputBroadcastReceiver and in case of disconnection you can stop this work. I think its a bad practice cancel all running works if you only want to finish your audio work.
cancelAllWork()
Cancels all unfinished work. Use this method with extreme caution! By invoking it, you will potentially affect other modules or libraries in your codebase. It is strongly recommended that you use one of the other cancellation methods at your disposal.
class AudioInputBroadcastReceiver : BroadcastReceiver() {
val workerRequestId: UUID? = null
#RequiresApi(Build.VERSION_CODES.O)
override fun onReceive(context: Context?, intent: Intent?) {
val isConnected = intent!!.getIntExtra("state", 0) === 1
val workManager = WorkManager.getInstance(context!!)
if (isConnected) {
....
val workRequest = OneTimeWorkRequest.Builder(
GetHeadsetUsageTimeWorker::class.java,
).setInputData(data.build()).build()
workerRequestId = workRequest.id
workManager.enqueue(workRequest)
} else {
Toast.makeText(context,"Disconnected",Toast.LENGTH_SHORT).show()
workerRequestId?.let { workManager.cancelWorkById(it) }
}
}
...
I used the Foreground Service code from Floating Window in localazy.com. My foreground Service didn't send correct codes to the onStartCommand() after I change the package prefix name that they in given in their code in the putExtra Method it always sends null instead of sending the EXIT and NOTE command. Actually, what is the use of the package prefix in the key name? Why did this problem happen?.
package com.krithik.floatingnote.service
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import android.util.Log
import android.widget.Toast
import androidx.core.app.NotificationCompat
import com.krithik.floatingnote.R
const val INTENT_COMMAND = "com.localazy.quicknote.COMMAND"
const val INTENT_COMMAND_EXIT = "EXIT"
const val INTENT_COMMAND_NOTE = "NOTE"
private const val NOTIFICATION_CHANNEL_GENERAL = "note_general"
private const val CODE_FOREGROUND_SERVICE = 1
private const val CODE_EXIT_INTENT = 2
private const val CODE_NOTE_INTENT = 3
class FloatingService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
/**
* Remove the foreground notification and stop the service.
*/
private fun stopService() {
stopForeground(true)
stopSelf()
}
/**
* Create and show the foreground notification.
*/
private fun showNotification() {
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val exitIntent = Intent(this, FloatingService::class.java).apply {
putExtra(INTENT_COMMAND, INTENT_COMMAND_EXIT)
}
val noteIntent = Intent(this, FloatingService::class.java).apply {
putExtra(INTENT_COMMAND, INTENT_COMMAND_NOTE)
}
val exitPendingIntent = PendingIntent.getService(
this, CODE_EXIT_INTENT, exitIntent, 0
)
val notePendingIntent = PendingIntent.getService(
this, CODE_NOTE_INTENT, noteIntent, 0
)
// From Android O, it's necessary to create a notification channel first.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
with(
NotificationChannel(
NOTIFICATION_CHANNEL_GENERAL,
getString(R.string.notification_channel_general),
NotificationManager.IMPORTANCE_DEFAULT
)
) {
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
manager.createNotificationChannel(this)
}
} catch (ignored: Exception) {
// Ignore exception.
}
}
with(
NotificationCompat.Builder(
this,
NOTIFICATION_CHANNEL_GENERAL
)
) {
setContentTitle(getString(R.string.app_name))
setContentText(getString(R.string.notification_text))
setAutoCancel(false)
setOngoing(true)
setSmallIcon(R.drawable.ic_baseline_note_24)
setContentIntent(notePendingIntent)
addAction(
NotificationCompat.Action(
0,
getString(R.string.notification_exit),
exitPendingIntent
)
)
startForeground(CODE_FOREGROUND_SERVICE, build())
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val command = intent.getStringExtra(INTENT_COMMAND)
Log.i("ServiceCommand", command.toString())
// Exit the service if we receive the EXIT command.
// START_NOT_STICKY is important here, we don't want
// the service to be relaunched.
if (command == INTENT_COMMAND_EXIT) {
stopService()
return START_NOT_STICKY
}
// Be sure to show the notification first for all commands.
// Don't worry, repeated calls have no effects.
showNotification()
// Show the floating window for adding a new note.
if (command == INTENT_COMMAND_NOTE) {
Toast.makeText(
this,
"Floating window to be added",
Toast.LENGTH_SHORT
).show()
}
return START_STICKY
}
}
package com.krithik.floatingnote
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.krithik.floatingnote.database.Note
import com.krithik.floatingnote.database.NoteDatabase
import com.krithik.floatingnote.database.NoteRepository
import com.krithik.floatingnote.databinding.ActivityMainBinding
import com.krithik.floatingnote.service.FloatingService
import com.krithik.floatingnote.service.INTENT_COMMAND
import com.krithik.floatingnote.viewModel.NoteViewModel
import com.krithik.floatingnote.viewModel.NoteViewModelFactory
import com.krithik.floatingnote.viewModel.RecyclerViewAdapter
class MainActivity : AppCompatActivity(), RecyclerViewAdapter.RowClickListener {
private lateinit var binding: ActivityMainBinding
private lateinit var noteViewModel: NoteViewModel
private lateinit var adapter: RecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startFloatingService()
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val dao = NoteDatabase.getInstance(application).noteDao
val repository = NoteRepository(dao)
val factory = NoteViewModelFactory(repository)
noteViewModel = ViewModelProvider(this, factory).get(NoteViewModel::class.java)
binding.noteViewModel = noteViewModel
binding.lifecycleOwner = this
noteViewModel.message.observe(this, Observer {
it.getContentIfNotHandled()?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
})
initRecyclerView()
noteViewModel.noteList.observe(this, Observer {
adapter.submitList(it)
})
}
private fun initRecyclerView() {
binding.noteRecyclerView.layoutManager = LinearLayoutManager(this)
adapter = RecyclerViewAdapter(this)
binding.noteRecyclerView.adapter = adapter
}
override fun onDeleteNote(note: Note) {
noteViewModel.deleteNote(note)
}
private fun Context.startFloatingService(command: String = "") {
val intent = Intent(this, FloatingService::class.java)
if (command.isNotBlank()) intent.putExtra(INTENT_COMMAND, command)
Log.i("Command", INTENT_COMMAND + command)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
this.startForegroundService(intent)
} else {
this.startService(intent)
}
}
}
```