Just add notification channel id - kotlin

in your code you dont have a **channel **which notification can work with us code
e.g.
private fun showNotification() {
val builder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
builder.setContentTitle("")
builder.setContentText("")
builder.setSmallIcon()
val notification = builder.build()
val manager = NotificationManagerCompat.from(this)
manager.notify(1, notification)
}
companion object {
const val NOTIFICATION_CHANNEL_ID = "my_channel_id_01"
}
in this moment you can just add const NOTIFICATION_CHANNEL_ID and your code does work

Related

how to add a points system to an app preferences DataStore Jetpack Compose

I'm working on a Quiz app and I'm trying to add a points system to the app so that everytime the user gets a question right he gets a +1pts.
and for storing the points I use jetpack compose preferences Datastore.
the problem is whenever I want to add a point to the already saved points it doesn't work.
this is my PointsData
class PointsData(private val context: Context) {
//create the preference datastore
companion object{
private val Context.datastore : DataStore<Preferences> by preferencesDataStore("points")
val CURRENT_POINTS_KEY = intPreferencesKey("points")
}
//get the current points
val getpoints: Flow<Int> =context.datastore.data.map { preferences->
preferences[CURRENT_POINTS_KEY] ?: 0
}
// to save current points
suspend fun SaveCurrentPoints(numPoints : Int){
context.datastore.edit {preferences ->
preferences[PointsData.CURRENT_POINTS_KEY] = numPoints
}
}
}
save points methode
class SavePoints {
companion object {
#Composable
fun savepoints(numPointsToSave : Int) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val datastore = PointsData(context)
LaunchedEffect(1) {
scope.launch {
datastore.SaveCurrentPoints(numPointsToSave)
}
}
}
}
}
and whenever i want to get the number of points from my DataStore i use
val pointsdatastore = PointsData(context)
val currentpoints = pointsdatastore.getpoints.collectAsState(initial = 0)
//display it as text for example
Text(text = currentpoints.value.toString(), fontSize = 30.sp, fontWeight = FontWeight.Bold,
color = Color.White)
and to do the operation i want (add +1 o the already existing points i do this
val pointsdatastore = PointsData(context)
val currentpoints = pointsdatastore.getpoints.collectAsState(initial = 0)
SavePoints.savepoints(numPointsToSave = currentpoints.value + 1)
but it doesn't seem to work because the number of points always stays 1.
if you know whats the problem please help.
I found the answer my self but for anyone who is stuck in same situation the solution is to another method in PointsData(look at the question provided code)
the method is:
suspend fun incrementpoints(){
context.datastore.edit { preferences->
val currentpoints = preferences[CURRENT_POINTS_KEY] ?: 0
preferences[CURRENT_POINTS_KEY] = currentpoints + 1
}
}
(if you want decrement not increment you can just change + to - )
and now in the PointsMethod(look at the question provided code) you should add
#Composable
fun incrementpoints() {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val datastore = PointsData(context)
LaunchedEffect(key1 = 1) {
scope.launch {
datastore.incrementpoints()
}
}
}

How to send data another page and list with kotlin?

I just started learning Kotlin. I am making an application that saves bank (IBAN) information. I save the information locally using sharedpreferences.
I save the information to the list and save it as sharedpreferences.
Information is recorded on page 2 and I want to list this recorded information on page 1.
I was doing this simply in Flutter, but I couldn't understand how to do it in Kotlin.
package com.example.ibansfer
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.ibansfer.databinding.ActivityAddIbanBinding
import com.example.ibansfer.models.IbanModel
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class AddIbanActivity : AppCompatActivity() {
private lateinit var binding: ActivityAddIbanBinding
private var ibanList: ArrayList<IbanModel> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddIbanBinding.inflate(layoutInflater)
setContentView(binding.root)
setTitle("IBAN bilgisi ekle")
getSupportActionBar()?.setDisplayHomeAsUpEnabled(true);
val sharedPreferences = this.getSharedPreferences("ibans", MODE_PRIVATE)
val editor = sharedPreferences.edit()
fun saveData() {
val gson = Gson()
val json = gson.toJson(ibanList)
editor.putString("ibans", json)
editor.apply()
}
binding.ibanSaveBttn.setOnClickListener {
val ibanOwner = binding.ibanOwner.text.toString()
val bankName = binding.bankName.text.toString()
val ibanAdress = binding.ibanAdress.text.toString()
if (ibanOwner.isEmpty() || bankName.isEmpty() || ibanAdress.isEmpty()) {
Toast.makeText(this, "Lütfen tüm alanları doldurunuz.", Toast.LENGTH_LONG).show()
} else {
ibanList.add(IbanModel(ibanOwner, bankName, ibanAdress))
saveData()
Toast.makeText(this, "IBAN bilgileri başarıyla eklendi.", Toast.LENGTH_LONG).show()
finish()
}
}
}
}
If you save the data in SharedPrefferences, you can always retrieve it back in the other screen.
val sharedPref = activity?.getPreferences("ibans", Context.MODE_PRIVATE) ?: return
val myString = sharedPref.getString("ibans", defaultValue)
You can even set up a SharedPrefferences listener that will get notified whenever new data is added to it and act accordingly.
val sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
// code to execute on changes here
}
activity?.getPreferences("ibans", Context.MODE_PRIVATE)
?.registerOnSharedPreferenceChangeListener(sharedPreferencesListener )
Also don't forget to unregister using unregisterOnSharedPreferenceChangeListener
Or alternatively, use Intents as the comment suggests to pass data between Activities/Fragments.

