How to set an alarm with jetpack compose? - android-jetpack-compose

I am triyng to set an alarm with Jetpack Compose but does not work i am triyng to test with emulator and with app running on main thread, this is my code:
class MainActivity : ComponentActivity() {
lateinit var navController: NavHostController
private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context= LocalContext.current
navController = rememberNavController()
NotePadReminderTheme {
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY,6)
set(Calendar.MINUTE,34)
}
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val intent = Intent(context, AlarmReceiver::class.java)
val pendingIntent =
PendingIntent.getService(context, 3, intent,0)
alarmManager?.setExact(AlarmManager.ELAPSED_REALTIME,calendar.timeInMillis,pendingIntent)
// MainPage(navController =navController )
}
}
}
}
And this is my Manifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<application
android:allowBackup="true"
android:dataExtractionRules="#xml/data_extraction_rules"
android:fullBackupContent="#xml/backup_rules"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/Theme.NotePadReminder"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="#string/app_name"
android:theme="#style/Theme.NotePadReminder">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.lib_name"
android:value="" />
</activity>
<receiver android:name="com.mobile.notepadreminder.AlarmReceiver" />
</application>
</manifest>
And this is my AlarmReceiver class:
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("alarm","received")
}
}
I do not know why does not work.
My problem is that I set the alarm and the alarm does not run never.

The only thing I needed was to change the getService method to getBroadcast but I don't know why
class MainActivity : ComponentActivity() {
lateinit var navController: NavHostController
private var alarmMgr: AlarmManager? = null
private lateinit var alarmIntent: PendingIntent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context= LocalContext.current
navController = rememberNavController()
NotePadReminderTheme {
val calendar: Calendar = Calendar.getInstance().apply {
timeInMillis = System.currentTimeMillis()
set(Calendar.HOUR_OF_DAY,6)
set(Calendar.MINUTE,34)
}
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val intent = Intent(context, AlarmReceiver::class.java)
val pendingIntent =
PendingIntent.getBroadcast(context, Date().seconds, intent, PendingIntent.FLAG_IMMUTABLE)
alarmManager?.setExact(AlarmManager.ELAPSED_REALTIME,calendar.timeInMillis,pendingIntent)
// MainPage(navController =navController )
}
}
}
}

Related

hiltViewModel() function gives a new instance of the same composable function every time navigating to it

I've got a simple navhost that looks like this:
#Composable
fun NavigationComponent(
navController: NavHostController,
context: Context,
) {
NavHost(
navController = navController,
startDestination = Screen.MainScreen.route
) {
composable(Screen.MainScreen.route) {
MainScreen(navController = navController)
}
composable(
route = Screen.NewPlanScreen.route,
) {
NewPlanScreen(context = context, navController = navController)
}
}
}
It is called from main activity like this:
#AndroidEntryPoint
class MainActivity : ComponentActivity() {
#OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = this
val navController: NavHostController = rememberNavController()
MainTheme(
darkTheme = isDarkMode.value
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(AppTheme.colors.primaryBackground)
) {
NavigationComponent(navController = navController, context = context)
}
}
}
}
}
NewPlanScreen has a viewmodel that is provided by dagger hilt:
#Composable
fun NewPlanScreen(
viewModel: NewPlanScreenViewModel = hiltViewModel(),
navController: NavController,
context: Context
) {
//...
}
Also, the NewPlanScreenViewModel has a #HiltViewModel annotation and an injected constructor.
And in the MainScreen I use
navController.navigate(Screen.NewPlanScreen.route)
but when I press a back button on a phone to navigate back to main screen and then call the
navController.navigate(Screen.NewPlanScreen.route)
again, a new ViewModel for a NewPlan is created. What am I doing wrong?

what does the "root" field in a view binding class represent?

class MainActivity : AppCompatActivity() {
private lateinit var myBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(myBinding.root)
}
}
myBinding.root refers to the parent view in my xml file which is a ConstraintLayout. This is my xml code
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rootConstraint"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
Now, consider this
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rootConstraint = findViewById<View>(R.id.rootConstraint)
setContentView(rootConstraint)
}
}
when i create a direct reference to the ConstraintLayout using findViewByID and pass it to setContentView, the app crashes. why's that?
Do myBinding.root and rootConstraint not refer to the same view?

