Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
AloqaIM-Android
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
AloqaIM-Android
Commits
5d064696
Unverified
Commit
5d064696
authored
Nov 14, 2017
by
Leonardo Aramaki
Committed by
GitHub
Nov 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #571 from RocketChat/fix/notifications
Fix broken notifications
parents
839f60b0
68755814
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
279 additions
and
1096 deletions
+279
-1096
build.gradle
app/build.gradle
+4
-1
RocketChatCache.java
app/src/main/java/chat/rocket/android/RocketChatCache.java
+5
-1
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+118
-66
PushNotificationHandler.java
...ava/chat/rocket/android/push/PushNotificationHandler.java
+0
-986
RocketChatCacheTest.kt
...rc/test/kotlin/chat.rocket.android/RocketChatCacheTest.kt
+0
-42
PushManagerTest.kt
...c/test/kotlin/chat.rocket.android/push/PushManagerTest.kt
+152
-0
No files found.
app/build.gradle
View file @
5d064696
...
...
@@ -150,8 +150,11 @@ dependencies {
compile
'com.github.matrixxun:MaterialBadgeTextView:c5a27e8243'
compile
'com.github.chrisbanes:PhotoView:2.0.0'
testCompile
'junit:junit:4.12'
testCompile
"org.mockito:mockito-core:2.8.9"
testCompile
'org.robolectric:robolectric:3.3'
testCompile
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
testCompile
"com.nhaarman:mockito-kotlin:1.1.0"
testCompile
'org.amshove.kluent:kluent:1.14'
testCompile
"org.jetbrains.kotlin:kotlin-reflect:$rootProject.ext.kotlinVersion"
}
apply
plugin:
'com.google.gms.google-services'
app/src/main/java/chat/rocket/android/RocketChatCache.java
View file @
5d064696
...
...
@@ -17,6 +17,7 @@ import java.util.UUID;
import
chat.rocket.android.helper.Logger
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.android.push.PushManager
;
import
chat.rocket.core.utils.Pair
;
import
io.reactivex.BackpressureStrategy
;
import
io.reactivex.Flowable
;
...
...
@@ -104,9 +105,12 @@ public class RocketChatCache {
}
}
public
@N
onNull
String
getSiteUrlFor
(
String
hostname
)
{
public
@N
ullable
String
getSiteUrlFor
(
String
hostname
)
{
try
{
String
selectedServerHostname
=
getSelectedServerHostname
();
if
(
getLoginHostnamesJson
()
==
null
||
getLoginHostnamesJson
().
isEmpty
())
{
return
null
;
}
return
new
JSONObject
(
getLoginHostnamesJson
())
.
optString
(
hostname
,
selectedServerHostname
);
}
catch
(
JSONException
e
)
{
...
...
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
5d064696
...
...
@@ -65,14 +65,19 @@ object PushManager {
@Synchronized
fun
handle
(
context
:
Context
,
data
:
Bundle
)
{
val
appContext
=
context
.
applicationContext
val
message
=
data
[
"message"
]
as
String
val
image
=
data
[
"image"
]
as
String
val
ejson
=
data
[
"ejson"
]
as
String
val
notId
=
data
[
"notId"
]
as
String
val
style
=
data
[
"style"
]
as
String
val
summaryText
=
data
[
"summaryText"
]
as
String
val
count
=
data
[
"count"
]
as
String
val
title
=
data
[
"title"
]
as
String
val
message
=
data
[
"message"
]
as
String
?
val
image
=
data
[
"image"
]
as
String
?
val
ejson
=
data
[
"ejson"
]
as
String
?
val
notId
=
data
[
"notId"
]
as
String
?
?:
randomizer
.
nextInt
().
toString
()
val
style
=
data
[
"style"
]
as
String
?
val
summaryText
=
data
[
"summaryText"
]
as
String
?
val
count
=
data
[
"count"
]
as
String
?
val
title
=
data
[
"title"
]
as
String
?
if
(
ejson
==
null
||
message
==
null
||
title
==
null
)
{
return
}
val
lastPushMessage
=
PushMessage
(
title
,
message
,
image
,
ejson
,
count
,
notId
,
summaryText
,
style
)
// We should use Timber here
...
...
@@ -123,7 +128,10 @@ object PushManager {
return
group
}
private
fun
showNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
)
{
internal
fun
showNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
)
{
if
(
lastPushMessage
.
host
==
null
||
lastPushMessage
.
message
==
null
||
lastPushMessage
.
title
==
null
)
{
return
}
val
manager
:
NotificationManager
=
context
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
as
NotificationManager
...
...
@@ -141,23 +149,37 @@ object PushManager {
if
(
isAndroidVersionAtLeast
(
Build
.
VERSION_CODES
.
N
))
{
val
notification
=
createSingleNotificationForNougatAndAbove
(
context
,
lastPushMessage
)
val
groupNotification
=
createGroupNotificationForNougatAndAbove
(
context
,
lastPushMessage
)
notification
?.
let
{
manager
.
notify
(
notId
,
notification
)
}
groupNotification
?.
let
{
manager
.
notify
(
groupTuple
.
first
,
groupNotification
)
}
}
else
{
val
notification
=
createSingleNotification
(
context
,
lastPushMessage
)
val
pushMessageList
=
hostToPushMessageList
.
get
(
host
)
notification
?.
let
{
NotificationManagerCompat
.
from
(
context
).
notify
(
notId
,
notification
)
}
pushMessageList
?.
let
{
if
(
pushMessageList
.
size
>
1
)
{
val
groupNotification
=
createGroupNotification
(
context
,
lastPushMessage
)
groupNotification
?.
let
{
NotificationManagerCompat
.
from
(
context
).
notify
(
groupTuple
.
first
,
groupNotification
)
}
}
}
}
}
private
fun
createGroupNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
{
internal
fun
createGroupNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
with
(
lastPushMessage
)
{
if
(
host
==
null
||
message
==
null
||
title
==
null
)
{
return
null
}
val
id
=
lastPushMessage
.
notificationId
.
toInt
()
val
contentIntent
=
getContentIntent
(
context
,
id
,
lastPushMessage
)
val
deleteIntent
=
getDismissIntent
(
context
,
lastPushMessage
)
...
...
@@ -176,18 +198,18 @@ object PushManager {
builder
.
setSubText
(
subText
)
}
if
(
style
==
"inbox"
)
{
if
(
style
==
null
||
style
==
"inbox"
)
{
val
pushMessageList
=
hostToPushMessageList
.
get
(
host
)
pushMessageList
?.
let
{
val
messageCount
=
pushMessageList
.
size
val
summary
=
summaryText
.
replace
(
"%n%"
,
messageCount
.
toString
())
.
fromHtml
()
val
summary
=
summaryText
?
.
replace
(
"%n%"
,
messageCount
.
toString
())
?.
fromHtml
()
?:
"$messageCount new messages"
builder
.
setNumber
(
messageCount
)
if
(
messageCount
>
1
)
{
val
firstPush
=
pushMessageList
[
0
]
val
singleConversation
=
pushMessageList
.
filter
{
firstPush
.
sender
.
username
!=
it
.
sender
.
username
firstPush
.
sender
?.
username
!=
it
.
sender
?
.
username
}.
isEmpty
()
val
inbox
=
NotificationCompat
.
InboxStyle
()
...
...
@@ -203,9 +225,13 @@ object PushManager {
builder
.
setStyle
(
inbox
)
}
else
{
val
firstMsg
=
pushMessageList
[
0
]
if
(
firstMsg
.
host
==
null
||
firstMsg
.
message
==
null
||
firstMsg
.
title
==
null
)
{
return
null
}
val
bigText
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
pushMessageList
[
0
]
.
message
.
fromHtml
())
.
setBigContentTitle
(
pushMessageList
[
0
]
.
title
.
fromHtml
())
.
bigText
(
firstMsg
.
message
.
fromHtml
())
.
setBigContentTitle
(
firstMsg
.
title
.
fromHtml
())
builder
.
setStyle
(
bigText
)
}
...
...
@@ -223,8 +249,11 @@ object PushManager {
}
@RequiresApi
(
Build
.
VERSION_CODES
.
N
)
private
fun
createGroupNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
{
internal
fun
createGroupNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
with
(
lastPushMessage
)
{
if
(
host
==
null
||
message
==
null
||
title
==
null
)
{
return
null
}
val
manager
:
NotificationManager
=
context
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
as
NotificationManager
val
id
=
notificationId
.
toInt
()
...
...
@@ -256,7 +285,7 @@ object PushManager {
builder
.
setSubText
(
subText
)
}
if
(
style
==
"inbox"
)
{
if
(
style
==
null
||
style
==
"inbox"
)
{
val
pushMessageList
=
hostToPushMessageList
.
get
(
host
)
pushMessageList
?.
let
{
...
...
@@ -287,8 +316,11 @@ object PushManager {
}
}
private
fun
createSingleNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
{
internal
fun
createSingleNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
with
(
lastPushMessage
)
{
if
(
host
==
null
||
message
==
null
||
title
==
null
)
{
return
null
}
val
id
=
notificationId
.
toInt
()
val
contentIntent
=
getContentIntent
(
context
,
id
,
lastPushMessage
)
val
deleteIntent
=
getDismissIntent
(
context
,
lastPushMessage
)
...
...
@@ -303,7 +335,7 @@ object PushManager {
.
setContentIntent
(
contentIntent
)
.
setMessageNotification
()
val
subText
=
RocketChatCache
(
context
).
getHostSiteName
(
lastPushMessage
.
host
)
val
subText
=
RocketChatCache
(
context
).
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
...
...
@@ -311,12 +343,16 @@ object PushManager {
val
pushMessageList
=
hostToPushMessageList
.
get
(
host
)
pushMessageList
?.
let
{
val
lastPushMsg
=
pushMessageList
.
last
()
if
(
lastPushMsg
.
host
==
null
||
lastPushMsg
.
message
==
null
||
lastPushMsg
.
title
==
null
)
{
return
null
}
if
(
pushMessageList
.
isNotEmpty
())
{
val
messageCount
=
pushMessageList
.
size
val
bigText
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
pushMessageList
.
last
()
.
message
.
fromHtml
())
.
setBigContentTitle
(
pushMessageList
.
last
()
.
title
.
fromHtml
())
.
bigText
(
lastPushMsg
.
message
.
fromHtml
())
.
setBigContentTitle
(
lastPushMsg
.
title
.
fromHtml
())
builder
.
setStyle
(
bigText
).
setNumber
(
messageCount
)
}
}
...
...
@@ -326,11 +362,14 @@ object PushManager {
}
@RequiresApi
(
Build
.
VERSION_CODES
.
N
)
private
fun
createSingleNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
{
internal
fun
createSingleNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
val
manager
:
NotificationManager
=
context
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
)
as
NotificationManager
with
(
lastPushMessage
)
{
if
(
host
==
null
||
message
==
null
||
title
==
null
)
{
return
null
}
val
id
=
notificationId
.
toInt
()
val
contentIntent
=
getContentIntent
(
context
,
id
,
lastPushMessage
)
val
deleteIntent
=
getDismissIntent
(
context
,
lastPushMessage
)
...
...
@@ -356,12 +395,12 @@ object PushManager {
manager
.
createNotificationChannel
(
channel
)
}
val
subText
=
RocketChatCache
(
context
).
getHostSiteName
(
lastPushMessage
.
host
)
val
subText
=
RocketChatCache
(
context
).
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
if
(
"inbox"
==
style
)
{
if
(
style
==
null
||
"inbox"
==
style
)
{
val
pushMessageList
=
hostToPushMessageList
.
get
(
host
)
pushMessageList
?.
let
{
...
...
@@ -470,12 +509,11 @@ object PushManager {
replyIntent
.
putExtra
(
EXTRA_PUSH_MESSAGE
,
pushMessage
as
Serializable
)
val
pendingIntent
=
PendingIntent
.
getBroadcast
(
context
,
randomizer
.
nextInt
(),
replyIntent
,
0
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_reply
,
REPLY_LABEL
,
pendingIntent
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_reply
,
REPLY_LABEL
,
pendingIntent
)
.
addRemoteInput
(
replyRemoteInput
)
.
setAllowGeneratedReplies
(
true
)
.
build
()
this
.
addAction
(
replyAction
)
return
this
}
...
...
@@ -488,49 +526,54 @@ object PushManager {
with
(
this
,
{
setAutoCancel
(
true
)
setShowWhen
(
true
)
setColor
(
ctx
.
resources
.
getColor
(
R
.
color
.
colorRed400
)
)
color
=
ctx
.
resources
.
getColor
(
R
.
color
.
colorRed400
)
setDefaults
(
Notification
.
DEFAULT_ALL
)
setSmallIcon
(
smallIcon
)
})
return
this
}
private
data class
PushMessage
(
val
title
:
String
,
val
message
:
String
,
val
image
:
String
?,
val
ejson
:
String
,
val
count
:
String
,
internal
data class
PushMessage
(
val
title
:
String
?
=
null
,
val
message
:
String
?
=
null
,
val
image
:
String
?
=
null
,
val
ejson
:
String
?
=
null
,
val
count
:
String
?
=
null
,
val
notificationId
:
String
,
val
summaryText
:
String
,
val
style
:
String
)
:
Serializable
{
val
host
:
String
val
rid
:
String
val
type
:
String
val
n
ame
:
String
?
val
sender
:
Sender
val
summaryText
:
String
?
=
null
,
val
style
:
String
?
=
null
)
:
Serializable
{
val
host
:
String
?
val
rid
:
String
?
val
type
:
String
?
val
channelN
ame
:
String
?
val
sender
:
Sender
?
val
createdAt
:
Long
init
{
val
json
=
JSONObject
(
ejson
)
host
=
json
.
getString
(
"host"
)
rid
=
json
.
getString
(
"rid"
)
type
=
json
.
getString
(
"type"
)
name
=
json
.
optString
(
"name"
)
sender
=
Sender
(
json
.
getString
(
"sender"
))
val
json
=
if
(
ejson
==
null
)
JSONObject
()
else
JSONObject
(
ejson
)
host
=
json
.
optString
(
"host"
,
null
)
rid
=
json
.
optString
(
"rid"
,
null
)
type
=
json
.
optString
(
"type"
,
null
)
channelName
=
json
.
optString
(
"name"
,
null
)
val
senderJson
=
json
.
optString
(
"sender"
,
null
)
if
(
senderJson
!=
null
&&
senderJson
!=
"null"
)
{
sender
=
Sender
(
senderJson
)
}
else
{
sender
=
null
}
createdAt
=
System
.
currentTimeMillis
()
}
data class
Sender
(
val
sender
:
String
)
:
Serializable
{
val
_id
:
String
val
username
:
String
val
name
:
String
val
_id
:
String
?
val
username
:
String
?
val
name
:
String
?
init
{
val
json
=
JSONObject
(
sender
)
_id
=
json
.
optString
(
"_id"
)
username
=
json
.
getString
(
"username"
)
name
=
json
.
optString
(
"name"
)
_id
=
json
.
optString
(
"_id"
,
null
)
username
=
json
.
optString
(
"username"
,
null
)
name
=
json
.
optString
(
"name"
,
null
)
}
}
}
...
...
@@ -565,13 +608,16 @@ object PushManager {
val
message
:
CharSequence
?
=
extractMessage
(
intent
)
val
pushMessage
=
intent
?.
extras
?.
getSerializable
(
EXTRA_PUSH_MESSAGE
)
as
PushMessage
?
pushMessage
?.
let
{
val
userNotId
=
pushMessage
.
notificationId
.
toInt
()
if
(
pushMessage
?.
host
==
null
)
{
return
}
pushMessage
.
let
{
val
groupTuple
=
groupMap
.
get
(
pushMessage
.
host
)
val
pushes
=
hostToPushMessageList
.
get
(
pushMessage
.
host
)
pushes
?.
let
{
val
allMessagesFromSameUser
=
pushes
.
filter
{
it
.
sender
.
_id
==
pushMessage
.
sender
.
_id
it
.
sender
?.
_id
==
pushMessage
.
sender
?
.
_id
}
for
(
msg
in
allMessagesFromSameUser
)
{
manager
.
cancel
(
msg
.
notificationId
.
toInt
())
...
...
@@ -586,9 +632,15 @@ object PushManager {
}
}
message
?.
let
{
if
(
pushMessage
.
rid
==
null
)
{
return
}
val
httpUrl
=
HttpUrl
.
parse
(
pushMessage
.
host
)
httpUrl
?.
let
{
sendMessage
(
RocketChatCache
(
context
).
getSiteUrlFor
(
httpUrl
.
host
()),
message
,
pushMessage
.
rid
)
val
siteUrl
=
RocketChatCache
(
context
).
getSiteUrlFor
(
httpUrl
.
host
())
if
(
siteUrl
!=
null
)
{
sendMessage
(
siteUrl
,
message
,
pushMessage
.
rid
)
}
}
}
}
...
...
@@ -629,7 +681,7 @@ object PushManager {
roomUserTuple
.
flatMap
{
tuple
->
messageInteractor
.
send
(
tuple
.
first
,
tuple
.
second
,
message
as
String
)
}
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
({
success
->
.
subscribe
({
_
->
// Empty
},
{
throwable
->
throwable
.
printStackTrace
()
...
...
app/src/main/java/chat/rocket/android/push/PushNotificationHandler.java
deleted
100644 → 0
View file @
839f60b0
package
chat
.
rocket
.
android
.
push
;
import
android.app.Notification
;
import
android.app.NotificationChannel
;
import
android.app.NotificationManager
;
import
android.app.PendingIntent
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
import
android.content.res.AssetManager
;
import
android.content.res.Resources
;
import
android.graphics.Bitmap
;
import
android.graphics.BitmapFactory
;
import
android.graphics.Color
;
import
android.net.Uri
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.support.annotation.RequiresApi
;
import
android.support.v4.app.NotificationCompat
;
import
android.support.v4.app.RemoteInput
;
import
android.support.v4.util.SparseArrayCompat
;
import
android.text.Html
;
import
android.text.Spanned
;
import
android.util.Log
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.net.HttpURLConnection
;
import
java.net.URL
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Random
;
import
chat.rocket.android.activity.MainActivity
;
import
chat.rocket.android.helper.ServerPolicyHelper
;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android.widget.helper.AvatarHelper
;
import
chat.rocket.core.models.ServerInfo
;
public
class
PushNotificationHandler
implements
PushConstants
{
private
static
final
String
LOG_TAG
=
"PushNotificationHandler"
;
private
static
SparseArrayCompat
<
ArrayList
<
String
>>
messageMap
=
new
SparseArrayCompat
<>();
private
Random
random
=
new
Random
();
public
static
synchronized
void
cleanUpNotificationStack
(
int
notId
)
{
messageMap
.
remove
(
notId
);
}
private
synchronized
void
setNotification
(
int
notId
,
String
message
)
{
ArrayList
<
String
>
messageList
=
messageMap
.
get
(
notId
);
if
(
messageList
==
null
)
{
messageList
=
new
ArrayList
<>();
messageMap
.
put
(
notId
,
messageList
);
}
if
(
message
.
isEmpty
())
{
messageList
.
clear
();
}
else
{
messageList
.
add
(
message
);
}
}
private
synchronized
ArrayList
<
String
>
getMessageList
(
int
notId
)
{
return
messageMap
.
get
(
notId
);
}
public
void
showNotificationIfPossible
(
Context
context
,
Bundle
extras
)
{
// Send a notification if there is a message or title, otherwise just send data
String
message
=
extras
.
getString
(
MESSAGE
);
String
title
=
extras
.
getString
(
TITLE
);
String
contentAvailable
=
extras
.
getString
(
CONTENT_AVAILABLE
);
String
forceStart
=
extras
.
getString
(
FORCE_START
);
Log
.
d
(
LOG_TAG
,
"message =["
+
message
+
"]"
);
Log
.
d
(
LOG_TAG
,
"title =["
+
title
+
"]"
);
Log
.
d
(
LOG_TAG
,
"contentAvailable =["
+
contentAvailable
+
"]"
);
Log
.
d
(
LOG_TAG
,
"forceStart =["
+
forceStart
+
"]"
);
if
((
message
!=
null
&&
message
.
length
()
!=
0
)
||
(
title
!=
null
&&
title
.
length
()
!=
0
))
{
Log
.
d
(
LOG_TAG
,
"build notification"
);
if
(
title
==
null
||
title
.
isEmpty
())
{
extras
.
putString
(
TITLE
,
getAppName
(
context
));
}
createNotification
(
context
,
extras
);
}
}
public
void
createNotification
(
Context
context
,
Bundle
extras
)
{
NotificationManager
notificationManager
=
(
NotificationManager
)
context
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
);
String
appName
=
getAppName
(
context
);
String
packageName
=
context
.
getPackageName
();
Resources
resources
=
context
.
getResources
();
String
hostname
=
getHostname
(
extras
);
String
roomId
=
getRoomId
(
extras
);
int
notId
=
parseInt
(
NOT_ID
,
extras
);
Intent
notificationIntent
=
new
Intent
(
context
,
MainActivity
.
class
);
notificationIntent
.
addFlags
(
Intent
.
FLAG_ACTIVITY_SINGLE_TOP
|
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
);
notificationIntent
.
putExtra
(
PUSH_BUNDLE
,
extras
);
notificationIntent
.
putExtra
(
NOT_ID
,
notId
);
if
(
hostname
!=
null
&&
roomId
!=
null
&&
isValidHostname
(
context
,
hostname
))
{
notificationIntent
.
putExtra
(
HOSTNAME
,
hostname
);
notificationIntent
.
putExtra
(
ROOM_ID
,
roomId
);
}
int
requestCode
=
random
.
nextInt
();
PendingIntent
contentIntent
=
PendingIntent
.
getActivity
(
context
,
requestCode
,
notificationIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
);
SharedPreferences
prefs
=
context
.
getSharedPreferences
(
PushConstants
.
COM_ADOBE_PHONEGAP_PUSH
,
Context
.
MODE_PRIVATE
);
String
localIcon
=
prefs
.
getString
(
ICON
,
null
);
String
localIconColor
=
prefs
.
getString
(
ICON_COLOR
,
null
);
boolean
soundOption
=
prefs
.
getBoolean
(
SOUND
,
true
);
boolean
vibrateOption
=
prefs
.
getBoolean
(
VIBRATE
,
true
);
Log
.
d
(
LOG_TAG
,
"stored icon="
+
localIcon
);
Log
.
d
(
LOG_TAG
,
"stored iconColor="
+
localIconColor
);
Log
.
d
(
LOG_TAG
,
"stored sound="
+
soundOption
);
Log
.
d
(
LOG_TAG
,
"stored vibrate="
+
vibrateOption
);
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
{
String
channelId
=
"rocket-chat-channel"
;
CharSequence
name
=
"RocketChatMessage"
;
int
importance
=
NotificationManager
.
IMPORTANCE_HIGH
;
NotificationChannel
channel
=
new
NotificationChannel
(
channelId
,
name
,
importance
);
channel
.
enableLights
(
true
);
notificationManager
.
createNotificationChannel
(
channel
);
Notification
.
Builder
notificationBuilder
=
new
Notification
.
Builder
(
context
,
channelId
)
.
setWhen
(
System
.
currentTimeMillis
())
.
setContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setTicker
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setContentIntent
(
contentIntent
)
.
setChannelId
(
channelId
)
.
setAutoCancel
(
true
);
setNotificationImportance
(
extras
,
channel
);
setNotificationVibration
(
extras
,
vibrateOption
,
channel
);
setNotificationMessage
(
notId
,
extras
,
notificationBuilder
);
setNotificationCount
(
context
,
extras
,
notificationBuilder
);
setNotificationSmallIcon
(
extras
,
packageName
,
resources
,
notificationBuilder
,
localIcon
);
setNotificationLargeIcon
(
context
,
extras
,
packageName
,
resources
,
notificationBuilder
);
setNotificationLedColor
(
extras
,
channel
);
if
(
soundOption
)
{
setNotificationSound
(
context
,
extras
,
channel
);
}
createActions
(
context
,
extras
,
notificationBuilder
,
resources
,
packageName
,
notId
);
notificationManager
.
notify
(
notId
,
notificationBuilder
.
build
());
}
else
{
NotificationCompat
.
Builder
notificationBuilder
=
new
NotificationCompat
.
Builder
(
context
)
.
setWhen
(
System
.
currentTimeMillis
())
.
setContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setTicker
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setContentIntent
(
contentIntent
)
.
setAutoCancel
(
true
);
setNotificationCount
(
context
,
extras
,
notificationBuilder
);
setNotificationVibration
(
extras
,
vibrateOption
,
notificationBuilder
);
setNotificationIconColor
(
extras
.
getString
(
"color"
),
notificationBuilder
,
localIconColor
);
setNotificationSmallIcon
(
extras
,
packageName
,
resources
,
notificationBuilder
,
localIcon
);
setNotificationLargeIcon
(
context
,
extras
,
packageName
,
resources
,
notificationBuilder
);
if
(
soundOption
)
{
setNotificationSound
(
context
,
extras
,
notificationBuilder
);
}
setNotificationLedColor
(
extras
,
notificationBuilder
);
setNotificationPriority
(
extras
,
notificationBuilder
);
setNotificationMessage
(
notId
,
extras
,
notificationBuilder
);
setVisibility
(
context
,
extras
,
notificationBuilder
);
createActions
(
context
,
extras
,
notificationBuilder
,
resources
,
packageName
,
notId
);
notificationManager
.
notify
(
appName
,
notId
,
notificationBuilder
.
build
());
}
}
private
void
createActions
(
Context
context
,
Bundle
extras
,
NotificationCompat
.
Builder
builder
,
Resources
resources
,
String
packageName
,
int
notId
)
{
Log
.
d
(
LOG_TAG
,
"build actions: with in-line"
);
String
actions
=
extras
.
getString
(
ACTIONS
);
if
(
actions
==
null
)
{
return
;
}
try
{
JSONArray
actionsArray
=
new
JSONArray
(
actions
);
ArrayList
<
NotificationCompat
.
Action
>
wActions
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
actionsArray
.
length
();
i
++)
{
int
min
=
1
;
int
max
=
2000000000
;
Random
random
=
new
Random
();
int
uniquePendingIntentRequestCode
=
random
.
nextInt
((
max
-
min
)
+
1
)
+
min
;
Log
.
d
(
LOG_TAG
,
"adding action"
);
JSONObject
action
=
actionsArray
.
getJSONObject
(
i
);
Log
.
d
(
LOG_TAG
,
"adding callback = "
+
action
.
getString
(
CALLBACK
));
boolean
foreground
=
action
.
optBoolean
(
FOREGROUND
,
true
);
boolean
inline
=
action
.
optBoolean
(
"inline"
,
false
);
Intent
intent
;
PendingIntent
pIntent
;
if
(
inline
)
{
Log
.
d
(
LOG_TAG
,
"Version: "
+
android
.
os
.
Build
.
VERSION
.
SDK_INT
+
" = "
+
android
.
os
.
Build
.
VERSION_CODES
.
M
);
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
<=
android
.
os
.
Build
.
VERSION_CODES
.
M
)
{
Log
.
d
(
LOG_TAG
,
"push activity"
);
intent
=
new
Intent
(
context
,
MainActivity
.
class
);
}
else
{
Log
.
d
(
LOG_TAG
,
"push receiver"
);
intent
=
new
Intent
(
context
,
BackgroundActionButtonHandler
.
class
);
}
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
<=
android
.
os
.
Build
.
VERSION_CODES
.
M
)
{
Log
.
d
(
LOG_TAG
,
"push activity for notId "
+
notId
);
pIntent
=
PendingIntent
.
getActivity
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_ONE_SHOT
);
}
else
{
Log
.
d
(
LOG_TAG
,
"push receiver for notId "
+
notId
);
pIntent
=
PendingIntent
.
getBroadcast
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_ONE_SHOT
);
}
}
else
if
(
foreground
)
{
intent
=
new
Intent
(
context
,
MainActivity
.
class
);
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
pIntent
=
PendingIntent
.
getActivity
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
);
}
else
{
intent
=
new
Intent
(
context
,
BackgroundActionButtonHandler
.
class
);
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
pIntent
=
PendingIntent
.
getBroadcast
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
);
}
NotificationCompat
.
Action
.
Builder
actionBuilder
=
new
NotificationCompat
.
Action
.
Builder
(
resources
.
getIdentifier
(
action
.
optString
(
ICON
,
""
),
DRAWABLE
,
packageName
),
action
.
getString
(
TITLE
),
pIntent
);
RemoteInput
remoteInput
;
if
(
inline
)
{
Log
.
d
(
LOG_TAG
,
"build remote input"
);
String
replyLabel
=
"Enter your reply here"
;
remoteInput
=
new
RemoteInput
.
Builder
(
INLINE_REPLY
)
.
setLabel
(
replyLabel
)
.
build
();
actionBuilder
.
addRemoteInput
(
remoteInput
);
}
NotificationCompat
.
Action
wAction
=
actionBuilder
.
build
();
wActions
.
add
(
actionBuilder
.
build
());
if
(
inline
)
{
builder
.
addAction
(
wAction
);
}
else
{
builder
.
addAction
(
resources
.
getIdentifier
(
action
.
optString
(
ICON
,
""
),
DRAWABLE
,
packageName
),
action
.
getString
(
TITLE
),
pIntent
);
}
wAction
=
null
;
pIntent
=
null
;
}
builder
.
extend
(
new
NotificationCompat
.
WearableExtender
().
addActions
(
wActions
));
wActions
.
clear
();
}
catch
(
JSONException
e
)
{
// nope
}
}
@RequiresApi
(
api
=
Build
.
VERSION_CODES
.
KITKAT_WATCH
)
private
void
createActions
(
Context
context
,
Bundle
extras
,
Notification
.
Builder
builder
,
Resources
resources
,
String
packageName
,
int
notId
)
{
Log
.
d
(
LOG_TAG
,
"build actions: with in-line"
);
String
actions
=
extras
.
getString
(
ACTIONS
);
if
(
actions
==
null
)
{
return
;
}
try
{
JSONArray
actionsArray
=
new
JSONArray
(
actions
);
ArrayList
<
Notification
.
Action
>
wActions
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
actionsArray
.
length
();
i
++)
{
int
min
=
1
;
int
max
=
2000000000
;
Random
random
=
new
Random
();
int
uniquePendingIntentRequestCode
=
random
.
nextInt
((
max
-
min
)
+
1
)
+
min
;
Log
.
d
(
LOG_TAG
,
"adding action"
);
JSONObject
action
=
actionsArray
.
getJSONObject
(
i
);
Log
.
d
(
LOG_TAG
,
"adding callback = "
+
action
.
getString
(
CALLBACK
));
boolean
foreground
=
action
.
optBoolean
(
FOREGROUND
,
true
);
boolean
inline
=
action
.
optBoolean
(
"inline"
,
false
);
Intent
intent
;
PendingIntent
pIntent
;
if
(
inline
)
{
Log
.
d
(
LOG_TAG
,
"Version: "
+
android
.
os
.
Build
.
VERSION
.
SDK_INT
+
" = "
+
android
.
os
.
Build
.
VERSION_CODES
.
M
);
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
<=
android
.
os
.
Build
.
VERSION_CODES
.
M
)
{
Log
.
d
(
LOG_TAG
,
"push activity"
);
intent
=
new
Intent
(
context
,
MainActivity
.
class
);
}
else
{
Log
.
d
(
LOG_TAG
,
"push receiver"
);
intent
=
new
Intent
(
context
,
BackgroundActionButtonHandler
.
class
);
}
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
if
(
android
.
os
.
Build
.
VERSION
.
SDK_INT
<=
android
.
os
.
Build
.
VERSION_CODES
.
M
)
{
Log
.
d
(
LOG_TAG
,
"push activity for notId "
+
notId
);
pIntent
=
PendingIntent
.
getActivity
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_ONE_SHOT
);
}
else
{
Log
.
d
(
LOG_TAG
,
"push receiver for notId "
+
notId
);
pIntent
=
PendingIntent
.
getBroadcast
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_ONE_SHOT
);
}
}
else
if
(
foreground
)
{
intent
=
new
Intent
(
context
,
MainActivity
.
class
);
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
pIntent
=
PendingIntent
.
getActivity
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
);
}
else
{
intent
=
new
Intent
(
context
,
BackgroundActionButtonHandler
.
class
);
updateIntent
(
intent
,
action
.
getString
(
CALLBACK
),
extras
,
foreground
,
notId
);
pIntent
=
PendingIntent
.
getBroadcast
(
context
,
uniquePendingIntentRequestCode
,
intent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
);
}
Notification
.
Action
.
Builder
actionBuilder
=
new
Notification
.
Action
.
Builder
(
resources
.
getIdentifier
(
action
.
optString
(
ICON
,
""
),
DRAWABLE
,
packageName
),
action
.
getString
(
TITLE
),
pIntent
);
android
.
app
.
RemoteInput
remoteInput
;
if
(
inline
)
{
Log
.
d
(
LOG_TAG
,
"build remote input"
);
String
replyLabel
=
"Enter your reply here"
;
remoteInput
=
new
android
.
app
.
RemoteInput
.
Builder
(
INLINE_REPLY
)
.
setLabel
(
replyLabel
)
.
build
();
actionBuilder
.
addRemoteInput
(
remoteInput
);
}
Notification
.
Action
wAction
=
actionBuilder
.
build
();
wActions
.
add
(
actionBuilder
.
build
());
if
(
inline
)
{
builder
.
addAction
(
wAction
);
}
else
{
builder
.
addAction
(
resources
.
getIdentifier
(
action
.
optString
(
ICON
,
""
),
DRAWABLE
,
packageName
),
action
.
getString
(
TITLE
),
pIntent
);
}
wAction
=
null
;
pIntent
=
null
;
}
builder
.
extend
(
new
Notification
.
WearableExtender
().
addActions
(
wActions
));
wActions
.
clear
();
}
catch
(
JSONException
e
)
{
// nope
}
}
private
void
setNotificationCount
(
Context
context
,
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
int
count
=
extractBadgeCount
(
extras
);
if
(
count
>=
0
)
{
Log
.
d
(
LOG_TAG
,
"count =["
+
count
+
"]"
);
builder
.
setNumber
(
count
);
}
}
private
void
setNotificationCount
(
Context
context
,
Bundle
extras
,
Notification
.
Builder
builder
)
{
int
count
=
extractBadgeCount
(
extras
);
if
(
count
>=
0
)
{
Log
.
d
(
LOG_TAG
,
"count =["
+
count
+
"]"
);
builder
.
setNumber
(
count
);
}
}
private
void
setVisibility
(
Context
context
,
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
String
visibilityStr
=
extras
.
getString
(
VISIBILITY
);
if
(
visibilityStr
==
null
)
{
return
;
}
try
{
Integer
visibility
=
Integer
.
parseInt
(
visibilityStr
);
if
(
visibility
>=
NotificationCompat
.
VISIBILITY_SECRET
&&
visibility
<=
NotificationCompat
.
VISIBILITY_PUBLIC
)
{
builder
.
setVisibility
(
visibility
);
}
else
{
Log
.
e
(
LOG_TAG
,
"Visibility parameter must be between -1 and 1"
);
}
}
catch
(
NumberFormatException
e
)
{
e
.
printStackTrace
();
}
}
private
void
setNotificationVibration
(
Bundle
extras
,
Boolean
vibrateOption
,
NotificationCompat
.
Builder
builder
)
{
String
vibrationPattern
=
extras
.
getString
(
VIBRATION_PATTERN
);
if
(
vibrationPattern
!=
null
)
{
String
[]
items
=
vibrationPattern
.
replaceAll
(
"\\["
,
""
).
replaceAll
(
"\\]"
,
""
).
split
(
","
);
long
[]
results
=
new
long
[
items
.
length
];
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
try
{
results
[
i
]
=
Long
.
parseLong
(
items
[
i
].
trim
());
}
catch
(
NumberFormatException
nfe
)
{
}
}
builder
.
setVibrate
(
results
);
}
else
{
if
(
vibrateOption
)
{
builder
.
setDefaults
(
Notification
.
DEFAULT_VIBRATE
);
}
}
}
@RequiresApi
(
api
=
26
)
private
void
setNotificationVibration
(
Bundle
extras
,
Boolean
vibrateOption
,
NotificationChannel
channel
)
{
String
vibrationPattern
=
extras
.
getString
(
VIBRATION_PATTERN
);
if
(
vibrationPattern
!=
null
)
{
String
[]
items
=
vibrationPattern
.
replaceAll
(
"\\["
,
""
).
replaceAll
(
"\\]"
,
""
).
split
(
","
);
long
[]
results
=
new
long
[
items
.
length
];
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
try
{
results
[
i
]
=
Long
.
parseLong
(
items
[
i
].
trim
());
}
catch
(
NumberFormatException
nfe
)
{
}
}
channel
.
setVibrationPattern
(
results
);
}
else
{
if
(
vibrateOption
)
{
channel
.
enableVibration
(
true
);
}
}
}
private
void
setNotificationMessage
(
int
notId
,
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
String
message
=
extras
.
getString
(
MESSAGE
);
String
style
=
extras
.
getString
(
STYLE
,
STYLE_TEXT
);
if
(
STYLE_INBOX
.
equals
(
style
))
{
setNotification
(
notId
,
message
);
builder
.
setContentText
(
fromHtml
(
message
));
ArrayList
<
String
>
messageList
=
getMessageList
(
notId
);
Integer
sizeList
=
messageList
.
size
();
if
(
sizeList
>
1
)
{
String
sizeListMessage
=
sizeList
.
toString
();
String
stacking
=
sizeList
+
" more"
;
if
(
extras
.
getString
(
SUMMARY_TEXT
)
!=
null
)
{
stacking
=
extras
.
getString
(
SUMMARY_TEXT
);
stacking
=
stacking
.
replace
(
"%n%"
,
sizeListMessage
);
}
NotificationCompat
.
InboxStyle
notificationInbox
=
new
NotificationCompat
.
InboxStyle
()
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setSummaryText
(
fromHtml
(
stacking
));
for
(
int
i
=
messageList
.
size
()
-
1
;
i
>=
0
;
i
--)
{
notificationInbox
.
addLine
(
fromHtml
(
messageList
.
get
(
i
)));
}
builder
.
setStyle
(
notificationInbox
);
}
else
{
NotificationCompat
.
BigTextStyle
bigText
=
new
NotificationCompat
.
BigTextStyle
();
if
(
message
!=
null
)
{
bigText
.
bigText
(
fromHtml
(
message
));
bigText
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
builder
.
setStyle
(
bigText
);
}
}
}
else
if
(
STYLE_PICTURE
.
equals
(
style
))
{
setNotification
(
notId
,
""
);
NotificationCompat
.
BigPictureStyle
bigPicture
=
new
NotificationCompat
.
BigPictureStyle
();
bigPicture
.
bigPicture
(
getBitmapFromURL
(
extras
.
getString
(
PICTURE
),
null
,
null
));
bigPicture
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
bigPicture
.
setSummaryText
(
fromHtml
(
extras
.
getString
(
SUMMARY_TEXT
)));
builder
.
setContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
builder
.
setContentText
(
fromHtml
(
message
));
builder
.
setStyle
(
bigPicture
);
}
else
{
setNotification
(
notId
,
""
);
NotificationCompat
.
BigTextStyle
bigText
=
new
NotificationCompat
.
BigTextStyle
();
if
(
message
!=
null
)
{
builder
.
setContentText
(
fromHtml
(
message
));
bigText
.
bigText
(
fromHtml
(
message
));
bigText
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
String
summaryText
=
extras
.
getString
(
SUMMARY_TEXT
);
if
(
summaryText
!=
null
)
{
bigText
.
setSummaryText
(
fromHtml
(
summaryText
));
}
builder
.
setStyle
(
bigText
);
}
}
}
private
void
setNotificationMessage
(
int
notId
,
Bundle
extras
,
Notification
.
Builder
builder
)
{
String
message
=
extras
.
getString
(
MESSAGE
);
String
style
=
extras
.
getString
(
STYLE
,
STYLE_TEXT
);
if
(
STYLE_INBOX
.
equals
(
style
))
{
setNotification
(
notId
,
message
);
builder
.
setContentText
(
fromHtml
(
message
));
ArrayList
<
String
>
messageList
=
getMessageList
(
notId
);
Integer
sizeList
=
messageList
.
size
();
if
(
sizeList
>
1
)
{
String
sizeListMessage
=
sizeList
.
toString
();
String
stacking
=
sizeList
+
" more"
;
if
(
extras
.
getString
(
SUMMARY_TEXT
)
!=
null
)
{
stacking
=
extras
.
getString
(
SUMMARY_TEXT
);
stacking
=
stacking
.
replace
(
"%n%"
,
sizeListMessage
);
}
Notification
.
InboxStyle
notificationInbox
=
new
Notification
.
InboxStyle
()
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)))
.
setSummaryText
(
fromHtml
(
stacking
));
for
(
int
i
=
messageList
.
size
()
-
1
;
i
>=
0
;
i
--)
{
notificationInbox
.
addLine
(
fromHtml
(
messageList
.
get
(
i
)));
}
builder
.
setStyle
(
notificationInbox
);
}
else
{
Notification
.
BigTextStyle
bigText
=
new
Notification
.
BigTextStyle
();
if
(
message
!=
null
)
{
bigText
.
bigText
(
fromHtml
(
message
));
bigText
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
builder
.
setStyle
(
bigText
);
}
}
}
else
if
(
STYLE_PICTURE
.
equals
(
style
))
{
setNotification
(
notId
,
""
);
Notification
.
BigPictureStyle
bigPicture
=
new
Notification
.
BigPictureStyle
();
bigPicture
.
bigPicture
(
getBitmapFromURL
(
extras
.
getString
(
PICTURE
),
null
,
null
));
bigPicture
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
bigPicture
.
setSummaryText
(
fromHtml
(
extras
.
getString
(
SUMMARY_TEXT
)));
builder
.
setContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
builder
.
setContentText
(
fromHtml
(
message
));
builder
.
setStyle
(
bigPicture
);
}
else
{
setNotification
(
notId
,
""
);
Notification
.
BigTextStyle
bigText
=
new
Notification
.
BigTextStyle
();
if
(
message
!=
null
)
{
builder
.
setContentText
(
fromHtml
(
message
));
bigText
.
bigText
(
fromHtml
(
message
));
bigText
.
setBigContentTitle
(
fromHtml
(
extras
.
getString
(
TITLE
)));
String
summaryText
=
extras
.
getString
(
SUMMARY_TEXT
);
if
(
summaryText
!=
null
)
{
bigText
.
setSummaryText
(
fromHtml
(
summaryText
));
}
builder
.
setStyle
(
bigText
);
}
}
}
private
void
setNotificationSound
(
Context
context
,
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
String
soundname
=
extras
.
getString
(
SOUNDNAME
);
if
(
soundname
==
null
)
{
soundname
=
extras
.
getString
(
SOUND
);
}
if
(
SOUND_RINGTONE
.
equals
(
soundname
))
{
builder
.
setSound
(
android
.
provider
.
Settings
.
System
.
DEFAULT_RINGTONE_URI
);
}
else
if
(
soundname
!=
null
&&
!
soundname
.
contentEquals
(
SOUND_DEFAULT
))
{
Uri
sound
=
Uri
.
parse
(
ContentResolver
.
SCHEME_ANDROID_RESOURCE
+
"://"
+
context
.
getPackageName
()
+
"/raw/"
+
soundname
);
Log
.
d
(
LOG_TAG
,
sound
.
toString
());
builder
.
setSound
(
sound
);
}
else
{
builder
.
setSound
(
android
.
provider
.
Settings
.
System
.
DEFAULT_NOTIFICATION_URI
);
}
}
@RequiresApi
(
api
=
26
)
private
void
setNotificationSound
(
Context
context
,
Bundle
extras
,
NotificationChannel
channel
)
{
String
soundname
=
extras
.
getString
(
SOUNDNAME
);
if
(
soundname
==
null
)
{
soundname
=
extras
.
getString
(
SOUND
);
}
if
(
SOUND_RINGTONE
.
equals
(
soundname
))
{
channel
.
setSound
(
android
.
provider
.
Settings
.
System
.
DEFAULT_RINGTONE_URI
,
Notification
.
AUDIO_ATTRIBUTES_DEFAULT
);
}
else
if
(
soundname
!=
null
&&
!
soundname
.
contentEquals
(
SOUND_DEFAULT
))
{
Uri
sound
=
Uri
.
parse
(
ContentResolver
.
SCHEME_ANDROID_RESOURCE
+
"://"
+
context
.
getPackageName
()
+
"/raw/"
+
soundname
);
Log
.
d
(
LOG_TAG
,
sound
.
toString
());
channel
.
setSound
(
sound
,
Notification
.
AUDIO_ATTRIBUTES_DEFAULT
);
}
else
{
channel
.
setSound
(
android
.
provider
.
Settings
.
System
.
DEFAULT_NOTIFICATION_URI
,
Notification
.
AUDIO_ATTRIBUTES_DEFAULT
);
}
}
private
void
setNotificationLedColor
(
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
String
ledColor
=
extras
.
getString
(
LED_COLOR
);
if
(
ledColor
==
null
)
{
return
;
}
// Converts parse Int Array from ledColor
String
[]
items
=
ledColor
.
replaceAll
(
"\\["
,
""
).
replaceAll
(
"\\]"
,
""
).
split
(
","
);
int
[]
results
=
new
int
[
items
.
length
];
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
try
{
results
[
i
]
=
Integer
.
parseInt
(
items
[
i
].
trim
());
}
catch
(
NumberFormatException
nfe
)
{
}
}
if
(
results
.
length
==
4
)
{
builder
.
setLights
(
Color
.
argb
(
results
[
0
],
results
[
1
],
results
[
2
],
results
[
3
]),
500
,
500
);
}
else
{
Log
.
e
(
LOG_TAG
,
"ledColor parameter must be an array of length == 4 (ARGB)"
);
}
}
@RequiresApi
(
api
=
26
)
private
void
setNotificationLedColor
(
Bundle
extras
,
NotificationChannel
channel
)
{
String
ledColor
=
extras
.
getString
(
LED_COLOR
);
if
(
ledColor
==
null
)
{
return
;
}
// Converts parse Int Array from ledColor
String
[]
items
=
ledColor
.
replaceAll
(
"\\["
,
""
).
replaceAll
(
"\\]"
,
""
).
split
(
","
);
int
[]
results
=
new
int
[
items
.
length
];
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
try
{
results
[
i
]
=
Integer
.
parseInt
(
items
[
i
].
trim
());
}
catch
(
NumberFormatException
nfe
)
{
}
}
if
(
results
.
length
==
4
)
{
channel
.
setLightColor
(
Color
.
argb
(
results
[
0
],
results
[
1
],
results
[
2
],
results
[
3
]));
}
else
{
Log
.
e
(
LOG_TAG
,
"ledColor parameter must be an array of length == 4 (ARGB)"
);
}
}
private
void
setNotificationPriority
(
Bundle
extras
,
NotificationCompat
.
Builder
builder
)
{
String
priorityStr
=
extras
.
getString
(
PRIORITY
);
if
(
priorityStr
==
null
)
{
return
;
}
try
{
Integer
priority
=
Integer
.
parseInt
(
priorityStr
);
if
(
priority
>=
NotificationCompat
.
PRIORITY_MIN
&&
priority
<=
NotificationCompat
.
PRIORITY_MAX
)
{
builder
.
setPriority
(
priority
);
}
else
{
Log
.
e
(
LOG_TAG
,
"Priority parameter must be between -2 and 2"
);
}
}
catch
(
NumberFormatException
e
)
{
e
.
printStackTrace
();
}
}
@RequiresApi
(
api
=
26
)
private
void
setNotificationImportance
(
Bundle
extras
,
NotificationChannel
channel
)
{
String
priorityStr
=
extras
.
getString
(
PRIORITY
);
if
(
priorityStr
==
null
)
{
return
;
}
try
{
Integer
priority
=
Integer
.
parseInt
(
priorityStr
);
if
(
priority
>=
NotificationCompat
.
PRIORITY_MIN
&&
priority
<=
NotificationCompat
.
PRIORITY_MAX
)
{
channel
.
setImportance
(
priority
);
}
else
{
Log
.
e
(
LOG_TAG
,
"Priority parameter must be between -2 and 2"
);
}
}
catch
(
NumberFormatException
e
)
{
e
.
printStackTrace
();
}
}
private
void
setNotificationLargeIcon
(
Context
context
,
Bundle
extras
,
String
packageName
,
Resources
resources
,
NotificationCompat
.
Builder
builder
)
{
String
hostname
=
getHostname
(
extras
);
String
username
=
getSenderUsername
(
extras
);
String
gcmLargeIcon
;
if
(
username
!=
null
&&
!
username
.
isEmpty
())
{
gcmLargeIcon
=
"https://"
+
hostname
+
"/avatar/"
+
username
;
}
else
{
gcmLargeIcon
=
extras
.
getString
(
IMAGE
);
// from gcm
}
if
(
gcmLargeIcon
==
null
||
""
.
equals
(
gcmLargeIcon
))
{
return
;
}
if
(
gcmLargeIcon
.
startsWith
(
"http://"
)
||
gcmLargeIcon
.
startsWith
(
"https://"
))
{
builder
.
setLargeIcon
(
getBitmapFromURL
(
gcmLargeIcon
,
username
,
context
));
Log
.
d
(
LOG_TAG
,
"using remote large-icon from gcm"
);
}
else
{
AssetManager
assetManager
=
context
.
getAssets
();
InputStream
istr
;
try
{
istr
=
assetManager
.
open
(
gcmLargeIcon
);
Bitmap
bitmap
=
BitmapFactory
.
decodeStream
(
istr
);
builder
.
setLargeIcon
(
bitmap
);
Log
.
d
(
LOG_TAG
,
"using assets large-icon from gcm"
);
}
catch
(
IOException
e
)
{
int
largeIconId
=
resources
.
getIdentifier
(
gcmLargeIcon
,
DRAWABLE
,
packageName
);
if
(
largeIconId
!=
0
)
{
Bitmap
largeIconBitmap
=
BitmapFactory
.
decodeResource
(
resources
,
largeIconId
);
builder
.
setLargeIcon
(
largeIconBitmap
);
Log
.
d
(
LOG_TAG
,
"using resources large-icon from gcm"
);
}
else
{
Log
.
d
(
LOG_TAG
,
"Not setting large icon"
);
}
}
}
}
private
void
setNotificationLargeIcon
(
Context
context
,
Bundle
extras
,
String
packageName
,
Resources
resources
,
Notification
.
Builder
builder
)
{
String
hostname
=
getHostname
(
extras
);
String
username
=
getSenderUsername
(
extras
);
String
gcmLargeIcon
;
if
(
username
!=
null
&&
!
username
.
isEmpty
())
{
gcmLargeIcon
=
"https://"
+
hostname
+
"/avatar/"
+
username
;
}
else
{
gcmLargeIcon
=
extras
.
getString
(
IMAGE
);
// from gcm
}
if
(
gcmLargeIcon
==
null
||
""
.
equals
(
gcmLargeIcon
))
{
return
;
}
if
(
gcmLargeIcon
.
startsWith
(
"http://"
)
||
gcmLargeIcon
.
startsWith
(
"https://"
))
{
builder
.
setLargeIcon
(
getBitmapFromURL
(
gcmLargeIcon
,
username
,
context
));
Log
.
d
(
LOG_TAG
,
"using remote large-icon from gcm"
);
}
else
{
AssetManager
assetManager
=
context
.
getAssets
();
InputStream
istr
;
try
{
istr
=
assetManager
.
open
(
gcmLargeIcon
);
Bitmap
bitmap
=
BitmapFactory
.
decodeStream
(
istr
);
builder
.
setLargeIcon
(
bitmap
);
Log
.
d
(
LOG_TAG
,
"using assets large-icon from gcm"
);
}
catch
(
IOException
e
)
{
int
largeIconId
=
resources
.
getIdentifier
(
gcmLargeIcon
,
DRAWABLE
,
packageName
);
if
(
largeIconId
!=
0
)
{
Bitmap
largeIconBitmap
=
BitmapFactory
.
decodeResource
(
resources
,
largeIconId
);
builder
.
setLargeIcon
(
largeIconBitmap
);
Log
.
d
(
LOG_TAG
,
"using resources large-icon from gcm"
);
}
else
{
Log
.
d
(
LOG_TAG
,
"Not setting large icon"
);
}
}
}
}
private
void
setNotificationSmallIcon
(
Bundle
extras
,
String
packageName
,
Resources
resources
,
NotificationCompat
.
Builder
builder
,
String
localIcon
)
{
int
iconId
=
0
;
String
icon
=
extras
.
getString
(
ICON
);
if
(
icon
!=
null
&&
!
""
.
equals
(
icon
))
{
iconId
=
resources
.
getIdentifier
(
icon
,
DRAWABLE
,
packageName
);
Log
.
d
(
LOG_TAG
,
"using icon from plugin options"
);
}
else
if
(
localIcon
!=
null
&&
!
""
.
equals
(
localIcon
))
{
iconId
=
resources
.
getIdentifier
(
localIcon
,
DRAWABLE
,
packageName
);
Log
.
d
(
LOG_TAG
,
"using icon from plugin options"
);
}
if
(
iconId
==
0
)
{
Log
.
d
(
LOG_TAG
,
"no icon resource found - using default icon"
);
iconId
=
resources
.
getIdentifier
(
"rocket_chat_notification"
,
DRAWABLE
,
packageName
);
}
builder
.
setSmallIcon
(
iconId
);
}
private
void
setNotificationSmallIcon
(
Bundle
extras
,
String
packageName
,
Resources
resources
,
Notification
.
Builder
builder
,
String
localIcon
)
{
int
iconId
=
0
;
String
icon
=
extras
.
getString
(
ICON
);
if
(
icon
!=
null
&&
!
""
.
equals
(
icon
))
{
iconId
=
resources
.
getIdentifier
(
icon
,
DRAWABLE
,
packageName
);
Log
.
d
(
LOG_TAG
,
"using icon from plugin options"
);
}
else
if
(
localIcon
!=
null
&&
!
""
.
equals
(
localIcon
))
{
iconId
=
resources
.
getIdentifier
(
localIcon
,
DRAWABLE
,
packageName
);
Log
.
d
(
LOG_TAG
,
"using icon from plugin options"
);
}
if
(
iconId
==
0
)
{
Log
.
d
(
LOG_TAG
,
"no icon resource found - using default icon"
);
iconId
=
resources
.
getIdentifier
(
"rocket_chat_notification"
,
DRAWABLE
,
packageName
);
}
builder
.
setSmallIcon
(
iconId
);
}
private
void
setNotificationIconColor
(
String
color
,
NotificationCompat
.
Builder
builder
,
String
localIconColor
)
{
int
iconColor
=
0
;
if
(
color
!=
null
&&
!
""
.
equals
(
color
))
{
try
{
iconColor
=
Color
.
parseColor
(
color
);
}
catch
(
IllegalArgumentException
e
)
{
Log
.
e
(
LOG_TAG
,
"couldn't parse color from android options"
);
}
}
else
if
(
localIconColor
!=
null
&&
!
""
.
equals
(
localIconColor
))
{
try
{
iconColor
=
Color
.
parseColor
(
localIconColor
);
}
catch
(
IllegalArgumentException
e
)
{
Log
.
e
(
LOG_TAG
,
"couldn't parse color from android options"
);
}
}
if
(
iconColor
!=
0
)
{
builder
.
setColor
(
iconColor
);
}
}
private
void
updateIntent
(
Intent
intent
,
String
callback
,
Bundle
extras
,
boolean
foreground
,
int
notId
)
{
intent
.
putExtra
(
CALLBACK
,
callback
);
intent
.
putExtra
(
PUSH_BUNDLE
,
extras
);
intent
.
putExtra
(
FOREGROUND
,
foreground
);
intent
.
putExtra
(
NOT_ID
,
notId
);
}
public
Bitmap
getBitmapFromURL
(
String
strURL
,
String
username
,
Context
context
)
{
try
{
URL
url
=
new
URL
(
strURL
);
HttpURLConnection
connection
=
(
HttpURLConnection
)
url
.
openConnection
();
connection
.
setDoInput
(
true
);
connection
.
connect
();
InputStream
input
=
connection
.
getInputStream
();
Bitmap
bitmap
=
BitmapFactory
.
decodeStream
(
input
);
if
(
bitmap
==
null
&&
username
!=
null
&&
context
!=
null
)
{
return
AvatarHelper
.
INSTANCE
.
getTextBitmap
(
username
,
context
);
}
return
bitmap
;
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
return
null
;
}
}
public
static
String
getAppName
(
Context
context
)
{
CharSequence
appName
=
context
.
getPackageManager
()
.
getApplicationLabel
(
context
.
getApplicationInfo
());
return
(
String
)
appName
;
}
private
int
parseInt
(
String
value
,
Bundle
extras
)
{
int
retval
=
0
;
try
{
retval
=
Integer
.
parseInt
(
extras
.
getString
(
value
));
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
LOG_TAG
,
"Number format exception - Error parsing "
+
value
+
": "
+
e
.
getMessage
());
}
catch
(
Exception
e
)
{
Log
.
e
(
LOG_TAG
,
"Number format exception - Error parsing "
+
value
+
": "
+
e
.
getMessage
());
}
return
retval
;
}
private
Spanned
fromHtml
(
String
source
)
{
if
(
source
!=
null
)
{
return
Html
.
fromHtml
(
source
);
}
else
{
return
null
;
}
}
private
int
extractBadgeCount
(
Bundle
extras
)
{
int
count
=
-
1
;
String
msgcnt
=
extras
.
getString
(
COUNT
);
try
{
if
(
msgcnt
!=
null
)
{
count
=
Integer
.
parseInt
(
msgcnt
);
}
}
catch
(
NumberFormatException
e
)
{
Log
.
e
(
LOG_TAG
,
e
.
getLocalizedMessage
(),
e
);
}
return
count
;
}
private
String
getHostname
(
Bundle
extras
)
{
try
{
JSONObject
jsonObject
=
new
JSONObject
(
extras
.
getString
(
"ejson"
,
"[]"
));
if
(!
jsonObject
.
has
(
"host"
))
{
return
null
;
}
return
ServerPolicyHelper
.
enforceHostname
(
jsonObject
.
getString
(
"host"
));
}
catch
(
Exception
e
)
{
return
null
;
}
}
private
String
getRoomId
(
Bundle
extras
)
{
try
{
JSONObject
jsonObject
=
new
JSONObject
(
extras
.
getString
(
"ejson"
,
"[]"
));
if
(!
jsonObject
.
has
(
"rid"
))
{
return
null
;
}
return
jsonObject
.
getString
(
"rid"
);
}
catch
(
Exception
e
)
{
return
null
;
}
}
private
String
getSenderUsername
(
Bundle
extras
)
{
try
{
JSONObject
jsonObject
=
new
JSONObject
(
extras
.
getString
(
"ejson"
,
"[]"
));
return
jsonObject
.
getJSONObject
(
"sender"
).
optString
(
"username"
);
}
catch
(
JSONException
e
)
{
return
null
;
}
}
private
boolean
isValidHostname
(
Context
context
,
String
hostname
)
{
final
List
<
ServerInfo
>
serverInfoList
=
ConnectivityManager
.
getInstance
(
context
.
getApplicationContext
()).
getServerList
();
for
(
ServerInfo
serverInfo
:
serverInfoList
)
{
if
(
serverInfo
.
getHostname
().
equals
(
hostname
))
{
return
true
;
}
}
return
false
;
}
}
app/src/test/kotlin/chat.rocket.android/RocketChatCacheTest.kt
deleted
100644 → 0
View file @
839f60b0
package
chat.rocket.android;
import
android.content.Context
import
chat.rocket.core.utils.Pair
import
org.hamcrest.CoreMatchers.equalTo
import
org.json.JSONObject
import
org.junit.Assert.assertThat
import
org.junit.Before
import
org.junit.Test
import
org.junit.runner.RunWith
import
org.mockito.Mockito.*
import
org.mockito.junit.MockitoJUnitRunner
@RunWith
(
MockitoJUnitRunner
::
class
)
class
RocketChatCacheTest
{
lateinit
var
cache
:
RocketChatCache
@Before
fun
setup
()
{
val
mockedContext
=
mock
(
Context
::
class
.
java
)
val
mockAppContext
=
mock
(
Context
::
class
.
java
)
`when`
(
mockedContext
.
applicationContext
).
thenReturn
(
mockAppContext
)
cache
=
spy
(
RocketChatCache
(
mockedContext
))
}
@Test
fun
getServerList_ShouldReturnHostnameList
()
{
val
hostnameList
=
JSONObject
()
.
put
(
"http://open.rocket.chat"
,
"images/logo/logo.png"
)
.
put
(
"http://192.168.0.6:3000"
,
"images/icon.svg"
)
.
toString
()
doReturn
(
hostnameList
).
`when`
(
cache
).
getString
(
"KEY_HOSTNAME_LIST"
,
null
)
val
expectedServerList
=
mutableListOf
(
Pair
(
"http://192.168.0.6:3000"
,
"http://192.168.0.6:3000/images/icon.svg"
),
Pair
(
"http://open.rocket.chat"
,
"http://open.rocket.chat/images/logo/logo.png"
))
val
serverList
=
cache
.
serverList
assertThat
(
serverList
,
equalTo
(
expectedServerList
))
}
}
\ No newline at end of file
app/src/test/kotlin/chat.rocket.android/push/PushManagerTest.kt
0 → 100644
View file @
5d064696
package
chat.rocket.android.push
import
android.app.Application
import
android.content.Context
import
android.content.res.Resources
import
android.os.Bundle
import
chat.rocket.android.BuildConfig
import
chat.rocket.android.R
import
chat.rocket.android.push.PushManager.PushMessage
import
com.nhaarman.mockito_kotlin.*
import
org.amshove.kluent.`should
equal
`
import
org.junit.Before
import
org.junit.Test
import
org.junit.runner.RunWith
import
org.mockito.ArgumentMatchers.anyString
import
org.mockito.MockitoAnnotations
import
org.robolectric.RobolectricTestRunner
import
org.robolectric.RuntimeEnvironment
import
org.robolectric.annotation.Config
import
kotlin.test.assertTrue
@RunWith
(
RobolectricTestRunner
::
class
)
@Config
(
constants
=
BuildConfig
::
class
,
application
=
PushManagerTest
.
StubApplication
::
class
,
sdk
=
intArrayOf
(
23
))
class
PushManagerTest
{
val
EJSON
=
"""
{
"host":"https://open.rocket.chat/",
"rid":"FaXMyHqbNJbPq6Ym9uWiyfQkgekhXywvKw",
"sender":{
"_id":"uWiFa3adOi0adac",
"username":"jean-luc.picard",
"name":"Jean-Luc Picard"
},
"type":"d",
"name":null
}
"""
val
EJSON_NO_SENDER
=
"""
{
"host":"https://open.rocket.chat/",
"rid":"FaXMyHqbNJbPq6Ym9uWiyfQkgekhXywvKw",
"sender":null,
"type":"d",
"name":null
}
"""
lateinit
var
data
:
Bundle
lateinit
var
pushManager
:
PushManager
lateinit
var
context
:
Context
@Before
fun
setUp
()
{
MockitoAnnotations
.
initMocks
(
this
)
data
=
Bundle
()
data
.
putString
(
"message"
,
"Hello"
)
data
.
putString
(
"title"
,
"jean-luc.picard"
)
data
.
putString
(
"ejson"
,
EJSON
)
data
.
putString
(
"notId"
,
"1"
)
context
=
spy
(
RuntimeEnvironment
.
application
)
pushManager
=
spy
(
PushManager
)
val
res
=
mock
<
Resources
>
{
on
{
getColor
(
any
())
}
doReturn
0
on
{
getIdentifier
(
anyString
(),
anyString
(),
any
())
}
doReturn
R
.
drawable
.
notification_background
on
{
getConfiguration
()
}
doReturn
RuntimeEnvironment
.
application
.
resources
.
configuration
}
whenever
(
context
.
resources
).
doReturn
(
res
)
whenever
(
context
.
applicationContext
).
doReturn
(
context
)
}
@Test
fun
`
should
create
PushMessage
without
throwing
`
()
{
PushMessage
(
null
,
null
,
null
,
null
,
null
,
"xxx"
,
null
,
null
)
}
@Test
fun
`
given
data
shoud
show
notification
`
()
{
pushManager
.
handle
(
context
,
data
)
val
push
=
PushMessage
(
title
=
data
[
"title"
]
as
String
,
message
=
data
[
"message"
]
as
String
,
ejson
=
EJSON
,
notificationId
=
"1"
)
verify
(
pushManager
,
times
(
1
)).
showNotification
(
context
,
push
)
}
@Test
fun
`
given
required
data
is
missing
do
not
show
notification
`
()
{
val
bundle
=
Bundle
()
pushManager
.
handle
(
context
,
bundle
)
verify
(
pushManager
,
never
()).
showNotification
(
any
(),
any
())
bundle
.
putString
(
"title"
,
"jean-luc.picard"
)
bundle
.
putString
(
"message"
,
"Hello!"
)
pushManager
.
handle
(
context
,
bundle
)
verify
(
pushManager
,
never
()).
showNotification
(
any
(),
any
())
bundle
.
clear
()
bundle
.
putString
(
"ejson"
,
EJSON
)
bundle
.
putString
(
"message"
,
"Hello!"
)
pushManager
.
handle
(
context
,
bundle
)
verify
(
pushManager
,
never
()).
showNotification
(
any
(),
any
())
bundle
.
clear
()
bundle
.
putString
(
"ejson"
,
EJSON
)
bundle
.
putString
(
"title"
,
"jean-luc.picard"
)
pushManager
.
handle
(
context
,
bundle
)
verify
(
pushManager
,
never
()).
showNotification
(
any
(),
any
())
}
@Test
fun
`
given
data
should
deserialize
correctly
`
()
{
pushManager
.
handle
(
context
,
data
)
val
push
=
PushMessage
(
data
.
getString
(
"title"
),
data
.
getString
(
"message"
),
null
,
EJSON
,
null
,
data
.
getString
(
"notId"
),
null
,
null
)
verify
(
pushManager
,
times
(
1
)).
showNotification
(
context
,
push
)
push
.
title
`
should
equal
`
"jean-luc.picard"
push
.
message
`
should
equal
`
"Hello"
val
sender
=
push
.
sender
assertTrue
(
sender
!=
null
)
sender
?.
_id
`
should
equal
`
"uWiFa3adOi0adac"
sender
?.
name
`
should
equal
`
"Jean-Luc Picard"
sender
?.
username
`
should
equal
`
"jean-luc.picard"
}
@Test
fun
`
given
that
only
sender
is
missing
show
notification
`
()
{
val
bundle
=
Bundle
()
bundle
.
putString
(
"title"
,
"jean-luc.picard"
)
bundle
.
putString
(
"message"
,
"Hello"
)
bundle
.
putString
(
"ejson"
,
EJSON_NO_SENDER
)
pushManager
.
handle
(
context
,
bundle
)
verify
(
pushManager
,
times
(
1
)).
showNotification
(
any
(),
any
())
}
internal
class
StubApplication
:
Application
()
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment