Skip to content

Commit

Permalink
add: notification copy button (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
KaustubhPatange authored Aug 6, 2023
1 parent da1349e commit 73fd496
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import javax.inject.Inject
// A drop in component to send clipboard notifications like on copy clipboard
// cc: ClipboardNotification interface
class ClipboardNotificationImpl @Inject constructor(
@ApplicationContext private val context: Context
@ApplicationContext private val context: Context
) : ClipboardNotification {
override fun notifyOnCopy(data: String, withSpecialActions: Boolean) {
Notifications.sendClipboardCopiedNotification(
context = context,
text = data,
withSpecialActions = withSpecialActions
)
}
override fun notifyOnCopy(
data: String,
withCopyButton: Boolean,
withSpecialActions: Boolean
) {
Notifications.sendClipboardCopiedNotification(
context = context,
text = data,
withCopyButton = withCopyButton,
withSpecialActions = withSpecialActions
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ object Notifications {
fun sendClipboardCopiedNotification(
context: Context,
text: String,
withCopyButton: Boolean = false,
withSpecialActions: Boolean = true
): Unit = with(context) {
val notificationId = text.hashCode()

val deleteIntent = SpecialActionsReceiver.createDeleteAction(context, text, notificationId)
val copyIntent = SpecialActionsReceiver.createCopyAction(context, text, notificationId)

val specialIntent =
SpecialActions.launchIntent(context, text, SpecialActionOption(showShareOption = true))
Expand All @@ -57,6 +59,18 @@ object Notifications {
.setColor(getColorAttr(R.attr.colorAccent))
.setContentIntent(NotificationUtils.getAppLaunchPendingIntent(this))

if (withCopyButton) {
notificationBuilder.addAction(
R.drawable.ic_copy,
context.getString(android.R.string.copy),
PendingIntent.getBroadcast(
context,
getRandomPendingCode(),
copyIntent,
NotificationUtils.getPendingIntentFlags()
)
)
}
if (withSpecialActions) {
notificationBuilder.addAction(
R.drawable.ic_delete_white,
Expand Down
4 changes: 2 additions & 2 deletions XClipper.Android/buildSrc/src/main/java/AndroidConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ object AndroidConfig {
const val TARGET_SDK_VERSION = 33
const val BUILD_TOOLS_VERSION = "30.0.3" // deprecated

const val VERSION_CODE = 41
const val VERSION_NAME = "1.3.6"
const val VERSION_CODE = 42
const val VERSION_NAME = "1.3.7"

const val ID = "com.kpstv.xclipper"
const val TEST_INSTRUMENTATION_RUNNER = "androidx.test.runner.AndroidJUnitRunner"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.kpstv.xclipper.di.notifications.ClipboardNotification
import com.kpstv.xclipper.extensions.launchInIO
import com.kpstv.xclipper.extensions.launchInMain
import com.kpstv.xclipper.extensions.toInt
import com.kpstv.xclipper.ui.helpers.AppSettings
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -21,6 +22,7 @@ class ClipRepositoryHelperImpl @Inject constructor(
@ApplicationContext private val context: Context,
private val clipboardNotification: ClipboardNotification,
private val repository: MainRepository,
private val appSettings: AppSettings,
) : ClipRepositoryHelper {
private val pendingClipData = ArrayDeque<String>()

Expand All @@ -35,7 +37,11 @@ class ClipRepositoryHelperImpl @Inject constructor(
}

// A concurrent way to inserting clips into database.
private suspend fun internalInsertOrUpdateClip(data: String?, toNotify: Boolean = true, toFirebase: Boolean = true) {
private suspend fun internalInsertOrUpdateClip(
data: String?,
toNotify: Boolean = true,
toFirebase: Boolean = true
) {
if (data == null || data.isBlank()) return
if (pendingClipData.contains(data)) return
pendingClipData.addLast(data)
Expand All @@ -55,7 +61,12 @@ class ClipRepositoryHelperImpl @Inject constructor(
* @param notifyOffset An offset value to show combined (eg: x clips added) instead of individual
* notifications.
*/
override fun insertOrUpdateClip(clips: List<Clip>, toFirebase: Boolean, toNotify: Boolean, notifyOffset: Int) {
override fun insertOrUpdateClip(
clips: List<Clip>,
toFirebase: Boolean,
toNotify: Boolean,
notifyOffset: Int
) {
launchInIO {
val singleNotify = clips.size <= notifyOffset
var addedClips = 0
Expand All @@ -82,7 +93,7 @@ class ClipRepositoryHelperImpl @Inject constructor(
}
}

private suspend fun addOrUpdateData(data: String, toFirebase: Boolean) : Boolean {
private suspend fun addOrUpdateData(data: String, toFirebase: Boolean): Boolean {
return if (repository.checkForDuplicate(data)) {
repository.getClipByData(data)?.let { clip ->
repository.updateTime(clip)
Expand All @@ -100,7 +111,11 @@ class ClipRepositoryHelperImpl @Inject constructor(

private fun sendClipNotification(data: String, withAction: Boolean = true) {
launchInMain {
clipboardNotification.notifyOnCopy(data, withAction)
clipboardNotification.notifyOnCopy(
data = data,
withCopyButton = appSettings.isNotificationCopyButtonEnabled(),
withSpecialActions = withAction
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.kpstv.xclipper.di.notifications

interface ClipboardNotification {
fun notifyOnCopy(data: String, withSpecialActions: Boolean = true)
fun notifyOnCopy(data: String, withCopyButton: Boolean = false, withSpecialActions: Boolean = true)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS
import android.os.Build
import android.widget.Toast
import com.kpstv.xclipper.data.provider.ClipboardProvider
import com.kpstv.xclipper.data.repository.MainRepository
import com.kpstv.xclipper.extensions.elements.AbstractBroadcastReceiver
import com.kpstv.xclipper.extensions.launchInIO
import com.kpstv.xclipper.extensions.utils.ToastyUtils
import com.kpstv.xclipper.specials.R
import com.kpstv.xclipper.ui.helpers.AppSettings
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
Expand All @@ -21,6 +25,7 @@ class SpecialActionsReceiver : AbstractBroadcastReceiver() {

companion object {
private const val ACTION_CLIP_DELETE = "com.kpstv.xclipper.clip_delete"
private const val ACTION_CLIP_COPY = "com.kpstv.xclipper.clip_copy"

private const val APP_CLIP_DATA = "com.kpstv.xclipper.clip_data"
private const val NOTIFICATION_CODE = "com.kpstv.xclipper.sp.notification_code"
Expand All @@ -32,6 +37,14 @@ class SpecialActionsReceiver : AbstractBroadcastReceiver() {
action = ACTION_CLIP_DELETE
}
}

fun createCopyAction(context: Context, data: String, notificationId: Int) : Intent {
return Intent(context, SpecialActionsReceiver::class.java).apply {
putExtra(APP_CLIP_DATA, data)
putExtra(NOTIFICATION_CODE, notificationId)
action = ACTION_CLIP_COPY
}
}
}

override fun onReceive(context: Context, intent: Intent) {
Expand All @@ -41,6 +54,14 @@ class SpecialActionsReceiver : AbstractBroadcastReceiver() {
val notifyId = intent.getIntExtra(NOTIFICATION_CODE, -1)

when(intent.action) {
ACTION_CLIP_COPY -> {
clipboardProvider.setClipboard(data)

// On Android 12+, system will automatically display this notification
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
Toast.makeText(context, context.getString(R.string.copy_to_clipboard), Toast.LENGTH_SHORT).show()
}
}
ACTION_CLIP_DELETE -> {
launchInIO {
repository.deleteClip(data)
Expand All @@ -49,11 +70,11 @@ class SpecialActionsReceiver : AbstractBroadcastReceiver() {
if (appSettings.isClipboardClearEnabled() && clipboardProvider.getCurrentClip().value == data) {
clipboardProvider.clearClipboard()
}

dismissNotification(context, notifyId)
collapseStatusBar(context)
}
}

collapseStatusBar(context)
}

private fun dismissNotification(context: Context, notificationId: Int) {
Expand All @@ -64,6 +85,10 @@ class SpecialActionsReceiver : AbstractBroadcastReceiver() {
}

private fun collapseStatusBar(context: Context) {
context.sendBroadcast(Intent(ACTION_CLOSE_SYSTEM_DIALOGS))
try {
context.sendBroadcast(Intent(ACTION_CLOSE_SYSTEM_DIALOGS))
} catch (e: SecurityException) {
// Won't work Android 12+
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.DATABASE_BINDING
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.DATABASE_DELETE_BINDING
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.IMAGE_MARKDOWN
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.IMPROVE_DETECTION
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.NOTIFICATION_COPY_BUTTON
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.ON_BOARDING_SCREEN
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.POLICY_DISCLOSURE
import com.kpstv.xclipper.ui.helpers.AppSettingKeys.Keys.SHOW_SEARCH_FEATURE
Expand Down Expand Up @@ -86,6 +87,12 @@ class AppSettings @Inject constructor(
notifyListeners(SWIPE_DELETE_CLIP_ITEM, value)
}

fun isNotificationCopyButtonEnabled(): Boolean = preferenceProvider.getBooleanKey(NOTIFICATION_COPY_BUTTON, false)
fun setNotificationCopyButtonEnabled(value: Boolean) {
preferenceProvider.putBooleanKey(NOTIFICATION_COPY_BUTTON, value)
notifyListeners(NOTIFICATION_COPY_BUTTON, value)
}

fun isTextTrimmingEnabled(): Boolean = preferenceProvider.getBooleanKey(CLIP_TEXT_TRIMMING, false)
fun setTextTrimmingEnabled(value: Boolean) {
preferenceProvider.putBooleanKey(CLIP_TEXT_TRIMMING, value)
Expand Down Expand Up @@ -207,6 +214,7 @@ class AppSettings @Inject constructor(
IMAGE_MARKDOWN,
CLIPBOARD_SUGGESTIONS,
SWIPE_DELETE_CLIP_ITEM,
NOTIFICATION_COPY_BUTTON,
CLIP_TEXT_TRIMMING,
ON_BOARDING_SCREEN,
CLIPBOARD_BLACKLIST_APPS,
Expand Down Expand Up @@ -236,6 +244,7 @@ annotation class AppSettingKeys {
internal const val SUGGESTION_BUBBLE_Y_POS = "suggestion_bubble_y_pos"
internal const val SUGGESTION_BUBBLE_X_GRAVITY = "suggestion_bubble_x_gravity"
const val SWIPE_DELETE_CLIP_ITEM = "swipe_delete_clip_item"
const val NOTIFICATION_COPY_BUTTON = "notification_copy_button"
const val CLIP_TEXT_TRIMMING = "clip_text_trimming"
const val ON_BOARDING_SCREEN = "tutorial_pref"
const val CLIPBOARD_BLACKLIST_APPS = "blacklist_pref"
Expand Down
Loading

0 comments on commit 73fd496

Please sign in to comment.