Swift how to conform to different associate type in a protocol

I am developing a state management library. The original design only has 1 listener, which works great until I need to support multiple listeners.
The original design is here:
Swift how to use generic protocol in generic class
This is what I have done to support multiple listeners:
public protocol StateObserver: AnyObject {
associatedtype State
func didUpdateState(_ state: State)
}
public final class StateStore<Observer: StateObserver> {
struct WeakRef<T: AnyObject> {
weak var value: T?
}
public private(set) var state: Observer.State
private var observers = [WeakRef<Observer>]()
public init(initialState: Observer.State) {
state = initialState
}
public func addObservers(_ observers: [Observer]) {
self.observers += observers.map { WeakRef(value: $0) }
}
public func update(_ block: (inout Observer.State) -> Void) {
var nextState = state
block(&nextState)
state = nextState
notify()
}
public func notify() {
for observer in observers {
observer.value?.didUpdateState(state)
}
}
}
Now I need to create the store with 2 observers:
class MyScene: SKScene {
init {
let leftPanel = LeftPanelSKNode()
let topBar = TopBarSKNode()
let store: StateStore<?> // How to make this support `LeftPanelSKNode `, `TopBarSKNode`, and `MyScene`?
store.addObservers([leftPanel, topBar, self])
}
Now I am stuck here. I need to create a StateStore<?> of something, which can be either MyScene, LeftPanelSKNode and TopBarSKNode.
First of all, I have to say that what you are building already exists in many reactive libraries:
CurrentValueSubject in Apple's Combine;
BehaviorSubject in RxSwift;
You can also check the small internal class I've made myself, it allows to hold the state and observe it ObservableProperty.
Back to your question, I've found a way to add the StateObserver one by one while keeping only the weak reference to them.
public protocol StateObserver: AnyObject {
associatedtype State
func didUpdateState(_ state: State)
}
class Node1: StateObserver {
typealias State = Int
func didUpdateState(_ state: Int) { }
}
class Node2: StateObserver {
typealias State = Int
func didUpdateState(_ state: Int) { }
}
class StateStore<StateType> {
private(set) var state: StateType
init(_ initialState: StateType) {
self.state = initialState
}
private var observers: [(StateType) -> Void] = []
func observe<Observer: StateObserver>(by observer: Observer) where Observer.State == StateType {
weak var weakObserver = observer
observers.append { state in
weakObserver?.didUpdateState(state)
}
}
func notify() {
observers.forEach {
$0(self.state)
}
}
}
let store = StateStore<Int>(0)
let node1 = Node1()
let node2 = Node2()
store.observe(by: node1)
store.observe(by: node2)
Adding the array-based observe API might be a problem because of the associatedtype in the StateObserver.

kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen <object> when using ktor in Kotlin Multiplatform (iOS)

I am trying to build a simple Kotlin Multiplatform app that calls to the internet to fetch some Strings from the internet with ktor. I took some functions from Kotlin conference app which I compiled and it works fine on both Android and iOS.
However, in my sample app, it only works on Android, but on iOS it returns
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen <object>#c422ffe8
Here is the GitHub repository and below is my code:
// src/commonMain/CoroutinePresenter.kt
open class CoroutinePresenter(
private val mainContext: CoroutineContext, // TODO: Use Dispatchers.Main instead when it will be supported on iOS
private val baseView: BaseView
): CoroutineScope {
private val job = Job()
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
baseView.showError(throwable)
}
override val coroutineContext: CoroutineContext
get() = mainContext + job + exceptionHandler
open fun onDestroy() {
job.cancel()
}
}
--
// src/commonMain/SamplePresenter.kt
class SamplePresenter(
val uiContext: CoroutineContext,
baseView: BaseView,
val sampleView: SampleView
) : CoroutinePresenter(uiContext, baseView) {
private val client = HttpClient()
fun callSimpleApi() {
try {
GlobalScope.launch(uiContext) {
getToolString()
}
} catch (e: Exception) {
sampleView.returnString(e.toString())
}
}
suspend fun getToolString() = client.get<String> {
url("https://tools.ietf.org/rfc/rfc1866.txt")
}
override fun onDestroy() {
super.onDestroy()
}
}
--
// src/iosMain/SampleIos.kt
object MainLoopDispatcher: CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
NSRunLoop.mainRunLoop().performBlock {
block.run()
}
}
}
--
// iosApp/iosApp/ViewController.swift
import app
class ViewController: UIViewController, SampleView, BaseView {
private lazy var presenter: SamplePresenter = { SamplePresenter(
uiContext: MainLoopDispatcher(),
baseView: self,
sampleView: self
)
}()
#IBOutlet weak var label: UILabel!
func showError(error: KotlinThrowable) {
print(error.message)
}
func returnString(result: String) {
label.text = result
print(result)
}
override func viewDidLoad() {
super.viewDidLoad()
print("helo")
presenter.callSimpleApi()
}
}
Turns out that the Kotlin version 1.3.11 is causing the trouble. I have downgraded it to 1.3.10 and it works perfectly fine. ktor will receive a fix in the next minor release.
Source - Kotlin Slack, multiplatform channel.

