Code Monkey home page Code Monkey logo

lockscreenwidgetsample's Introduction

Lockscreen Widget

The widget we are showing on lockscreen isn't an actual widget, it's our Activity that look like a lockscreen and we put a View on it.

In order start an activity when there is no foreground component, we have to attach it to a notification by calling "setFullscreenIntent" when building the notification and allow the activity to show on lockscreen in the activity.

Update 18/08/2023

  • Only send notification if the device screen is off or at lockscreen. AppNotificationManager.kt:
val powerManager = context.getSystemService(PowerManager::class.java)
val keyguardManager = context.getSystemService(KeyguardManager::class.java)

// Only send the notification when screen if off or screen is on but in lockscreen
if (!powerManager.isInteractive || (powerManager.isInteractive && keyguardManager.isKeyguardLocked)) {
    // Send notification here
}
  • Cancel the notification right after the widget activity shows to avoid user seeing or clicking on the notification. This should be call right in onCreate of the widget activity
AppNotificationManager.cancelNotification(this, AppNotificationManager.LockscreenWidgetNotificationId)
  • Add dismiss keyguard function to prompt user to unlock screen after clicking on the widget. Call this in onClick listener.
private fun dismissKeyguard() {
    with(getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            requestDismissKeyguard(this@LockscreenWidgetActivity, null)
        }
    }
}

Step by step:

  • First we need some permissions:
<manifest>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />~~~~
</manifest>
  • Now we create an activity that looks like lockscreen with our desired view on it. Take a look at my sample "LockscreenWidgetActivity".
  • Put the follow block of code into the activity and call it in onCreate to allow the activity to show on lockscreen.
private fun showOnLockscreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
        setShowWhenLocked(true)
    } else {
        window.addFlags(WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
    }
}
  • To make the activity looks like lockscreen, we have to put some attributes into activity's theme:
<style name="FullscreenReminderTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:windowShowWallpaper">true</item>
    <item name="android:windowTranslucentNavigation">true</item>
    <item name="android:windowTranslucentStatus">true</item>
</style>
  • And we also want to exclude the Activity from recent applications. Put these into Activity's manifest:
<activity android:name=".LockscreenWidgetActivity" android:exported="false" android:excludeFromRecents="true" android:launchMode="singleInstance"
    android:noHistory="true" android:theme="@style/FullscreenReminderTheme" />
  • Now let's build the notification and put the intent to launch activity into it
fun sendLockscreenWidgetNotification(context: Context) {
    val fullScreenIntent = Intent(context, LockscreenWidgetActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK
    }
    val fullScreenPendingIntent = PendingIntent.getActivity(
        context, 0,
        fullScreenIntent, PendingIntent.FLAG_IMMUTABLE
    )

    val notificationBuilder =
        NotificationCompat.Builder(context, DefaultNotificationChannelId)
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle(context.getString(R.string.app_name))
            .setStyle(NotificationCompat.DecoratedCustomViewStyle())
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setCategory(NotificationCompat.CATEGORY_CALL)
            .setFullScreenIntent(fullScreenPendingIntent, true)

    // Cancel old notification
    cancelNotification(context, LockscreenWidgetNotificationId)
    // Send new notification
    sendNotification(context, LockscreenWidgetNotificationId, notificationBuilder.build())
}
  • When the notification is fired, there are two situations:
    • If device is in lockscreen (regardless the screen is on or not): the activity will show over the lockscreen.
    • If device is not in lockscreen: a notification is shown instead.

Note:

  • In demo code, when the button is clicked, I set a delay of 5 seconds before the notification is fired.
  • In real situation, you should schedule to fire the notification using schedule mechanics. Currently I'm using AlarmManager.

Use AlarmManager to schedule notification

First we need some more permissions:

<manifest>
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
</manifest>
  • We need SCHEDULE_EXACT_ALARM so that we can schedule with AlarmManager
  • We need RECEIVE_BOOT_COMPLETED in order to reschedule alarm after boot because all alarms will be gone if device resets.

Next, we need to schedule the alarm at the time that we want

private fun scheduleLockscreenWidget(context: Context, requestCode: Int, atHour: Int, atMinute: Int = 0) {
    Log.d(TAG, "scheduleLockscreenWidget")
    val alarmIntent = Intent(context, AlarmReceiver::class.java)
    val pendingIntent = PendingIntent.getBroadcast(
        context, requestCode, alarmIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )

    // Set time to show widget
    val currentTimeMillis = System.currentTimeMillis()
    val calendar: Calendar = Calendar.getInstance().apply {
        timeInMillis = currentTimeMillis
        set(Calendar.HOUR_OF_DAY, atHour)
        set(Calendar.MINUTE, atMinute)
        set(Calendar.SECOND, 0)

        // If current time is after set time, push set time back one day
        if (timeInMillis <= currentTimeMillis) add(Calendar.HOUR_OF_DAY, 24)
    }

    // Schedule alarm
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, pendingIntent)

    Log.d(TAG, "scheduleFullscreenReminder: alarm scheduled at ${calendar.time}")
}

Then we simple call this function in our code depends on where we want to call in

To cancel this alarm, declare a similar PendingIntent with the same Intent and request code then call cancel on it:

fun cancelLockscreenWidgets(context: Context) {
    // Create a pending intent with the same intent and request code
    val pendingIntent = PendingIntent.getBroadcast(
        context, lockscreenWidgetRequestCode,
        Intent(context, AlarmReceiver::class.java), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_NO_CREATE
    )
    // Cancel it with alarm manager
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    alarmManager.cancel(pendingIntent)
}

lockscreenwidgetsample's People

Contributors

hieunvapero avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.