How to use media player for play a ringtone while one signal notification received in android - notifications

I am using onesignal for push notification. A call notification is received which has actions answer and reject.
i wanna play a sound while this notification is received to 45 second.
does one-signal have any solution for playing a sound while call notification? is there any alternative solution like media player?

I Fixed my problem by using media player .
In my app one signal notification clicks handled in Application class
But I used MyFirebaseMessagingService class for real time notification handle when app are closed.
MyFirebaseMessagingService class
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
}
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
Timber.tag("message").d(message.toString())
val data = message.data
var notificationCount = true
data.values.forEach {
if (notificationCount) {
val modelNotification = Gson().fromJson(it, NotificationResponse::class.java)
val notification_type = modelNotification.a?.notificationType
if (notification_type == "callStart"){
playRingtone()
}
notificationCount = false
}
}
}
private fun playRingtone() {
if (!PH7User.isAppOpen){
if (!isPlaying){
mediaPlayer = MediaPlayer.create(applicationContext, R.raw.ringtone)
mediaPlayer.isLooping = true
isPlaying = true
mediaPlayer.start()
}
}
}
}
In Android Manifest
add this service in application tag.
<service
android:name=".network.firebase.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
in InComing Call Activity
lateinit var mediaPlayer : MediaPlayer
var isPlaying = false
lateinit var instance: IncomingCall //? = null
var isOpenIncoming = false
override fun onRendered(viewModel: ConsultationViewModel, binding: ActivityIncomingCallBinding) {
binding.apply {
activity = this#IncomingCall
vm = viewModel
instance = this#IncomingCall
isOpenIncoming = true
viewModel.doctorProfile.value = userProfile
if (!isPlaying) playRingtone()
tvName.text = "${getString(R.string.dr)} $name"
Glide.with(this#IncomingCall).load(userProfile).placeholder(R.drawable.ic_profile_bg).into(ivProfile)
// broadcastReceiver()
}
SocketEvents.doctorCallReject {
lifecycleScope.launch {
try {
mediaPlayer.stop()
isPlaying = false
OneSignal.clearOneSignalNotifications()
finish()
} catch (e:Exception) {
toast(e.message.toString())
}
}
}
}
override fun onStop() {
super.onStop()
mediaPlayer.stop()
isPlaying = false
isOpenIncoming = false
}

Related

Store full-size images using a FileProvider

I want to save an image in its full size. I use a file provider for this. I can take the picture, but I don't get any data in the onActivityResult function. The intent is null.
What am I doing wrong?
provider_path.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="." />
</paths>
provider in AndroidManifest.xml:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.bkoubik.longlesstime.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_path"/>
</provider>
My Fragment Class:
fun capturePhoto(){
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val fileUri:File = createImageFile();
val photoURI = FileProvider.getUriForFile(
requireActivity(),
"com.bkoubik.longlesstime.fileprovider",
fileUri
)
intent.putExtra( MediaStore.EXTRA_OUTPUT, photoURI )
startActivityForResult(intent,IMAGE_REQUEST)
}
private fun createImageFile(): File {
val wrapper = ContextWrapper(requireContext())
var photoFile = wrapper.getDir(IMAGE_DIRECTORY,Context.MODE_PRIVATE)
photoFile = File(photoFile,"${UUID.randomUUID()}.jpg")
return photoFile
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null)
{
//data = null
val imgData = data.data!!
...
}
}
companion object {
private val IMAGE_DIRECTORY = "long_less"
}
Error:
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.bkoubik.longlesstime/app_long_less/1219a0bd-c17e-4709-b880-1e4f549362f6.jpg
First of all, I'll strongly recommend you to use new contracts API which is also recommended by Google:
While the underlying startActivityForResult() and onActivityResult() APIs are available on the Activity class on all API levels, it is strongly recommended to use the Activity Result APIs introduced in AndroidX Activity and Fragment. (see Android docs)
Than, when photo was taken from GUI, I'm getting photo data from provider like this (it's my separate camera request Activity). Also, I'm using external cache directory because on my Android 10.0 device data folder is not accessible (see Android docs)
P.S. Take a note about grantUriPermission method call on photo URI
// URI for photo file
private lateinit var mPhotoUri : Uri
/// Photo file itself
private lateinit var mPhotoFile : File
// Camera permission request
private val mRequestCamera = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
// Camera permission request parse
when (result.resultCode) {
// Success
RESULT_OK -> {
// Photo request
mRequestPhoto.launch(mPhotoUri)
}
// No permisison
else -> {
// Close activity
finish()
}
}
}
// Photo request
private val mRequestPhoto = registerForActivityResult(ActivityResultContracts.TakePicture()) { result ->
// Check request result
when (result) {
// Success
true -> {
// Photo from media stream
val photoData = BitmapFactory.decodeStream(
contentResolver.openInputStream(mPhotoUri)
)
// Stream for photo save
val dataStream = ByteArrayOutputStream()
// Compress photo as JPEG of 90% quality
photoData.compress(
Bitmap.CompressFormat.JPEG,
90,
dataStream
)
// Coroutine to write photo
CoroutineScope(Dispatchers.Main).launch {
// Inside IO context
withContext(Dispatchers.IO) {
// File output stream
FileOutputStream(mPhotoFile).also { fileStream ->
// Write JPEG image data to file
fileStream.write(dataStream.toByteArray())
// Close JPEG image data stream
dataStream.close()
// Close file stream
fileStream.close()
}
}
}
// Done here - notify someone photo is ready
sendBroadcast(
Intent(
<Where/Whom to send>
).apply {
// Content provider URI
putExtra(
<Photo URI extra name>,
mPhotoUri
)
}
)
// Close activity
finish()
}
// Error
else -> {
// Close activity
finish()
}
}
}
// Activity onCreate method
#Override
override fun onCreate(savedInstanceState: Bundle?) {
// Parent method call
super.onCreate(savedInstanceState)
// Try
try {
// Create temporary file
mPhotoFile = File.createTempFile(
"<file prefix>",
"<file suffix>",
externalCacheDir
).apply {
// Make it writable
setWritable(true)
}
// Get file URI (Android M+ ?)
mPhotoUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Trying to get provider (I send it to this Activity via Intent with extra "EXTRA_AUTHORITY")
intent?.extras?.getString("EXTRA_AUTHORITY")
?.let { authority ->
// For Android 7.0+ getting FileProvider
FileProvider.getUriForFile(
applicationContext,
authority,
mPhotoFile
)
// Else for Android 7.0 or lower
} ?: Uri.fromFile(mPhotoFile)
} else {
// Else for Android 7.0 or lower
Uri.fromFile(mPhotoFile)
}
// Permissions for URI itself
grantUriPermission(
packageName,
mPhotoUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
)
// Catch
} catch (e: Exception) {
// Stacktrace to logcat
e.printStackTrace()
// Close camera activity
finish()
}
// No camera permissions ?
if (!ActivityPermissionsCamera.hasPermissions(applicationContext)) {
// Intent for camera permissions
val permissionsIntent = Intent(
applicationContext,
<Permissions for Camera Activity>::class.java
).apply {
// New task
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
// Request permissions
mRequestCamera.launch(permissionsIntent)
} else {
// Camera launch
mRequestPhoto.launch(mPhotoUri)
}
}
And this is how I reques photo from camera:
// Photo pending intent
private fun createPendingIntentPhoto() : PendingIntent? {
// Runtime check - any camera ?!
if (!applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
// No camera - no photo !!!
return null
}
// Intent flags
val intentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or
PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
// Pending intent
return PendingIntent.getActivity(
applicationContext,
1,
Intent(
applicationContext,
<Activity for Camera>::class.java
).apply {
// Pass provider as Intent extra
putExtra("EXTRA_AUTHORITY", "${BuildConfig.APPLICATION_ID}.provider")
},
intentFlags
)
}
P.P.S. Quick note on why do I pass provider URI over Intent's extra - my camera routines are in separate library which is used in GUI-project So they can have different BuildConfig.APPLICATION_ID. This allows me to use same library in many projects.

