feat: better notification
This commit is contained in:
parent
2f7c6e734e
commit
e256fb1560
8 changed files with 131 additions and 27 deletions
|
@ -1,5 +1,6 @@
|
||||||
package ani.dantotsu
|
package ani.dantotsu
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
@ -16,7 +17,6 @@ import android.content.res.Configuration
|
||||||
import android.content.res.Resources.getSystem
|
import android.content.res.Resources.getSystem
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.Manifest
|
|
||||||
import android.media.MediaScannerConnection
|
import android.media.MediaScannerConnection
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities.*
|
import android.net.NetworkCapabilities.*
|
||||||
|
@ -688,7 +688,11 @@ fun downloadsPermission(activity: AppCompatActivity): Boolean {
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
|
|
||||||
return if (requiredPermissions.isNotEmpty()) {
|
return if (requiredPermissions.isNotEmpty()) {
|
||||||
ActivityCompat.requestPermissions(activity, requiredPermissions, DOWNLOADS_PERMISSION_REQUEST_CODE)
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
requiredPermissions,
|
||||||
|
DOWNLOADS_PERMISSION_REQUEST_CODE
|
||||||
|
)
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -1068,3 +1072,11 @@ suspend fun View.pop() {
|
||||||
}
|
}
|
||||||
delay(100)
|
delay(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun logToFile(context: Context, message: String) {
|
||||||
|
val externalFilesDir = context.getExternalFilesDir(null)
|
||||||
|
val file = File(externalFilesDir, "notifications.log")
|
||||||
|
file.appendText(message)
|
||||||
|
file.appendText("\n")
|
||||||
|
}
|
|
@ -220,6 +220,21 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
intent.extras?.let { extras ->
|
||||||
|
val fragmentToLoad = extras.getString("FRAGMENT_TO_LOAD")
|
||||||
|
val mediaId = extras.getInt("mediaId", -1)
|
||||||
|
val commentId = extras.getInt("commentId", -1)
|
||||||
|
|
||||||
|
if (fragmentToLoad != null && mediaId != -1 && commentId != -1) {
|
||||||
|
val detailIntent = Intent(this, MediaDetailsActivity::class.java).apply {
|
||||||
|
putExtra("FRAGMENT_TO_LOAD", fragmentToLoad)
|
||||||
|
putExtra("mediaId", mediaId)
|
||||||
|
putExtra("commentId", commentId)
|
||||||
|
}
|
||||||
|
startActivity(detailIntent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
val offlineMode: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
val offlineMode: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
||||||
if (!isOnline(this)) {
|
if (!isOnline(this)) {
|
||||||
snackString(this@MainActivity.getString(R.string.no_internet_connection))
|
snackString(this@MainActivity.getString(R.string.no_internet_connection))
|
||||||
|
|
|
@ -17,9 +17,11 @@ import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
import okio.IOException
|
import okio.IOException
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
object CommentsAPI {
|
object CommentsAPI {
|
||||||
val address: String = "https://1224665.xyz:443"
|
val address: String = "https://1224665.xyz:443"
|
||||||
|
@ -214,11 +216,13 @@ object CommentsAPI {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun reportComment(commentId: Int, username: String, mediaTitle: String): Boolean {
|
suspend fun reportComment(commentId: Int, username: String, mediaTitle: String, reportedId: String): Boolean {
|
||||||
val url = "$address/report/$commentId"
|
val url = "$address/report/$commentId"
|
||||||
val body = FormBody.Builder()
|
val body = FormBody.Builder()
|
||||||
.add("username", username)
|
.add("username", username)
|
||||||
.add("mediaName", mediaTitle)
|
.add("mediaName", mediaTitle)
|
||||||
|
.add("reporter", Anilist.username ?: "unknown")
|
||||||
|
.add("reportedId", reportedId)
|
||||||
.build()
|
.build()
|
||||||
val request = requestBuilder()
|
val request = requestBuilder()
|
||||||
val json = try{
|
val json = try{
|
||||||
|
@ -234,9 +238,9 @@ object CommentsAPI {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getNotifications(): NotificationResponse? {
|
suspend fun getNotifications(client: OkHttpClient): NotificationResponse? {
|
||||||
val url = "$address/notification/reply"
|
val url = "$address/notification/reply"
|
||||||
val request = requestBuilder()
|
val request = requestBuilder(client)
|
||||||
val json = try {
|
val json = try {
|
||||||
request.get(url)
|
request.get(url)
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
@ -255,7 +259,7 @@ object CommentsAPI {
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchAuthToken() {
|
suspend fun fetchAuthToken(client: OkHttpClient? = null) {
|
||||||
if (authToken != null) return
|
if (authToken != null) return
|
||||||
val MAX_RETRIES = 5
|
val MAX_RETRIES = 5
|
||||||
val tokenLifetime: Long = 1000 * 60 * 60 * 24 * 6 // 6 days
|
val tokenLifetime: Long = 1000 * 60 * 60 * 24 * 6 // 6 days
|
||||||
|
@ -276,7 +280,7 @@ object CommentsAPI {
|
||||||
val token = PrefManager.getVal(PrefName.AnilistToken, null as String?) ?: return
|
val token = PrefManager.getVal(PrefName.AnilistToken, null as String?) ?: return
|
||||||
repeat(MAX_RETRIES) {
|
repeat(MAX_RETRIES) {
|
||||||
try {
|
try {
|
||||||
val json = authRequest(token, url)
|
val json = authRequest(token, url, client)
|
||||||
if (json.code == 200) {
|
if (json.code == 200) {
|
||||||
if (!json.text.startsWith("{")) throw IOException("Invalid response")
|
if (!json.text.startsWith("{")) throw IOException("Invalid response")
|
||||||
val parsed = try {
|
val parsed = try {
|
||||||
|
@ -307,11 +311,11 @@ object CommentsAPI {
|
||||||
snackString("Failed to login after multiple attempts")
|
snackString("Failed to login after multiple attempts")
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun authRequest(token: String, url: String): NiceResponse {
|
private suspend fun authRequest(token: String, url: String, client: OkHttpClient? = null): NiceResponse {
|
||||||
val body: FormBody = FormBody.Builder()
|
val body: FormBody = FormBody.Builder()
|
||||||
.add("token", token)
|
.add("token", token)
|
||||||
.build()
|
.build()
|
||||||
val request = requestBuilder()
|
val request = if (client != null) requestBuilder(client) else requestBuilder()
|
||||||
return request.post(url, requestBody = body)
|
return request.post(url, requestBody = body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,9 +329,9 @@ object CommentsAPI {
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestBuilder(): Requests {
|
private fun requestBuilder(client: OkHttpClient = Injekt.get<NetworkHelper>().client): Requests {
|
||||||
return Requests(
|
return Requests(
|
||||||
Injekt.get<NetworkHelper>().client,
|
client,
|
||||||
headerBuilder()
|
headerBuilder()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,7 +139,7 @@ class CommentItem(val comment: Comment,
|
||||||
dialogBuilder("Report Comment", "Only report comments that violate the rules. Are you sure you want to report this comment?") {
|
dialogBuilder("Report Comment", "Only report comments that violate the rules. Are you sure you want to report this comment?") {
|
||||||
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val success = CommentsAPI.reportComment(comment.commentId, comment.username, commentsFragment.mediaName)
|
val success = CommentsAPI.reportComment(comment.commentId, comment.username, commentsFragment.mediaName, comment.userId)
|
||||||
if (success) {
|
if (success) {
|
||||||
snackString("Comment Reported")
|
snackString("Comment Reported")
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,7 +348,6 @@ class CommentsFragment : Fragment() {
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
adapter.notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
@ -423,6 +422,10 @@ class CommentsFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.commentsProgressBar.visibility = View.GONE
|
||||||
|
binding.commentsList.visibility = View.VISIBLE
|
||||||
|
adapter.add(section)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortComments(comments: List<Comment>?): List<Comment> {
|
private fun sortComments(comments: List<Comment>?): List<Comment> {
|
||||||
|
@ -693,7 +696,7 @@ class CommentsFragment : Fragment() {
|
||||||
.usePlugin(TaskListPlugin.create(activity))
|
.usePlugin(TaskListPlugin.create(activity))
|
||||||
.usePlugin(HtmlPlugin.create { plugin ->
|
.usePlugin(HtmlPlugin.create { plugin ->
|
||||||
plugin.addHandler(
|
plugin.addHandler(
|
||||||
TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre")
|
TagHandlerNoOp.create("h1", "h2", "h3", "h4", "h5", "h6", "hr", "pre", "a")
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
|
.usePlugin(GlideImagesPlugin.create(object : GlideImagesPlugin.GlideStore {
|
||||||
|
|
|
@ -13,6 +13,10 @@ class MediaNameFetch {
|
||||||
mediaIds.forEachIndexed { index, mediaId ->
|
mediaIds.forEachIndexed { index, mediaId ->
|
||||||
query += """
|
query += """
|
||||||
media$index: Media(id: $mediaId) {
|
media$index: Media(id: $mediaId) {
|
||||||
|
coverImage {
|
||||||
|
medium
|
||||||
|
color
|
||||||
|
}
|
||||||
id
|
id
|
||||||
title {
|
title {
|
||||||
romaji
|
romaji
|
||||||
|
@ -24,7 +28,7 @@ class MediaNameFetch {
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchMediaTitles(ids: List<Int>): Map<Int, String> {
|
suspend fun fetchMediaTitles(ids: List<Int>): Map<Int, ReturnedData> {
|
||||||
return try {
|
return try {
|
||||||
val url = "https://graphql.anilist.co/"
|
val url = "https://graphql.anilist.co/"
|
||||||
val data = mapOf(
|
val data = mapOf(
|
||||||
|
@ -40,15 +44,19 @@ class MediaNameFetch {
|
||||||
data = data
|
data = data
|
||||||
)
|
)
|
||||||
val mediaResponse = parseMediaResponseWithGson(response.text)
|
val mediaResponse = parseMediaResponseWithGson(response.text)
|
||||||
val mediaMap = mutableMapOf<Int, String>()
|
val mediaMap = mutableMapOf<Int, ReturnedData>()
|
||||||
mediaResponse.data.forEach { (_, mediaItem) ->
|
mediaResponse.data.forEach { (_, mediaItem) ->
|
||||||
mediaMap[mediaItem.id] = mediaItem.title.romaji
|
mediaMap[mediaItem.id] = ReturnedData(
|
||||||
|
mediaItem.title.romaji,
|
||||||
|
mediaItem.coverImage.medium,
|
||||||
|
mediaItem.coverImage.color
|
||||||
|
)
|
||||||
}
|
}
|
||||||
mediaMap
|
mediaMap
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val errorMap = mutableMapOf<Int, String>()
|
val errorMap = mutableMapOf<Int, ReturnedData>()
|
||||||
ids.forEach { errorMap[it] = "Unknown" }
|
ids.forEach { errorMap[it] = ReturnedData("Unknown", "", "#222222") }
|
||||||
errorMap
|
errorMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,14 +66,17 @@ class MediaNameFetch {
|
||||||
val type = object : TypeToken<MediaResponse>() {}.type
|
val type = object : TypeToken<MediaResponse>() {}.type
|
||||||
return gson.fromJson(response, type)
|
return gson.fromJson(response, type)
|
||||||
}
|
}
|
||||||
|
data class ReturnedData(val title: String, val coverImage: String, val color: String)
|
||||||
|
|
||||||
data class MediaResponse(val data: Map<String, MediaItem>)
|
data class MediaResponse(val data: Map<String, MediaItem>)
|
||||||
data class MediaItem(
|
data class MediaItem(
|
||||||
|
val coverImage: MediaCoverImage,
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val title: MediaTitle
|
val title: MediaTitle
|
||||||
)
|
)
|
||||||
|
|
||||||
data class MediaTitle(val romaji: String)
|
data class MediaTitle(val romaji: String)
|
||||||
|
data class MediaCoverImage(val medium: String, val color: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,37 +5,55 @@ import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import ani.dantotsu.MainActivity
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.connections.comments.CommentsAPI
|
import ani.dantotsu.connections.comments.CommentsAPI
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import java.io.File
|
||||||
|
import java.text.DateFormat
|
||||||
|
|
||||||
|
|
||||||
class NotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
class NotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
||||||
Worker(appContext, workerParams) {
|
Worker(appContext, workerParams) {
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val scope = CoroutineScope(Dispatchers.IO)
|
val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
//time in human readable format
|
||||||
|
val formattedTime = DateFormat.getDateTimeInstance().format(System.currentTimeMillis())
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val notifications = CommentsAPI.getNotifications()
|
PrefManager.init(applicationContext) //make sure prefs are initialized
|
||||||
|
val client = OkHttpClient()
|
||||||
|
CommentsAPI.fetchAuthToken(client)
|
||||||
|
val notifications = CommentsAPI.getNotifications(client)
|
||||||
val mediaIds = notifications?.notifications?.map { it.mediaId }
|
val mediaIds = notifications?.notifications?.map { it.mediaId }
|
||||||
val names = MediaNameFetch.fetchMediaTitles(mediaIds ?: emptyList())
|
val names = MediaNameFetch.fetchMediaTitles(mediaIds ?: emptyList())
|
||||||
notifications?.notifications?.forEach {
|
notifications?.notifications?.forEach {
|
||||||
val title = "New Comment Reply"
|
val title = "New Comment Reply"
|
||||||
val mediaName = names[it.mediaId] ?: "Unknown"
|
val mediaName = names[it.mediaId]?.title ?: "Unknown"
|
||||||
val message = "${it.username} replied to your comment in $mediaName"
|
val message = "${it.username} replied to your comment in $mediaName"
|
||||||
val notification = createNotification(
|
val notification = createNotification(
|
||||||
NotificationType.COMMENT_REPLY,
|
NotificationType.COMMENT_REPLY,
|
||||||
message,
|
message,
|
||||||
title,
|
title,
|
||||||
it.mediaId,
|
it.mediaId,
|
||||||
it.commentId
|
it.commentId,
|
||||||
|
names[it.mediaId]?.color ?: "#222222",
|
||||||
|
names[it.mediaId]?.coverImage ?: ""
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ActivityCompat.checkSelfPermission(
|
if (ActivityCompat.checkSelfPermission(
|
||||||
|
@ -46,11 +64,10 @@ class NotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
||||||
NotificationManagerCompat.from(applicationContext)
|
NotificationManagerCompat.from(applicationContext)
|
||||||
.notify(
|
.notify(
|
||||||
NotificationType.COMMENT_REPLY.id,
|
NotificationType.COMMENT_REPLY.id,
|
||||||
Notifications.ID_COMMENT_REPLY,
|
it.commentId,
|
||||||
notification
|
notification
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success()
|
return Result.success()
|
||||||
|
@ -61,11 +78,13 @@ class NotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
||||||
message: String,
|
message: String,
|
||||||
title: String,
|
title: String,
|
||||||
mediaId: Int,
|
mediaId: Int,
|
||||||
commentId: Int
|
commentId: Int,
|
||||||
|
color: String,
|
||||||
|
imageUrl: String
|
||||||
): android.app.Notification {
|
): android.app.Notification {
|
||||||
val notification = when (notificationType) {
|
val notification = when (notificationType) {
|
||||||
NotificationType.COMMENT_REPLY -> {
|
NotificationType.COMMENT_REPLY -> {
|
||||||
val intent = Intent(applicationContext, MediaDetailsActivity::class.java).apply {
|
val intent = Intent(applicationContext, MainActivity::class.java).apply {
|
||||||
putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
|
putExtra("FRAGMENT_TO_LOAD", "COMMENTS")
|
||||||
putExtra("mediaId", mediaId)
|
putExtra("mediaId", mediaId)
|
||||||
putExtra("commentId", commentId)
|
putExtra("commentId", commentId)
|
||||||
|
@ -80,16 +99,46 @@ class NotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
||||||
val builder = NotificationCompat.Builder(applicationContext, notificationType.id)
|
val builder = NotificationCompat.Builder(applicationContext, notificationType.id)
|
||||||
.setContentTitle(title)
|
.setContentTitle(title)
|
||||||
.setContentText(message)
|
.setContentText(message)
|
||||||
.setSmallIcon(R.drawable.ic_round_comment_24)
|
.setSmallIcon(R.drawable.notification_icon)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setContentIntent(pendingIntent)
|
.setContentIntent(pendingIntent)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
|
if (imageUrl.isNotEmpty()) {
|
||||||
|
val bitmap = getBitmapFromUrl(imageUrl)
|
||||||
|
if (bitmap != null) {
|
||||||
|
builder.setLargeIcon(bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (color.isNotEmpty()) {
|
||||||
|
builder.color = Color.parseColor(color)
|
||||||
|
}
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return notification
|
return notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getBitmapFromVectorDrawable(context: Context, drawableId: Int): Bitmap? {
|
||||||
|
val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
|
||||||
|
val bitmap = Bitmap.createBitmap(
|
||||||
|
drawable.intrinsicWidth,
|
||||||
|
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
|
||||||
|
)
|
||||||
|
val canvas = Canvas(bitmap)
|
||||||
|
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||||
|
drawable.draw(canvas)
|
||||||
|
return bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBitmapFromUrl(url: String): Bitmap? {
|
||||||
|
return try {
|
||||||
|
val inputStream = java.net.URL(url).openStream()
|
||||||
|
BitmapFactory.decodeStream(inputStream)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum class NotificationType(val id: String) {
|
enum class NotificationType(val id: String) {
|
||||||
COMMENT_REPLY(Notifications.CHANNEL_COMMENTS),
|
COMMENT_REPLY(Notifications.CHANNEL_COMMENTS),
|
||||||
}
|
}
|
||||||
|
|
10
app/src/main/res/drawable/notification_icon.xml
Normal file
10
app/src/main/res/drawable/notification_icon.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector android:height="200dp" android:viewportHeight="768"
|
||||||
|
android:viewportWidth="768" android:width="200dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M-118.57,-113.45l1005.14,0l0,994.92l-1005.14,0z"/>
|
||||||
|
<path android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="m-277.06,-109.04c0,87.59 70.57,158.7 158.49,161.06L-118.57,-109.02L886.57,-109.02L886.57,877L1131.17,877L1131.17,-109.04ZM886.57,877L-118.57,877l0,0.04l1005.14,0zM-118.57,877L-118.57,716c-87.89,2.36 -158.45,73.43 -158.49,161zM-118.57,716c1.49,-0.04 2.95,-0.22 4.45,-0.22L384,715.78C569.12,715.78 719.18,567.23 719.18,384.01 719.18,200.77 569.1,52.24 384,52.24L-114.12,52.24c-1.5,0 -2.96,-0.18 -4.45,-0.22l0,143.59L384,195.61c105.11,0 190.34,84.36 190.34,188.4 0,104.06 -85.23,188.4 -190.34,188.4L-118.57,572.41ZM-118.57,572.41L-118.57,195.61L-363.17,195.61l0,376.8z" android:strokeWidth="0"/>
|
||||||
|
<path android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="m496.85,350.69 l-147.92,-84.53c-25.92,-14.81 -58.3,3.7 -58.3,33.32l0,169.06c0,29.62 32.4,48.13 58.3,33.32L496.85,417.33c25.92,-14.81 25.92,-51.83 0,-66.64z" android:strokeWidth="0"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
Loading…
Add table
Add a link
Reference in a new issue