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
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?
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?
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.
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.
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?