Why can't a State<T> variable fire UI update when it is assigned by a new value?

The Code A is based a Android offical sample project here.
The Code A can display correct data in UI.
If I use Code B, I find that nothing is displayed.
It seems that _uiState=_uiState.copy(...) doesn't make uiState to notice UI that the data has changed in Code B.
What is wrong with Code B?
Code A
class InterestsViewModel(
private val interestsRepository: InterestsRepository
) : ViewModel() {
// UI state exposed to the UI
private var _uiState by mutableStateOf (InterestsUiState(loading = true)) //My
val uiState: InterestsUiState = _uiState //My
private fun refreshAll() {
_uiState .loading = true //My
viewModelScope.launch {
// Trigger repository requests in parallel
val topicsDeferred = async { interestsRepository.getTopics() }
val peopleDeferred = async { interestsRepository.getPeople() }
val publicationsDeferred = async { interestsRepository.getPublications() }
// Wait for all requests to finish
val topics = topicsDeferred.await().successOr(emptyList())
val people = peopleDeferred.await().successOr(emptyList())
val publications = publicationsDeferred.await().successOr(emptyList())
_uiState.loading=false //My
_uiState.topics=topics //My
_uiState.people=people //My
_uiState.publications=publications //My
}
}
}
fun rememberTabContent(interestsViewModel: InterestsViewModel): List<TabContent> {
// UiState of the InterestsScreen
val uiState = interestsViewModel.uiState //My
...
}
data class InterestsUiState(
var topics: List<InterestSection> = emptyList(), //My
var people: List<String> = emptyList(), //My
var publications: List<String> = emptyList(), //My
var loading: Boolean = false, //My
)
Code B
class InterestsViewModel(
private val interestsRepository: InterestsRepository
) : ViewModel() {
// UI state exposed to the UI
private var _uiState by mutableStateOf (InterestsUiState(loading = true))
val uiState: InterestsUiState = _uiState
private fun refreshAll() {
_uiState .loading = true
viewModelScope.launch {
// Trigger repository requests in parallel
val topicsDeferred = async { interestsRepository.getTopics() }
val peopleDeferred = async { interestsRepository.getPeople() }
val publicationsDeferred = async { interestsRepository.getPublications() }
// Wait for all requests to finish
val topics = topicsDeferred.await().successOr(emptyList())
val people = peopleDeferred.await().successOr(emptyList())
val publications = publicationsDeferred.await().successOr(emptyList())
_uiState=_uiState.copy(
loading = false,
topics = topics,
people = people,
publications = publications
)
}
}
}
I don't see your pattern described in the official docs:
https://developer.android.com/jetpack/compose/state
It is possible it worked under an older version of Compose and doesn't work under the current version??
According to the docs, recomposition can only occur when you use mutableStateOf in conjunction with a remember and set the value property to a new value to trigger the recomposition:
val someProperty = remember { mutableStateOf(0) }
someProperty.value = 123
But this is done in a composable. If you want to trigger this within your viewmodel, you should resort to using LiveData. Here's an example:
https://stackoverflow.com/a/69718724/753632
Using mutableStateOf on its own doesn't trigger recomposition. It is only used to store the state.

Kotlin code Send SMS from within App without using default app