Coroutines not working in jetpack Compose

I use the following way to get network data.
I start a network request in a coroutine but it doesn't work, the pagination load is not called.
But if I call the network request through the init method in the ViewModel I can get the data successfully.
#Composable
fun HomeView() {
val viewModel = hiltViewModel<CountryViewModel>()
LaunchedEffect(true) {
viewModel.getCountryList() // Not working
}
val pagingItems = viewModel.countryGroupList.collectAsLazyPagingItems()
Scaffold {
LazyColumn(
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 96.dp),
verticalArrangement = Arrangement.spacedBy(32.dp),
modifier = Modifier.fillMaxSize()) {
items(pagingItems) { countryGroup ->
if (countryGroup == null) return#items
Text(text = "Hello", modifier = Modifier.height(100.dp))
}
}
}
}
#HiltViewModel
class CountryViewModel #Inject constructor() : ViewModel() {
var countryGroupList = flowOf<PagingData<CountryGroup>>()
private val config = PagingConfig(pageSize = 26, prefetchDistance = 1, initialLoadSize = 26)
init {
getCountryList() // can work
}
fun getCountryList() {
countryGroupList = Pager(config) {
CountrySource()
}.flow.cachedIn(viewModelScope)
}
}
I don't understand what's the difference between the two calls, why doesn't it work?
Any helpful comments and answers are greatly appreciated.
I solved the problem, the coroutine was used twice in the code above, which caused network data to not be fetched.
A coroutine is used here:
fun getCountryList() {
countryGroupList = Pager(config) {
CountrySource()
}.flow.cachedIn(viewModelScope)
}
Here is another coroutine:
LaunchedEffect(true) {
viewModel.getCountryList() // Not working
}
current usage:
val execute = rememberSaveable { mutableStateOf(true) }
if (execute.value) {
viewModel.getCountryList()
execute.value = false
}

Kotlin: Edit icon dashboard of icons between fragments

