I am trying to call a function from flutter on onTap event and pass some arguments into native android. Please note that I am more of a PHP/JavaScript developer, I am Kotlin virgin.
So far I have managed to do this using:
static const platform = const MethodChannel('foo.bar/example');
void _onTap() {
var arguments = {'name': widget.recipe.name, 'id': widget.recipe.id.toString()};
platform.invokeMethod('setPageView', arguments);
}
Then in the MainActivity.kt
class MainActivity : FlutterActivity() {
private val channel: String = "foo.bar/example"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
val logger = AppEventsLogger.newLogger(this)
MethodChannel(flutterView, channel).setMethodCallHandler(MethodChannel.MethodCallHandler(fun(methodCall: MethodCall, result: MethodChannel.Result) {
if (methodCall.method.equals("setPageView")) {
val params = Bundle()
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, "recipe")
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT, methodCall.argument("recipe"))
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT_ID, methodCall.argument("id"))
logger.logEvent(AppEventsConstants.EVENT_NAME_VIEWED_CONTENT, 0.0, params)
logger.logEvent("pageView", 0.0, params)
System.out.println("Called setPageView.")
}
}))
}
}
The problem is that when I log inside the MethodCallHandler the event is not sent to facebook for some reason. But if I log something right below the
val logger = AppEventsLogger.newLogger(this)
The event is successfully sent to facebook.
The code inside the MethodCallHandler executes, so that´s not the problem.
Any idea what I did wrong?
Thank You
Solved by making the content of MethodCallHandler as a standalone method in the MainActivity class
class MainActivity : FlutterActivity() {
private val channel: String = "foo.bar/example"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, channel).setMethodCallHandler(
MethodChannel.MethodCallHandler(
fun(methodCall: MethodCall, result: MethodChannel.Result) {
if (methodCall.method.equals("setPageView")) {
logPageView(methodCall.argument("name"), methodCall.argument("id"))
}
}
)
)
}
fun logPageView(name: String?, id: String?) {
val logger = AppEventsLogger.newLogger(this)
val params = Bundle()
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, "recipe")
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT, name)
params.putString(AppEventsConstants.EVENT_PARAM_CONTENT_ID, id)
params.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, "EUR")
logger.logEvent(AppEventsConstants.EVENT_NAME_VIEWED_CONTENT, 0.0, params)
}
}
Related
class MainActivity : AppCompatActivity() {
// private var nfcAdapter: NfcAdapter? = null
lateinit var text: Button
#SuppressLint("SuspiciousIndentation")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// nfcAdapter = NfcAdapter.getDefaultAdapter(this);
text = findViewById(R.id.helo) as Button
text.setOnClickListener {
val intent = Intent(this, MyHostApduService::class.java)
Log.e("TAGGG","HELLO")
startService(intent)
}
}
}`
`class MyHostApduService : HostApduService() {
private val SELECT_APDU = byteArrayOf(
0x00.toByte(),
0xA4.toByte(),
0x04.toByte(),
0x00.toByte(),
0x08.toByte(),
0xF0.toByte(),
0x01.toByte(),
0x02.toByte(),
0x03.toByte(),
0x04.toByte(),
0x05.toByte(),
0x06.toByte()
)
private val HELLO_WORLD_APDU = "Hello World".toByteArray()
override fun processCommandApdu(commandApdu: ByteArray?, extras: Bundle?): ByteArray? {
return if (Arrays.equals(SELECT_APDU, commandApdu)) {
HELLO_WORLD_APDU
} else {
ByteArray(0)
}
}
override fun onDeactivated(reason: Int) {}
}
I need to emulate NFC card with specific content/record on my android device. Content on card should be a text something like "t100200". It s just a part of wider project and i could not find working example or tutorial. So i am not sure if is something like this possible. Thanks for any help or advice
I was following the tutorial from
https://proandroiddev.com/suspend-what-youre-doing-retrofit-has-now-coroutines-support-c65bd09ba067.
I am having difficulty to understand how to send parameters to get request from MainActivity
Webservice.kt
interface Webservice {
#GET("/todos/{id}")
suspend fun getTodo(#Path(value = "id") todoId: Int): Todo
}
TodoRepository.kt
class TodoRepository {
var client: Webservice = RetrofitClient.webservice
suspend fun getTodo(id: Int) = client.getTodo(id)
}
MainViewModel.kt
class MainViewModel : ViewModel() {
val repository: TodoRepository = TodoRepository()
val firstTodo = liveData(Dispatchers.IO) {
val retrivedTodo = repository.getTodo(1)
emit(retrivedTodo)
}
}
MainAcitvity.kt
class MainActivity : AppCompatActivity() {
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
showFirstTodo()
}
private fun showFirstTodo() {
viewModel.getFirstTodo().observe(this, Observer {
titleTextView.text = it.title
})
}
}
You can change your viewModel code something like this
private val _todo = MutableLiveData<Todo>()
val todo : LiveData<Todo> get() =_todo
//Call this method from activity
fun getTodo(arg : Int)
{
val result = //Call Coroutines here
_todo.postValue(result)
}
There is a difference between Path and Query in GET request.
You can easily pass query string like this:
interface Webservice {
#GET("/todos/{id}")
suspend fun getTodo(#Path(value = "id") todoId: Int, #Query("name") name: String?): Todo
}
I want to get a variable from an activity and use it in another class.
This variable will be filled by an user in a editText that is called editTextSerie
override fun searchSeries(listener: OnDataListener) {
val retrofit = Retrofit.Builder().addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.themoviedb.org/3/")
.build()
val client = retrofit.create(MovieDBApiInterface::class.java)
val objetoClasse1 = SearchActivity()
var nomeS = objetoClasse1.editTextSerie.text.toString().trim()
val responseCall = client.searchSeries("API_KEY", "pt-BR", nomeS)
responseCall.enqueue(object : Callback<AllSeriesResponse> {
override fun onResponse(call: Call<AllSeriesResponse>?, response1: Response<AllSeriesResponse>?) {
listener.onSuccess(response1!!.body()!!.results)
}
override fun onFailure(call: Call<AllSeriesResponse>?, t: Throwable?) {
listener.onFailure(t!!.message.toString())
}
})
}
This function "searchSeries" is from the class "Series".
I want to get the "editTextSerie" from another class called "Search Activity",
so i created the variable "nomeS" to receive the value of it.
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSerie = editTextSerie.text.toString().trim()
}
}
}
I want to receive this value (value of editTextSerie comes from the XML of SearchActivity ) and use it at responseCall with the "nomeS" variable
What is OnDataListener? Not really sure it is interface or abstract class, so I' ll write some pseudo code.
First change your function searchSeries's params to
searchSeries(text: String, listener: OnDataListener)
So in the class Series, you can get the data in your function searchSeries:
override fun searchSeries(text: String, listener: OnDataListener) {
// ...
// you can get the "text" string
}
Then edit your SearActivity's listener:
class SearchActivity : AppCompatActivity() {
var botaoSearch: AppCompatImageButton? = null
// create class "Series"
val series = Series()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
botaoSearch = findViewById(R.id.btn_search)
botaoSearch?.setOnClickListener {
var nomeSeries = editTextSerie.text.toString().trim()
searchSeries(nomeSeries)
}
}
private fun searchSeries(text: String) {
series.searchSeries(text, object : OnDataListener {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})
}
}
If OnDataListener is a abstract class:
series.searchSeries(text, object : OnDataListener() {
override onSuccess(a0: ...) {
}
override onFailure(message: String) {
}
})
For every Fragment class I make, I add something like this:
companion object {
private const val PARAMETER_1 = "parameter1"
private const val PARAMETER_2 = "parameter2"
fun newInstance(parameter1: String, parameter2: Int) = MyDialog().apply {
arguments = bundleOf(
PARAMETER_1 to parameter1,
PARAMETER_2 to parameter2)
}
}
And then I add:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = arguments ?: return
property1 = args[PARAMETER_1]
property2 = args[PARAMETER_2]
}
This isn't horrific. But it is boilerplate that it would be great to get rid of.
Here's my attempt so far:
abstract class BaseFragment : Fragment() {
abstract val constructorArguments: List<KMutableProperty<*>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val args = arguments ?: return
constructorArguments.forEach {
val key = keyPrefix + it.name
val argument = args.get(key)
val clazz = it.javaClass
val typedArgument = clazz.cast(argument)
it.setter.call(typedArgument)
}
}
companion object {
const val keyPrefix = "ARGUMENT_"
fun newInstance(fragment: BaseFragment, vararg parameters: Any): BaseFragment {
val constructorArguments = fragment.constructorArguments
val parameterMap = mutableListOf<Pair<String, Any?>>()
constructorArguments.forEachIndexed { index, kMutableProperty ->
val key = keyPrefix + kMutableProperty.name
val parameter = parameters[index]
parameterMap.add(Pair(key, parameter))
}
val args = bundleOf(*parameterMap.toTypedArray())
fragment.arguments = args
return fragment
}
}
}
And then, in the actual fragment I can just have:
class MyFragment : BaseFragment() {
lateinit var myProperty: String
override val constructorArguments = listOf<KMutableProperty<*>>(
::myProperty
)
companion object {
fun newInstance(argument: String) = BaseFragment.newInstance(MyFragment(), argument)
}
}
This approach is far from perfect - especially the:
val parameter = parameters[index]
Does anyone know a better way to do this? Do you have some suggestions for how my approach can be improved? Or is this whole idea doomed to fail, and have I wasted a morning?
An 'answer' to this question is to use the Android Jetpack Navigation library. It provides SafeArgs, which greatly simplifies passing arguments to Fragments. See:
https://developer.android.com/guide/navigation/navigation-pass-data#Safe-args
You can have a base fragment that defines a common args parameter
abstract class BaseFragment : Fragment() {
companion object {
const val ARGS_KEY = "__ARGS__"
}
fun <T: Parcelable> getArgs(): T = requireArguments().getParcelable(ARGS_KEY)
fun putArgs(args: Parcelable): Bundle = (arguments ?: Bundle()).apply {
putParcelable(ARGS_KEY, args)
}
}
Then
#Parcelize data class Args(val parameter1: String, val parameter2: Int)
companion object {
fun newInstance(args: Args) = MyDialog().apply {
putArgs(args)
}
}
And now you can do it like
class MyFragment: BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val args: Args = getArgs()
args.parameter2
}
}
I'm trying to create my custom AlertDialog, and get an exception of "requestFeature() must be called before adding content"
Is there somebody can help me???
CustomAlertDialog :
class CustomConfirmDialog(
internal var context: Context,
// val title: String,
val msg: String,
val listener: OnConfirmListener
) : AlertDialog(context) {
interface OnConfirmListener {
fun onConfirmClick()
fun onCancelClick()
}
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
setContentView(R.layout.confirm_dialog)
// tvDialogTitle.text = title
tvDialogMsg.text = msg
btnCancel.setOnClickListener {
listener.onCancelClick()
if (isShowing)
dismiss()
}
btnOK.setOnClickListener {
listener.onConfirmClick()
if (isShowing)
dismiss()
}
}
fun setButtonText(strConfirm: String, strCancel: String) {
btnOK?.text = strConfirm
btnCancel?.text = strCancel
}
}
Here is the calling code
private fun stopAcc() {
val msg = ResUtils.getStringFromRes(R.string.acc_stop_acc_prompt_msg)
CustomConfirmDialog(activity!!, msg, listener).apply {
setButtonText("stopAcc", "startGame")
setCancelable(false)
show()
}
}