I am trying to build an app whereby the user clicks a submit button which will send the contents of their input via SMS to a predefined number. Being very new to Kotlin, I have been helped with the code to send the data via SMS, however it opens up the default messaging app and the user has to interact with the messaging app and then navigate back to my app. What I would like is for this to happen in the background and send directly from my app. The code is below...Any help greatly appreciated, Many thanks
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val backbut = findViewById<Button>(R.id.backbut)
backbut.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
var spinner: Spinner? = null
spinner = this.spinner
val sub1: Button = findViewById<Button>(R.id.sub1)
sub1.setOnClickListener {
val cust: String = cust.text.toString()
val reg: String = reg.text.toString()
val pal: String = pal.text.toString()
val cont:String = cont.text.toString()
val data: String =
"CUSTOMER : ".plus(cust).plus("\n").plus("CONTAINER : ").plus(cont).plus("\n").plus("VEH
REG : ").plus(reg).plus("\n").plus("PALLETS : ")
.plus(pal)
startActivity(getSendSmsIntent("1234567", data))
}
}
// textview_selected!!.text = "Selected : "+ Spinner [position]
private fun getSendSmsIntent(phoneNumber: String, content: String?): Intent? {
val uri = Uri.parse("smsto:$phoneNumber")
val intent = Intent(Intent.ACTION_SENDTO, uri)
intent.putExtra("sms_body", content)
return getIntent(intent, true)
}
private fun getIntent(intent: Intent, isNewTask: Boolean): Intent? {
return if (isNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) else intent
}
}
After reading the documentation, I think you can achieve your needs using the following code :
private fun sendSMS(phoneNumber: String, message: String) {
val sentPI: PendingIntent = PendingIntent.getBroadcast(this, 0, Intent("SMS_SENT"), 0)
SmsManager.getDefault().sendTextMessage(phoneNumber, null, message, sentPI, null)
}
Add this permission to your AndroidManifest and make sure it's granted :
<uses-permission android:name="android.permission.SEND_SMS" />
Call sendSMS method as follows :
sendSMS("+2126000000", "Some text here")
Screenshot :

How to emit from a LiveData builder from a non-suspending callback function

I'm new to LiveData and Kotlin Coroutines. I'm trying to use the Chromium Cronet library to make a request from my repository class to return a LiveData object. To return the liveData, I'm using the new LiveData builder (coroutines with LiveData). How would I emit the result from a successful Cronet request?
class CustomRepository #Inject constructor(private val context: Context, private val gson: Gson) : Repository {
private val coroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
override suspend fun getLiveData(): LiveData<List<MyItem>> = liveData(coroutineDispatcher) {
val executor = Executors.newSingleThreadExecutor()
val cronetEngineBuilder = CronetEngine.Builder(context)
val cronetEngine = cronetEngineBuilder.build()
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"http://www.exampleApi.com/example",
CustomRequestCallback(gson),
executor
)
val request: UrlRequest = requestBuilder.build()
request.start()
}
class CustomRequestCallback(private val gson: Gson) : UrlRequest.Callback() {
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
byteBuffer?.flip()
byteBuffer?.let {
val byteArray = ByteArray(it.remaining())
it.get(byteArray)
String(byteArray, Charset.forName("UTF-8"))
}.apply {
val myItems = gson.fromJson(this, MyItem::class.java)
// THIS IS WHAT I WANT TO EMIT
// emit(myItems) doesn't work since I'm not in a suspending function
}
byteBuffer?.clear()
request?.read(byteBuffer)
}
// other callbacks not shown
}
}
The solution involves wrapping the UrlRequest.Callback traditional callback structure in a suspendCoroutine builder.
I also captured my learning in a Medium article which discusses Cronet integration with LiveData and Kotlin Coroutines.
override suspend fun getLiveData(): LiveData<List<MyItem>> = liveData(coroutineDispatcher) {
lateinit var result: List<MyItem>
suspendCoroutine<List<MyItem>> { continuation ->
val requestBuilder = cronetEngine.newUrlRequestBuilder(
"http://www.exampleApi.com/example",
object : UrlRequest.Callback() {
// other callbacks not shown
override fun onReadCompleted(request: UrlRequest?, info: UrlResponseInfo?, byteBuffer: ByteBuffer?) {
byteBuffer?.flip()
byteBuffer?.let {
val byteArray = ByteArray(it.remaining())
it.get(byteArray)
String(byteArray, Charset.forName("UTF-8"))
}.apply {
val myItems = gson.fromJson(this, MyItem::class.java)
result = myItems
continuation.resume(result)
}
byteBuffer?.clear()
request?.read(byteBuffer)
},
executor
)
val request: UrlRequest = requestBuilder.build()
request.start()
}
emit(result)
}