Dagger 2, can not inject presenter in my activity

I am new in Dagger2.
I have a problem with inject presenter in my activity
I try to resolve my problem after read this article https://android.jlelse.eu/inject-interfaces-without-providing-in-dagger-2-618cce9b1e29 but it was not helpe me. I hope someone help me, I spend all day tring to resolve it...
here is my modules:
#Module
class AppModule(private val appContext: Context) {
#Singleton
#Provides
internal fun provideContext(): Context {
return appContext
}
#Singleton
#Provides
internal fun providePreferences(): SharedPreferences {
return appContext.getSharedPreferences(
appContext.resources.getString(R.string.shared_preferences_name), Context.MODE_PRIVATE)
}
}
#Module
abstract class ActivityModule {
#Binds
abstract fun provideMakeCheckPresenter (p :
MakeCheckPresenter<MakeCheckMvpView>)
: MakeCheckMvpPresenter<MakeCheckMvpView>
}
here is my component:
#Component(modules = { AppModule.class, ActivityModule.class})
#Singleton
public interface AppComponent {
void inject(MakeCheckActivity makeCheckActivity);
}
here is my App class:
class App : android.support.multidex.MultiDexApplication() {
override fun onCreate() {
super.onCreate()
component = buildComponent()
}
protected fun buildComponent(): AppComponent {
return DaggerAppComponent.builder().appModule(AppModule(this)).build()
}
companion object {
var component: AppComponent? = null
private set
}
}
here is my activity:
class MakeCheckActivity : BaseActivity(), MakeCheckMvpView {
#Inject lateinit var presenter: MakeCheckMvpPresenter<MakeCheckMvpView>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.new_check_activity)
App.getComponent().inject(this)
}
}
here is my presenter:
class MakeCheckPresenter<V : MakeCheckMvpView>
#Inject constructor() : BasePresenter<MakeCheckMvpView>(), MakeCheckMvpPresenter<MakeCheckMvpView> {
override fun saveEnterpriseId(enterpriseId: Int) {
//model.enterpriseId
}
}
here is interfaces for presenter and view:
interface MakeCheckMvpView : MvpView {
}
interface MakeCheckMvpPresenter<in V : MakeCheckMvpView> : MvpPresenter<V> {
fun saveEnterpriseId(enterpriseId : Int)
}
and here base classes:
open class BasePresenter<V : MvpView> #Inject constructor(): MvpPresenter<V> {
}
abstract class BaseActivity : AppCompatActivity(), MvpView{
}
and always when i build i have this error:
...\di\component\AppComponent.java:62: error: ..ui.check_making.MakeCheckMvpPresenter<? super ...ui.check_making.MakeCheckMvpView> cannot be provided without an #Provides- or #Produces-annotated method.
e:
e: void inject(MakeCheckActivity makeCheckActivity);
how can I resolve it?

Resources