I'm trying to figure out the most efficient way to structure this problem..
I'd like to click on the 'EDIT' icon in the dashboard of the MainFragment, display a DialogFragment, allow user to select/deselect up to 5 icons, save the selection, close the DialogFragment, and update the MainFragment.
Should I use MutableLiveData/Observer from a ViewModel? Or is there a better approach? I currently cannot figure out how to use the ViewModel approach correctly...
So far, this is the code I have:
MainFragment: https://i.stack.imgur.com/5fRt2.png
DialogFragment: https://i.stack.imgur.com/ZvW3d.png
ViewModel Class:
class IconDashboardViewModel() : ViewModel(){
var liveDataDashIcons: MutableLiveData<MutableList<String>> = MutableLiveData()
var liveItemData: MutableLiveData<String> = MutableLiveData()
// Observer for live list
fun getLiveDataObserver(): MutableLiveData<MutableList<String>> {
return liveDataDashIcons
}
// Observer for each icon
fun getLiveItemObserver(): MutableLiveData<String> {
return liveItemData
}
// Set icon list
fun setLiveDashIconsList(iconList: MutableLiveData<MutableList<String>>) {
liveDataDashIcons.value = iconList.value
}
// Set data for data
fun setItemData(icon : MutableLiveData<String>) {
liveItemData.value = icon.toString()
}
var iconList = mutableListOf<String>()
}
MainFragment:
private fun populateIconList() : MutableLiveData<MutableList> {
var iconList = viewModel.liveDataDashIcons
// Roster icon
if (roster_dash_layout.visibility == View.VISIBLE) {
iconList.value!!.add(getString(R.string.roster))
} else {
if (iconList.value!!.contains(getString(R.string.roster))) {
iconList.value!!.remove(getString(R.string.roster))
}
}
}
DialogFragment:
private fun setIconList(iconList: MutableList){
var iconList = viewModel.iconList
Log.d(TAG, "viewModel iconList = " + iconList)
if (iconList.contains(getString(R.string.roster))) {
binding.radioButtonRosterPick.setBackgroundResource(R.drawable.icon_helmet_blue_bg)
}
}

how to test project reactor code that does not return a subscription

i'm trying to create a component that stream data from remote service continuously. The component starts and stops according to spring container lifecycle. I'm not sure how to test this component as the subscription is done inside my component so i was wondering wether this is the correct way to implement this kind of component with webflux or not. Does anybody know any similar component in any framework from where i might take some ideas?
Regards
class StreamingTaskAdapter(
private val streamEventsUseCase: StreamEventsUseCase,
private val subscriptionProperties: subscriptionProperties,
) : SmartLifecycle, DisposableBean, BeanNameAware {
private lateinit var disposable: Disposable
private var running: Boolean = false
private var beanName: String = "StreamingTaskAdapter"
private val logger = KotlinLogging.logger {}
override fun start() {
logger.info { "Starting container with name $beanName" }
running = true
doStart()
}
private fun doStart() {
disposable = Mono.just(
CreateSubscriptionCommand(
subscriptionProperties.events,
subscriptionProperties.owningApplication
)
)
.flatMap(streamEventsUseCase::createSubscription)
.flatMap { subscription ->
Mono.just(subscription)
.map(::ConsumeSubscriptionCommand)
.flatMap(streamEventsUseCase::consumeSubscription)
}
.repeat()
.retryWhen(Retry.backoff(MAX_ATTEMPTS, Duration.ofSeconds(2)).jitter(0.75))
.doOnSubscribe { logger.info { "Started event streaming" } }
.doOnTerminate { logger.info { "Stopped event streaming" } }
.subscribe()
}
override fun stop() {
logger.info("Stopping container with name $beanName")
doStop()
}
override fun isRunning(): Boolean = running
private fun doStop() {
running = false
disposable.dispose()
}
override fun destroy() {
logger.info("Destroying container with name $beanName")
doStop()
}
override fun setBeanName(name: String) {
this.beanName = name
}
companion object {
const val MAX_ATTEMPTS: Long = 3
}
}

How can I successfully pass my LiveData from my Repository to my Compose UI using a ViewModel?

I am trying to pass live events from a Broadcast Receiver to the title of my Homepage.
I am passing a String from the Broadcast Receiver to my Repository successfully, but in the end my title is always null. What am I missing?
My Repository looks like this:
object Repository {
fun getAndSendData (query: String): String{
return query
}
}
Then in my ViewModel I have:
private val _data = MutableLiveData<String>()
val repoData = _data.switchMap {
liveData {
emit(Repository.getAndSendData(it))
}
}
And finally in my Composable I have:
val repoData = viewModel.repoData.observeAsState()
topBar = {
TopAppBar(
title = { Text(text = if (repoData.value == null)"null" else repoData.value!!, style = typography.body1) },
navigationIcon = {
IconButton(onClick = { scaffoldState.drawerState.open() }) {
Icon(Icons.Rounded.Menu)
}
}
)
},
I don't think we can use Live Data from the compose(jetpack) because it can run from the Main thread. I used onCommit() {} with interface from the compose.
onCommit() {
viewModel.testCountriesList(object : NetworkCallBack {
override fun test(response: GeneralResponse) {
if(response.code == 200) {
counties = response.response
responseState.value = true
} else {
responseState.value = false
}
}
})
}