I need to create a Flutter plugin and link native Android code from ADA bot. I have no experience with android but by following the documentation, it should be simple:
val adaView = AdaEmbedView(getContext())
val adaSettings = AdaEmbedView.Settings.Builder("ada-example").build()
adaView.initialize(adaSettings)
val dialog = AdaEmbedDialog()
adaDialog.arguments = Bundle().apply {
putParcelable(AdaEmbedDialog.ARGUMENT_SETTINGS, settings)
}
dialog.show(supportFragmentManager, AdaEmbedDialog.TAG)
When I run this code I get the error:
Failed to handle method call
java.lang.IllegalStateException: FragmentManager has not been attached to a host.
I write the code in the flutter create plugin autogenerated file FlutterAdaPlugin.kt and it looks like this:
public class FlutterAdaPlugin: FlutterPlugin, ActivityAware, MethodCallHandler, PluginRegistry.ActivityResultListener, FragmentActivity() {
private lateinit var methodChannel : MethodChannel
private lateinit var eventChannel : EventChannel
private lateinit var eventSink : EventChannel.EventSink
private lateinit var adaFramerwork: AdaEmbedView
private lateinit var applicationCnt: Context
private var activity: Activity? = null
private lateinit var addaSettings: AdaEmbedView.Settings
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) : Boolean {
super.onActivityResult(requestCode, resultCode, data)
return true
}
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
onAttachedToEngine(flutterPluginBinding.applicationContext, flutterPluginBinding.binaryMessenger)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.applicationCnt = applicationContext
}
private fun onAttachedToEngine(applicationContext: Context, messenger: BinaryMessenger) {
this.applicationCnt = applicationContext
methodChannel = MethodChannel(messenger, "FlutterAdaPlugin")
eventChannel = EventChannel(messenger, "FlutterAdaPluginStream")
methodChannel.setMethodCallHandler(this)
eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(listener: Any?, eventSink: EventChannel.EventSink) {
this#FlutterAdaPlugin.eventSink = eventSink;
}
})
}
companion object {
#JvmStatic
fun registerWith(registrar: Registrar) {
val plugin = FlutterAdaPlugin()
plugin.activity = registrar.activity()
registrar.addActivityResultListener(plugin)
plugin.onAttachedToEngine(registrar.context(), registrar.messenger())
}
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
when (call.method) {
"launchNavWebSupport" -> launchNavWebSupport()
}
else -> result.notImplemented()
}
}
private fun launchNavWebSupport() {
setupAdaFramework()
val dialog = AdaEmbedDialog()
dialog.arguments = Bundle().apply {
putParcelable(AdaEmbedDialog.ARGUMENT_SETTINGS, addaSettings)
}
if(supportFragmentManager != null) {
}
dialog.show(supportFragmentManager, AdaEmbedDialog.TAG)
}
private fun setupAdaFramework() {
val activity: Context = activity?.takeIf { activity != null } ?: return
adaFramerwork = AdaEmbedView(activity)
addaSettings = AdaEmbedView.Settings.Builder("test")
.build()
adaFramerwork.initialize(addaSettings)
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
eventChannel.setStreamHandler(null)
methodChannel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
binding.addActivityResultListener(this)
}
override fun onDetachedFromActivity() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
binding.addActivityResultListener(this)
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
}
Related
I want to write unit tests for this presenter class HomePresenter in Android Studio. However, I'm having trouble creating tests to cover both the geoQueryOnUserLocation method and the geoQueryOnSignLocation method due to using GeoFire and Firebase.
I want to test this class using Mockito. Is there a good way to test this class?
HomePresenter.kt:
class HomePresenter(val view: HomeView, val context: Context, val activity: FragmentActivity) {
private val mFirebaseDatabase = FirebaseDatabase.getInstance()
private val mDatabaseReference = mFirebaseDatabase.reference
private var mFirstSignKey: String? = "first_sign_key"
private var mLastSignKey: String? = "last_sign_key"
private var mGeoQueryOnSignLocation: GeoQuery? = null
private var mGeoQueryOnMyLocation: GeoQuery? = null
fun geoQueryOnUserLocation(lastLocation: Location?, radius: Double?, userAddress: String?) {
mGeoQueryOnMyLocation =
GeoFire(mDatabaseReference.child(CHILD_GEOFIRE_TYPE_1)).queryAtLocation(
GeoLocation(
lastLocation?.latitude!!,
lastLocation.longitude
), radius?.div(1000)!!
)
mGeoQueryOnMyLocation?.addGeoQueryEventListener(object : GeoQueryEventListener {
override fun onKeyEntered(key: String?, location: GeoLocation?) {
mFirstSignKey = key
if (mFirstSignKey != mLastSignKey) {
mDatabaseReference.child(CHILD_SIGNS).child(key!!)
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val sign = dataSnapshot.getValue(Sign::class.java)
if (sign?.address == userAddress) {
view.onRetrieveLastSign(sign, key, TAG_SIGN_CAT_1)
mLastSignKey = sign?.signId
}
}
override fun onCancelled(databaseError: DatabaseError) {
view.onFailure(databaseError.message, "")
}
})
}
}
override fun onGeoQueryError(error: DatabaseError) {
view.onFailure(error.message, "")
}
override fun onGeoQueryReady() {}
override fun onKeyMoved(key: String?, location: GeoLocation?) {}
override fun onKeyExited(key: String?) {}
})
}
fun geoQueryOnSignLocation(listSign: ArrayList<Sign>) {
if (mGeoQueryOnSignLocation != null) {
mGeoQueryOnSignLocation?.removeAllListeners()
}
listSign.iterator().forEach {
if (it.type == 0 || it.type == 1) {
mGeoQueryOnSignLocation =
GeoFire(mDatabaseReference.child(CHILD_GEOFIRE_TYPE_0)).queryAtLocation(
GeoLocation(it.latitude, it.longitude),
0.015
)
mGeoQueryOnSignLocation?.addGeoQueryEventListener(object : GeoQueryEventListener {
override fun onKeyEntered(key: String?, location: GeoLocation?) {
view.onRetrieveLastSign(it, key as String, TAG_SIGN_CAT_0_ENTERED)
}
override fun onKeyExited(key: String?) {
view.onRetrieveLastSign(it, key as String, TAG_SIGN_CAT_0_EXITED)
}
override fun onGeoQueryError(error: DatabaseError) {
view.onFailure(error.message, "")
}
override fun onKeyMoved(key: String?, location: GeoLocation?) {}
override fun onGeoQueryReady() {}
})
}
}
}
}
HomeFragment.kt:
class HomeFragment : Fragment(), HomeView {
private lateinit var mPresenter: HomePresenter
private var mUserLastLocation: Location? = null
private var mUserLastSpeed: Float = 0.0f
private var mUserLastRadius: Double? = null
override fun onRetrieveLastLocation(lastLocation: Location, address: String?) {
mUserLastLocation = lastLocation
mUserLastSpeed = mUserLastLocation?.speed!!
mUserLastRadius = mUserLastSpeed.times(mPrefNotificationTimeSignCat1 as Int).toDouble()
mPresenter.geoQueryOnUserLocation(mUserLastLocation, mUserLastRadius, address)
}
override fun onLoadAllSigns(signs: ArrayList<Sign>) {
mPresenter.updateSignLocationsToGeoFire(signs)
mPresenter.geoQueryOnSignLocation(signs)
}
override fun onRetrieveLastSign(sign: Sign?, key: String, tag: String) {
if (sign?.headingQuadrant != mUserLastLocation?.let { userBearingQuadrant(it) }) {
if (tag == TAG_SIGN_CAT_1) {
...
}
if (tag == TAG_SIGN_CAT_0_ENTERED) {
...
}
if (tag == TAG_SIGN_CAT_0_EXITED) {
...
}
}
}
private fun userBearingQuadrant(location: Location): String {
return if (location.bearing in 0.0..90.0) {
"I"
} else if (location.bearing > 90 && location.bearing <= 180) {
"II"
} else if (location.bearing > 180 && location.bearing <= 270) {
"III"
} else {
"IV"
}
}
}
I am using exoplayer for playing videos .And for this we are used Fragment instance with pagerstateadapter and viewpager2.
But when scroll fast previous played video sound listen in background as well as in screen video means mix the sound in same video player.
Please help me how to solve this.
1.State adapter
class StoriesPagerAdapter(
fragment: Fragment,
val onClick1: VideoItemAdapter.OnItemClicked?,
val onlikeClick: VideoItemAdapter.OnLikeCLicked?,
val onFollowClick: VideoItemAdapter.OnFollowCLicked?,
val ontrendingClick: VideoItemAdapter.OnTrendCLicked?,
val oniconCLick: VideoItemAdapter.OnIconClick?) : FragmentStateAdapter(fragment) {
val dataList:MutableList<Gettrendingvideos.Data.Postlist>=mutableListOf()
override fun getItemCount(): Int {
return dataList.size
}
fun addAll(movies: MutableList<Gettrendingvideos.Data.Postlist>) {
for (movie in movies) {
add(movie)
}
}
fun add(moive: Gettrendingvideos.Data.Postlist) {
dataList.add(moive)
notifyItemInserted(dataList.size - 1)
}
override fun createFragment(position: Int): Fragment {
return StoryViewFragment.newInstance(
onClick1,
onlikeClick,
onFollowClick,
ontrendingClick,
oniconCLick,
dataList[position]
)
}}
2 Fragment
class StoryViewFragment : Fragment(), CommentFragment.onCommentCountIncrease {
private var storyUrl: String? = null
private var storiesDataModel: Gettrendingvideos.Data.Postlist? = null
lateinit var mView: View
private var simplePlayer: SimpleExoPlayer? = null
private var cacheDataSourceFactory: CacheDataSourceFactory? = null
private val simpleCache = MainApplication.simpleCache
private var toPlayVideoPosition: Int = -1
lateinit var viewModel: MainViewModel
lateinit var preferences: SecurePreferences
private var bool: Boolean? = false
var onItemClick: VideoItemAdapter.OnItemClicked? = null
var onlikeCLicked: VideoItemAdapter.OnLikeCLicked? = null
var onFollowCLicked: VideoItemAdapter.OnFollowCLicked? = null
var onTrendCLicked: VideoItemAdapter.OnTrendCLicked? = null
var onIconClick: VideoItemAdapter.OnIconClick? = null
lateinit var huserId: String
lateinit var token: String
companion object {
fun newInstance(
itemClicked: VideoItemAdapter.OnItemClicked?,
likeCLicked: VideoItemAdapter.OnLikeCLicked?,
onFollowCLicked: VideoItemAdapter.OnFollowCLicked?,
onTrendCLicked: VideoItemAdapter.OnTrendCLicked?,
onIconClick: VideoItemAdapter.OnIconClick?,
storiesDataModel: Gettrendingvideos.Data.Postlist
) = StoryViewFragment()
.apply {
arguments = Bundle().apply {
putParcelable(Constants.KEY_STORY_DATA, storiesDataModel)
}
this.onItemClick = itemClicked
this.onlikeCLicked = likeCLicked
this.onFollowCLicked = onFollowCLicked
this.onTrendCLicked = onTrendCLicked
this.onIconClick = onIconClick
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
mView = inflater.inflate(
R.layout.layout_main,
container,
false
)
return mView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(this, ViewModelFactory(RetrofitBuilder.apiService))
.get(MainViewModel::class.java)
preferences =
SecurePreferences(
requireActivity(),
AppConstants.preferenceName,
AppConstants.USER_DETAILS,
true
)
storiesDataModel = arguments?.getParcelable(Constants.KEY_STORY_DATA)
setData()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
if (!preferences.getString(AppConstants.USER_ID).equals(null)) {
huserId = preferences.getString(AppConstants.USER_ID)!!
Log.d("TAG", "onActivityCreated: $huserId")
}
}
#SuppressLint("SetTextI18n")
private fun setData() {
Log.d("TAG", "setData: $storiesDataModel")
mView.textview2.text = storiesDataModel?.user_name
mView.like_count.text = storiesDataModel?.total_likes.toString()
comment_count.text = storiesDataModel?.total_comments.toString()
mView.textview.text = storiesDataModel?.type
Glide.with(this).load(storiesDataModel?.user_profile_pic).placeholder(R.drawable.profile).into(mView.image)
if (storiesDataModel?.is_like == 0) {
mView.imageView4.setImageResource(R.drawable.ic_like)
} else {
mView.imageView4.setImageResource(R.drawable.ic_like_red)
}
if (storiesDataModel?.is_following!! == 0) {
mView.textview3.text = "Follow"
} else {
mView.textview3.text = "Following"
}
if (storiesDataModel?.user_id.toString()==preferences.getString(AppConstants.USER_ID)) {
mView.textview3.visibility = View.GONE
}
image.setOnClickListener {
if (preferences.getString(AppConstants.token).equals(null)) {
MainActivity().show(childFragmentManager, "")
} else {
preferences.put(
AppConstants.OtherProfile_UserId,
storiesDataModel?.user_id.toString()
)
}
}
val simplePlayer = getPlayer()
player_view_story.player = simplePlayer player_view_story.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL)
simplePlayer?.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
storyUrl = storiesDataModel?.video_url
prepareMedia(storiesDataModel)
}
override fun onPause() {
pauseVideo()
super.onPause()
}
override fun onResume() {
restartVideo()
super.onResume()
}
override fun onDestroy() {
releasePlayer()
super.onDestroy()
}
private fun pausePlayer() {
simplePlayer?.setPlayWhenReady(false)
simplePlayer?.getPlaybackState()
}
private fun startPlayer() {
simplePlayer?.setPlayWhenReady(true)
simplePlayer?.getPlaybackState()
}
private val playerCallback: Player.EventListener? = object : Player.EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
}
override fun onPlayerError(error: com.google.android.exoplayer2.ExoPlaybackException?) {
super.onPlayerError(error)
}
}
private fun prepareVideoPlayer() {
simplePlayer = ExoPlayerFactory.newSimpleInstance(context)
cacheDataSourceFactory = CacheDataSourceFactory(
simpleCache,
DefaultHttpDataSourceFactory(
Util.getUserAgent(
context,
"exo"
)
)
)
}
private fun getPlayer(): SimpleExoPlayer? {
if (simplePlayer == null) {
prepareVideoPlayer()
}
return simplePlayer
}
private fun prepareMedia(datamodel: Gettrendingvideos.Data.Postlist?{
val uri = Uri.parse(datamodel?.video_url)
simplePlayer.repeatMode = Player.REPEAT_MODE_ONE
simplePlayer.playWhenReady = true
if (storiesDataModel!!.type == "following") {
following_page.typeface = Typeface.DEFAULT_BOLD
trending_page.setTypeface(null, Typeface.NORMAL)
} else {
following_page.setTypeface(null, Typeface.BOLD)
}
if (storiesDataModel.type == "treading") {
trending_page.typeface = Typeface.DEFAULT_BOLD
following_page.setTypeface(null, Typeface.NORMAL)
} else {
trending_page.setTypeface(null, Typeface.BOLD)
}
if (simplePlayer.playWhenReady == true) {
}
simplePlayer.addListener(playerCallback)
toPlayVideoPosition = -1
}
private fun setArtwork(drawable: Drawable, playerView: PlayerView) {
playerView.useArtwork = true
playerView.defaultArtwork = drawable
}
private fun playVideo() {
simplePlayer.playWhenReady = true
}
private fun restartVideo() {
if (simplePlayer == null) {
prepareMedia(storiesDataModel)
} else {
simplePlayer.seekToDefaultPosition()
simplePlayer.playWhenReady = true
}
}
private fun pauseVideo() {
simplePlayer.playWhenReady = false
}
private fun releasePlayer() {
simplePlayer.stop(true)
simplePlayer.release()
}}
override fun setMenuVisibility(menuVisible: Boolean) {
if (!menuVisible){
simplePlayer?.playWhenReady = false
simplePlayer?.pause()
}
super.setMenuVisibility(menuVisible)
}
JUST ADD THIS IN YOUR StoryViewFragment.
I try to implement Custom Tab Intent with warmup capability. So it needs to create CustomTabsServiceConnection.
However, when I bind the service it always return false.
Please check my implementation below:
// Custom chrome tabs
private lateinit var customTabsIntent: CustomTabsIntent
private lateinit var customTabsClient: CustomTabsClient
private var connection: CustomTabsServiceConnection? = null
override fun onStart() {
super.onStart()
val ok = CustomTabsClient.bindCustomTabsService(this, CUSTOM_TAB_PACKAGE_NAME, ServiceConnection())
Timber.i("Is OK = $ok")
}
inner class ServiceConnection: CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(
name: ComponentName,
client: CustomTabsClient
) {
customTabsClient = client
customTabsClient.warmup(0)
// Connection callback
val session = customTabsClient.newSession(object : CustomTabsCallback() {
override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
super.onNavigationEvent(navigationEvent, extras)
Timber.i("Event = $navigationEvent")
}
})
if (session != null) {
session.mayLaunchUrl(Uri.parse(loginUrl), null, null)
// Init custom tab intent
val customTabBuilder = CustomTabsIntent.Builder(session)
customTabsIntent = customTabBuilder.build()
customTabsIntent.launchUrl(this, Uri.parse(loginUrl))
}
}
override fun onServiceDisconnected(name: ComponentName?) {
connection = null
}
}
override fun onStop() {
super.onStop()
connection?.let {
this.unbindService(it)
}
}
Any idea about this?
Currently, I am trying to implement facebook-login in combination with cloud firestore inside my app. Everything works fine like it should (login, registration, Firebase.auth.currentUser != null).
The only problem I have is that when I log out of my app and then log in again with facebook I get the message "Up to now you have registered with xyz via Facebook. Do you want to continue."
I don't know why this message is coming. Even deleting the created facebook account from my cloud-firestore did not resolve the problem.
Here is a screenshot:
My Login Process
Fragment
class UserLogInRegistrationFragment : Fragment(R.layout.fragment_user_log_in_registration) {
private val viewModel: LoginViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Register Facebook Callback
viewModel.onEvent(LoginRegistrationEvent.RegisterFacebookCallback)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initExternalLogins()
observeSignInOptions()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
viewModel.onEvent(LoginRegistrationEvent.SignInWithFacebook(requestCode, resultCode, data))
}
override fun onDestroyView() {
super.onDestroyView()
// Unregister Facebook Callback
viewModel.onEvent(LoginRegistrationEvent.UnregisterFacebookCallback)
}
private fun initExternalLogins() {
facebookSignInBtn.setOnClickListener { viewModel.onEvent(LoginRegistrationEvent.OnStartFacebookSignIn) }
}
private fun observeSignInOptions() {
viewModel.signInOption.observe(viewLifecycleOwner) { event ->
when(event) {
is SignInOption.FacebookSignIn -> {
// Starting Login Process. This will be executed by the viewmodel
facebookManager().logInWithReadPermissions(this, listOf("email", "public_profile"))
}
}
}
}
}
Viewmodel
class LoginViewModel #Inject constructor(
private val loginValidator: LoginValidator
) : ViewModel() {
val signInOption = loginValidator.signInOption
val signInResult = loginValidator.signInResult
fun onEvent(event: LoginRegistrationEvent) {
when (event) {
LoginRegistrationEvent.RegisterFacebookCallback -> viewModelScope.launch { loginValidator.registerFacebookCallback() }
LoginRegistrationEvent.UnregisterFacebookCallback -> loginValidator.unregisterFacebookCallback()
LoginRegistrationEvent.OnStartFacebookSignIn -> loginValidator.startFacebookSignIn()
is LoginRegistrationEvent.SignInWithFacebook -> viewModelScope.launch {
loginValidator.handleFacebookSignInActivity(event.requestCode, event.resultCode, event.data)
}
}
}
}
LoginValidator (Use Case)
class LoginValidator #Inject constructor(
private val loginRepository: LoginRepository,
) {
private val _signInOption = MutableLiveData<SignInOption>()
val signInOption: LiveData<SignInOption> get() = _signInOption
private val _signInResult = MutableLiveData<LoginStateEvent>()
val signInResult: LiveData<LoginStateEvent> get() = _signInResult
fun startFacebookSignIn() {
_signInOption.value = SignInOption.FacebookSignIn
}
suspend fun registerFacebookCallback() {
loginRepository.signInWithFacebook().collect {
_signInResult.value = it
}
}
fun unregisterFacebookCallback() {
loginRepository.unregisterFacebookCallback()
}
fun handleFacebookSignInActivity(requestCode: Int, resultCode: Int, data: Intent?) {
loginRepository.handleFacebookSignInActivity(requestCode, resultCode, data)
}
fun signOut() = loginRepository.signOutUser()
}
LoginRepository
class LoginRepositoryImpl #Inject constructor(
#ApplicationContext private val context: Context,
private val callbackManager: CallbackManager,
private val dbAuth: FirebaseAuth,
private val dbFirestore: FirebaseFirestore
) : LoginRepository {
override fun isUserLogedIn(): Boolean = when (dbAuth.currentUser) {
null -> false
else -> true
}
override fun signOutUser() {
dbAuth.signOut()
facebookManager().logOut()
}
#ExperimentalCoroutinesApi
override suspend fun signInWithFacebook(): Flow<LoginStateEvent> = flow {
val result = facebookManager().registerMCallback(callbackManager)
emit(LoginStateEvent.Loading(context.getString(R.string.login_registration_progress_verify_facebook)))
val credential = FacebookAuthProvider.getCredential(result.accessToken.token)
emit(LoginStateEvent.Loading(context.getString(R.string.login_registration_progress_loggin_in)))
dbAuth.signInWithCredential(credential).await()
emit(LoginStateEvent.Loading(context.getString(R.string.login_regisration_progress_verifiy_rsb)))
createUserIfNotExist(
User(
eMail = dbAuth.currentUser!!.email ?: "",
lastName = splitToLastName(dbAuth.currentUser!!.displayName) ?: "",
firstName = splitToFirstName(dbAuth.currentUser!!.displayName) ?: "",
oAuth = OAuth.FACEBOOK
)
)
emit(LoginStateEvent.LoggedIn)
}.catch { e ->
emit(convertExceptionToState(e as Exception))
}
override fun handleFacebookSignInActivity(requestCode: Int, resultCode: Int, data: Intent?) {
callbackManager.onActivityResult(requestCode, resultCode, data)
}
override fun unregisterFacebookCallback() {
facebookManager().unregisterCallback(callbackManager)
}
Extension Function for Facebook Login
#ExperimentalCoroutinesApi
suspend fun LoginManager.registerMCallback(
manager: CallbackManager
) = suspendCancellableCoroutine<LoginResult> { cont ->
val callback = object : FacebookCallback<LoginResult> {
override fun onSuccess(result: LoginResult) = cont.resume(result, cont::resumeWithException)
override fun onCancel() { }
override fun onError(error: FacebookException?) {
error?.let { cont.resumeWithException(it) }
}
}
registerCallback(manager, callback)
}
fun facebookManager(): LoginManager = LoginManager.getInstance()
Callback Manager
#Provides
fun provideCallbackManager(): CallbackManager = CallbackManager.Factory.create()
Sign-In Flow
https://imgur.com/a/ZOjV5RZ
Dependencies I use
implementation "com.google.firebase:firebase-auth-ktx"
implementation 'com.facebook.android:facebook-login:8.1.0'
I have tried to stick to the cloud firestore documentation here and I've read this Stackoverflow post.
I develop a Flutter plugin, the problem is FlutterPluginBinding.flutterEngine to get FlutterEngine is deprecated. Full code is below:
class AMPlugin : FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener {
private lateinit var channel: MethodChannel
private var activity: Activity? = null
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val engine = flutterPluginBinding.flutterEngine
channel = MethodChannel(engine.dartExecutor, "aM")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
when (call.method) {
else -> result.notImplemented()
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
return true
}
override fun onDetachedFromActivity() {
activity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
activity = null
}
companion object {
#Suppress("UNUSED")
#JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "aM")
channel.setMethodCallHandler(AMPlugin())
}
}
}
But I can't understand how to get FlutterEngine or initialize MethodChannel in another way. I tried as val engine = FlutterEngine(binding.applicationContext) but it leads to the crash, seems it requires Activity context.
How to replace this deprecated API?
replace:
channel = MethodChannel(engine.dartExecutor, "aM")
with
channel = MethodChannel(flutterPluginBinding.getBinaryMessenger(), "aM")