I am making a chat app and I am trying to make message notification.
I have a Firebase database that i store all my data (users, friends, messages). When user sends a message, app adds that message to firebase database ( path is "/notification/user_who_recived/user_who_send" ).
my problem is i cant make service that gets message in "/notification..../" path and makes notification based on message.
here is my test code that doesnt work:
package com.cagsak.chatapp
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
class background_service : Service(){
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
println("background_service: THREAD İS STARTED")
val uid = FirebaseAuth.getInstance().uid ?: stopSelf()
val ref = FirebaseDatabase.getInstance().getReference("/notifications/$uid")
ref.get()
.addOnSuccessListener { data_list ->
println("background_service: MESSAGE GETTING IS SUCCESSFUL")
data_list.children.forEach { child ->
val data = child.getValue(ChatMessage::class.java)
if (data != null) {
println("background_service: MESSAGE RECEIVER IS FOUND")
SendNotification(data, baseContext)
child.ref.removeValue().addOnSuccessListener {
println("background_service: MESSAGE DATA IS DELETED")
}
}
}
}
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
private fun SendNotification(message: ChatMessage, context: Context) {
FirebaseDatabase.getInstance()
.getReference("/users").get()
.addOnSuccessListener { userlist ->
userlist.children.forEach { user_data ->
val user = user_data.getValue(User::class.java)
if (user != null && user.uid == message.fromId) {
val intent = Intent(context, ChatMessage::class.java)
intent.putExtra("USERKEY", user)
val pendingIntent: PendingIntent =
PendingIntent.getActivity(context, 0, intent, 0)
NotificationCompat.Builder(context, "MY_CHANEL_ID")
.setSmallIcon(R.drawable.small_logo)
.setContentTitle(message.fromId)
.setContentText(message.text)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
}
}
}
}
}
Related
I'm building an app (android studio - java) for my several devices, the app won't be on app store.
I want to be able to update the app remotely automatically.
I figured out how to check the version against a server, however I still don't know how to automatically update the app whenever the is a new version.
Thanks a head!
Firstly, as you said, you need to check the latest version, online and locally.
Then you need to put the apk newest apk online
Finally, if the online version is ahead the local, you need to download the file and update the app.
The easiest way to do it is to use the ota_update package to download and update the app
I hope this solution is useful for you
First you have to store the latest version on the server side using API.
After that you have to compare the installed current app version to the server-side stored version if both are different and the current version lower than the server-side version at that time you have to perform the below code.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!getPackageManager().canRequestPackageInstalls()) {
startActivityForResult(new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", getPackageName()))), 1234);
} else {
downloadController = new DownloadController(DashboardNewActivity.this, url);
downloadController.enqueueDownload();
}
}
Use this DownloadController.kt class
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.core.content.FileProvider
import com.app.golden.BuildConfig
import com.app.golden.R
import java.io.File
class DownloadController(private val context: Context, private val url: String) {
companion object {
private const val FILE_NAME = "company.apk"
private const val FILE_BASE_PATH = "file://"
private const val MIME_TYPE = "application/vnd.android.package-archive"
private const val PROVIDER_PATH = ".provider"
private const val APP_INSTALL_PATH = "\"application/vnd.android.package-archive\""
}
fun enqueueDownload() {
var destination = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/"
destination += FILE_NAME
val uri = Uri.parse("$FILE_BASE_PATH$destination")
val file = File(destination)
if (file.exists()) file.delete()
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadUri = Uri.parse(url)
val request = DownloadManager.Request(downloadUri)
request.setMimeType(MIME_TYPE)
request.setTitle(context.getString(R.string.app_name))
request.setDescription(context.getString(R.string.downloading))
// set destination
request.setDestinationUri(uri)
showInstallOption(destination, URI)
// Enqueue a new download and same the referenced
downloadManager.enqueue(request)
Toast.makeText(context, context.getString(R.string.downloading), Toast.LENGTH_LONG)
.show()
}
private fun showInstallOption(
destination: String,
uri: Uri
) {
// set BroadcastReceiver to install app when .apk is downloaded
val onComplete = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val contentUri = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + PROVIDER_PATH,
File(destination)
)
val install = Intent(Intent.ACTION_VIEW)
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
install.data = contentUri
context.startActivity(install)
context.unregisterReceiver(this)
} else {
val install = Intent(Intent.ACTION_VIEW)
install.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
install.setDataAndType(
uri,
APP_INSTALL_PATH
)
context.startActivity(install)
context.unregisterReceiver(this)
}
} catch (e: Exception) {
val data = e.message.toString()
Log.e("Exception: ", data)
}
}
}
context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
}
}
I need to connect my android app to an azure sql server to add to the sql server and population data from it in the app for school.
I’m not a great programmer, and don’t have much experience with Android and backend work.
Can anyone point me in the right direction on where to get started? I feel like anytime I see something for 2+ years ago the information is irrelevant.
I don’t need anything extravagant - bare bones is great.
Additionally, there seems to be more than one way to accomplish this? I see some mention jdbc and others mention rest api.
Here is the sample code for connecting SQL server from Android application.
package com.javahelps.usermanagement
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Toast
import com.javahelps.usermanagement.service.Model
import com.javahelps.usermanagement.service.ServiceFactory
import com.javahelps.usermanagement.service.UserService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
private val service by lazy {
val factory = ServiceFactory.getInstance("http://10.0.2.2:8080", "admin", "admin")
factory.build(UserService::class.java)
}
private var disposable: Disposable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun createUser(view: View) {
// Construct a user object
val user = Model.User(
etUsername.text.toString(),
etPassword.text.toString(),
etName.text.toString(),
etEmail.text.toString()
)
this.disposable = this.service.create(user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ showResult("Successfully created the new user ${user.username}") },
{ showResult("Failed to create the new user!") })
}
fun readUser(view: View) {
val username = etUsername.text.toString()
this.disposable = this.service.read(username)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ user ->
run {
etUsername.setText(user.username)
etPassword.setText(user.password)
etName.setText(user.name)
etEmail.setText(user.email)
}
},
{ showResult("Failed to read the user $username") })
}
fun updateUser(view: View) {
val username = etUsername.text.toString()
// Construct a user object
val user = Model.User(
etUsername.text.toString(),
etPassword.text.toString(),
etName.text.toString(),
etEmail.text.toString()
)
this.disposable = this.service.update(username, user)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ showResult("Successfully updated the user $username") },
{ showResult("Failed to update the user $username") })
}
fun deleteUser(view: View) {
val username = etUsername.text.toString()
// Prevent from accidentally deleting the admin user
if ("admin" == username) {
showResult("Cannot delete admin!")
return
}
this.disposable = this.service.delete(username)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ showResult("Successfully deleted the user $username") },
{ showResult("Failed to delete the user $username") })
}
private fun showResult(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
override fun onPause() {
super.onPause()
disposable?.dispose()
}
}
For the required steps connecting sql server with android application follow this document.
i am working on an online shopping application using retrofit, coroutine, livedata, mvvm,...
i want to show progress bar before fetching data from server for afew seconds
if i have one api request i can show that but in this app i have multiple request
what should i do in this situation how i should show progress bar??
Api Service
#GET("homeslider.php")
suspend fun getSliderImages(): Response<List<Model.Slider>>
#GET("amazingoffer.php")
suspend fun getAmazingProduct(): Response<List<Model.AmazingProduct>>
#GET("handsImages.php")
suspend fun getHandsFreeData(
#Query(
"handsfree_id"
) handsfree_id: Int
): Response<List<Model.HandsFreeImages>>
#GET("handsfreemoreinfo.php")
suspend fun gethandsfreemoreinfo(): Response<List<Model.HandsFreeMore>>
#GET("wristmetadata.php")
suspend fun getWristWatchMetaData(
#Query(
"wrist_id"
) wrist_id: Int
): Response<List<Model.WristWatch>>
repository
fun getSliderImages(): LiveData<List<Model.Slider>> {
val data = MutableLiveData<List<Model.Slider>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getSliderImages()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getAmazingOffer(): LiveData<List<Model.AmazingProduct>> {
val data = MutableLiveData<List<Model.AmazingProduct>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getAmazingProduct()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
fun getHandsFreeData(handsree_id: Int): LiveData<List<Model.HandsFreeImages>> {
val dfData = MutableLiveData<List<Model.HandsFreeImages>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.getHandsFreeData(handsree_id)
withContext(Main + SupervisorJob(job)) {
dfData.value = response.body()
}
job.complete()
job.cancel()
}
return dfData
}
fun getHandsFreeMore(): LiveData<List<Model.HandsFreeMore>> {
val data = MutableLiveData<List<Model.HandsFreeMore>>()
val job = Job()
applicationScope.launch(IO + job) {
val response = api.gethandsfreemoreinfo()
withContext(Main + SupervisorJob(job)) {
data.value = response.body()
}
job.complete()
job.cancel()
}
return data
}
VIEWMODEL
fun getSliderImages() = repository.getSliderImages()
fun getAmazingOffer() = repository.getAmazingOffer()
fun recieveAdvertise() = repository.recieveAdvertise()
fun dailyShoes(context: Context) = repository.getDailyShoes(context)
i will appreciate your help
I couldn't help but notice that your repository contains lots of repetitive code. first point to learn here is that all that logic in Repository, it usually goes in the ViewModel. second thing is that you are using applicationScope to launch your coroutines, which usually is done using viewModelScope(takes care of cancellation) object which is available in every viewModel.
So first we have to take care of that repetitive code and move it to ViewModel. So your viewModel would now look like
class YourViewModel: ViewModel() {
// Your other init code, repo creation etc
// Live data objects for progressBar and error, we will observe these in Fragment/Activity
val showProgress: MutableLiveData<Boolean> = MutableLiveData()
val errorMessage: MutableLiveData<String> = MutableLiveData()
/**
* A Generic api caller, which updates the given live data object with the api result
* and internally takes care of progress bar visibility. */
private fun <T> callApiAndPost(liveData: MutableLiveData<T>,
apiCall: () -> Response<T> ) = viewModelScope.launch {
try{
showProgress.postValue(true) // Show prgress bar when api call is active
if(result.code() == 200) { liveData.postValue(result.body()) }
else{ errorMessage.postValue("Network call failed, try again") }
showProgress.postValue(false)
}
catch (e: Exception){
errorMessage.postValue("Network call failed, try again")
showProgress.postValue(false)
}
}
/******** Now all your API call methods should be called as *************/
// First declare the live data object which will contain the api result
val sliderData: MutableLiveData<List<Model.Slider>> = MutableLiveData()
// Now call the API as
fun getSliderImages() = callApiAndPost(sliderData) {
repository.getSliderImages()
}
}
After that remove all the logic from Repository and make it simply call the network methods as
suspend fun getSliderImages() = api.getSliderImages() // simply delegate to network layer
And finally to display the progress bar, simply observe the showProgress LiveData object in your Activity/Fragment as
viewModel.showProgress.observer(this, Observer{
progressBar.visibility = if(it) View.VISIBLE else View.GONE
}
First create a enum class status:
enum class Status {
SUCCESS,
ERROR,
LOADING
}
Then create resource class like this:
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
Now add your request to a list of response:
var list = java.util.ArrayList<Response<*>>()
suspend fun getApis() = list.addAll(
listOf(
api.advertise(),
api.getAmazingProduct(),
api.dailyShoes(),
api.getSliderImages(),
.
.
.
)
)
In your viewmodel class:
private val _apis = MutableLiveData<Resource<*>>()
val apis: LiveData<Resource<*>>
get() = _apis
init {
getAllApi()
}
fun getAllApi() {
val job = Job()
viewModelScope.launch(IO + job) {
_apis.postValue(
Resource.loading(null)
)
delay(2000)
repository.getApis().let {
withContext(Main + SupervisorJob(job)) {
it.let {
if (it) {
_apis.postValue(Resource.success(it))
} else {
_apis.postValue(Resource.error("Unknown error eccured", null))
}
}
}
}
job.complete()
job.cancel()
}
}
Now you can use status to show progress like this . use this part in your target fragment:
private fun setProgress() {
viewModel.apis.observe(viewLifecycleOwner) {
when (it.status) {
Status.SUCCESS -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.VISIBLE
parentscroll.visibility = View.VISIBLE
}
}
Status.ERROR -> {
binding.apply {
progress.visibility = View.INVISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
Status.LOADING -> {
binding.apply {
progress.visibility = View.VISIBLE
line1.visibility = View.INVISIBLE
parentscroll.visibility = View.INVISIBLE
}
}
}
}
}
I hope you find it useful.
I'm getting the error com.google.firebase.database.DatabaseException: Failed to convert value of type java.lang.Long to String in line 53
The line is var note = n.getValue(Note::class.java)
I've been stuck on this error for awhile as I'm new enough to coding and I'm not sure what to do, any help is appreciated, thanks very much
package com.example.gearoidodonovan.books
import android.app.AlertDialog
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.renderscript.Sampler
import android.widget.Toast
import com.google.firebase.database.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.add_note.view.*
import java.text.SimpleDateFormat
import java.util.*
class MainActivity : AppCompatActivity() {
var mRef:DatabaseReference? = null
var mNoteList:ArrayList<Note>?= null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val database = FirebaseDatabase.getInstance()
mRef = database.getReference("Notes")
mNoteList = ArrayList()
add_new_note.setOnClickListener {
showDialogAddNote()
}
}
override fun onStart(){
super.onStart()
mRef?.addValueEventListener(object : ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
for (n in p0!!.children) {
var note = n.getValue(Note::class.java)
mNoteList?.add(note!!)
}
val noteAdapter = NoteAdapter(applicationContext, mNoteList!!)
note_list_view.adapter = noteAdapter
}
})
}
fun showDialogAddNote() {
val alertBuilder = AlertDialog.Builder(this)
val view = layoutInflater.inflate(R.layout.add_note, null)
alertBuilder.setView(view)
val alertDialog = alertBuilder.create()
alertDialog.show()
view.btnSaveNote.setOnClickListener {
val title = view.etTitle.text.toString()
val note = view.etNote.text.toString()
if (title.isNotEmpty() && note.isNotEmpty()) {
var id = mRef!!.push().key!! //reference 1
var myNote = Note(id, title, note, getCurrentDate())
mRef!!.child(id).setValue(myNote)
alertDialog.dismiss()
} else {
Toast.makeText(this, "Empty", Toast.LENGTH_LONG).show()
}
}
}
fun getCurrentDate(): String{
val calendar = Calendar.getInstance()
val mdformat = SimpleDateFormat("EEEE hh:mm a ")
val strDate = mdformat.format(calendar.time)
return strDate
}
}
Here is my note class
package com.example.gearoidodonovan.books
import com.google.firebase.database.Exclude
import com.google.firebase.database.ServerValue
class Note {
var id:String?= null
var title:String? = null
var note:String?= null
var timestamp: String? = null
constructor(){
}
constructor(id: String, title: String, note: String, timestamp: String){
this.id = id
this.title = title
this.note = note
this.timestamp = timestamp
}
}
you are getting this error because you have a field of type long in your firebase node which you are saving in String. make sure that data types are same as in firebase node and your data class.
I think most of the cases the id is of data type long, you can check that and change your data class of Note according to that
Order attributes of your class note as following
var id:String?= null
var note:long?= null
var timestamp: String? = null
var title:String? = null```
I'm using UrbanAirship for push notifications. Everything is working fine, but there are certain scenarios where I wouldn't like to display the push notification coming from urban. Do I have to handle this on the server side or can I handle it on the client side?
If possible, how would I handle this on the client side? I've tried canceling the notification, and that works, but it still displays the rollover message in the status bar.
You need to override Airship's notification factory for this:
package your.package;
import android.app.Notification;
import android.content.Context;
import android.support.annotation.NonNull;
import com.urbanairship.push.PushMessage;
import com.urbanairship.push.notifications.DefaultNotificationFactory;
public class PushNotificationFactory extends DefaultNotificationFactory {
public PushNotificationFactory(Context context) {
super(context);
}
public Notification createNotification(#NonNull PushMessage message, int notificationId) {
boolean shouldWeShowNotification = false; // your condition goes here
if (shouldWeShowNotification) {
return super.createNotification(message, notificationId);
} else {
return null;
}
}
}
and when taking off:
UAirship.takeOff(this, new UAirship.OnReadyCallback() {
#Override
public void onAirshipReady(UAirship airship) {
NotificationFactory notificationFactory = new PushNotificationFactory(getApplicationContext());
airship.getPushManager().setNotificationFactory(notificationFactory);
}
});
You need to create a BroadcastReceiver in your app to intercept the push. You can then choose to display a custom notification or not.
Check out the setup docs http://docs.urbanairship.com/build/android_features.html#set-up
If you're using Kotlin:
class PushNotificationFactory(context: Context) : NotificationFactory(context) {
override fun createNotification(message: PushMessage, notificationId: Int): Notification? {
val notificationWillBeShowed = false // your condition goes here
return if (notificationWillBeShowed) {
// Show notification
super.createNotification(message, notificationId)
} else {
// Prevent notification from showing
null
}
}
}
class UrbanAirshipAutopilot : Autopilot() {
#RequiresApi(Build.VERSION_CODES.O)
override fun onAirshipReady(airship: UAirship) {
airship.pushManager.userNotificationsEnabled = true
val context = UAirship.getApplicationContext()
val notificationFactory = PushNotificationFactory(context)
airship.pushManager.notificationFactory = notificationFactory
if (Build.VERSION.SDK_INT >= 26) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val defaultChannelId = context.getString(R.string.notification_channel_id_default)
val defaultChannelName = context.getString(R.string.notification_channel_name_default)
val channel = NotificationChannel(defaultChannelId, defaultChannelName, NotificationManager.IMPORTANCE_DEFAULT)
notificationManager.createNotificationChannel(channel)
}
}
override fun createAirshipConfigOptions(context: Context): AirshipConfigOptions? {
val defaultChannelId = context.getString(R.string.notification_channel_id_default)
return AirshipConfigOptions.Builder()
.setDevelopmentAppKey(BuildConfig.URBAN_AIRSHIP_APP_KEY_DEVELOPMENT)
.setDevelopmentAppSecret(BuildConfig.URBAN_AIRSHIP_APP_SECRET_DEVELOPMENT)
.setProductionAppKey(BuildConfig.URBAN_AIRSHIP_APP_KEY_PRODUCTION)
.setProductionAppSecret(BuildConfig.URBAN_AIRSHIP_APP_SECRET_PRODUCTION)
.setInProduction(!BuildConfig.DEBUG)
.setGcmSender(BuildConfig.CLOUD_MESSAGING_SENDER_ID) // FCM/GCM sender ID
// .setNotificationIcon(R.drawable.ic_notification)
// .setNotificationAccentColor(ContextCompat(getContext(), R.color.accent))
.setNotificationChannel(defaultChannelId)
.build()
}
}