How to Remove the MvvmCross Messenger Subscriptions - android

I am using MvvmCross for my Xamarin.Android Application. I am using Messenger for Communication between my Service and ViewModel. Eg: HomeViewModel has a Subscription for ItemRefresh Method. I used to Publish from Service for any change in Items. Below is the example code snippet.
public class HomeViewModel
{
private MvxSubscriptionToken _updatedItemToken;
public HomeViewModel()
{
_updatedItemToken = messenger.Subscribe<UpdatedItemMessage>(ItemUpdated);
}
public void ItemUpdated(UpdatedItemMessage message)
{
//my code to refresh the page.
}
}
What is the Problem now?
Everything is working fine until I Introduce logout feature.
During Logout, Irrespective of where you are in the app, I need to clear all the activities from navigation stack and navigate to login page
After Logout, If I goes to HomeViewModel, The Subscription added again and the messages doubled up (hitting twice on each publish message).
What I have tried?
Purge the Subscription On Logging out. using _messenger.RequestPurgeAll();
Tried purging individual message like below
if(_messenger.HasSubscriptionsFor())
{
messenger.RequestPurge(typeof(UpdatedItemMessage));
}
Unsubscribe the Message OnDestroy(). It works well on navigations of the page but not during logout. Logout will clear all activities immediately which will not call OnDestroy of every Visible Activities. Logout code is below.
var intent = new Intent(this, typeof(LoginView));
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
StartActivity(intent); Finish();
Any help would be appreciated? I don't understand the use of RequestPurge which should ideally help for me but not.

Have you tried disposing the MvxSubscriptionToken you get from the messenger?
In your case:
_updatedItemToken.Dispose();
This should make the subscription go away.

Related

How to detect interstitial ad is clicked by user?

Is there any way to detect when the user taps on the interstitial ads? I tried but did not find any callbacks for detecting interstitial ad clicks.
Any workaround for detecting the same would also be very helpful.
I want to detect ads click to prevent users from generating fake clicks for interstitial ads.
Google added back the removed interstitialAd callback onAdClicked() on SDK 20.4.0. Looks like they removed it and 6 months later realized they messed up and decided to add it back :)
Added the onAdClicked() callback to FullScreenContentCallback.
See the AdMob SDK release notes for details.
I’ve found that upgrading AdMob SDK to the latest version is a must.
You can utilize the combination of ActivityLifecycleCallbacks and WindowCallback.
The ActivityLifecycleCallbacks enables you to observe every Activity lifecycle event that occurs in your app. All in one place.
The WindowCallback enables you to intercept many window events. One of the events that gets fired by the system that we are particularly interested in is dispatchTouchEvent.
Now, here is the strategy:
Register our GlobalActivityLifecycleListener in the Application class
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(GlobalActivityLifecycleListener())
}
}
Check if the currently displayed Activity is an Ad Activity. If yes, register our AdWindowCallbacks
class GlobalActivityLifecycleListener : Application.ActivityLifecycleCallbacks {
//...
override fun onActivityResumed(activity: Activity) {
if (isAdActivity(activity)) {
registerWindowCallbacks(activity)
}
}
private fun registerWindowCallbacks(activity: Activity) {
val currentWindow = activity.window
/*This is needed to forward the events from our callback back to the original
callback after we are done with the processing*/
val originalCallbacks = currentWindow.callback
currentWindow.callback = AdWindowCallbacks(originalCallbacks)
}
}
Intercept/process user touch events
class AdWindowCallbacks(private val originalCallback: Window.Callback) : Window.Callback {
//...
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
//TODO process user touch event
return originalCallback.dispatchTouchEvent(event)
}
}
From there, you can detect the common gestures and act accordingly.
I understand your point.
But technically there is no way to track the Interstitial ad clicks by
user.
If you closely monitor the behavior, these ads are opened in a new page within your app. You can confirm them by logging onPause in your calling Activity.
And when user performs clicks on these ads, they are taken to respective destination either to Play Store or URL opened in mobile browser depending on the nature of the Ads.
As mentioned in comment section by #Daxesh Vekaria, you can set Frequency capping in your AdMob console
or try some other solution as suggested by #FreeLearning
EDIT 1 :
In worst case, you can try implementing fullScreenContentCallback. But as per the documentation it doesn't provide any click callbacks.

Passing data from MainActivity to sub-Activity when sub-Activity is opened through an Adapter?

So my title may be hard to follow, but I'll try to clarify and expand on what my issue is below.
I currently have an app that starts its life as a MainActivity with multiple Fragments sitting in a ViewPager.
In the MainActivity, I have Android In-App Billing V3 (library) setup so that the user can pay to remove ads. This works just fine in the MainActivity but my issue arises when moving to another Activity.
The first Fragment the user is presented with upon launching the app, contains a RecyclerView with an ArrayList of items. To get to a sub-Activity from the MainActivity, the user presses a button on one of the items in the RecyclerView, which means that the Intent data used to change Activities is contained within the RecyclerViewAdapter.
My issue is that once my app knows that the user has paid to remove ads, I want the app to also remove ads in all sub-Activities as well.
I don't know how to pass this info (that the "Remove Ads" in-app has been purchased) from Activity -> sub-Activity, when sub-Activity is launched through the RVAdapter instead.
So my question is: How would I pass data from MainActivity -> RVAdapter -> Sub-Activity?
Or is there an even better, more efficient way of passing this data along without using Intents? Do let me know!
Did my description of the issue make sense? I hope so! Otherwise let me know how I might clarify it! If you need me to paste in any code, let me know as well.
Thanks for any of your help!
you can use EventBus (greenrobot) nice library for send event this linke
to send evnts
after add library put below method to your main activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event)
don't forget about Register and unregister subscriber, do it like this :
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
finally post your event from everywhere like your subactivity :
EventBus.getDefault().postSticky(new MessageEvent());
Notice:I add postSticky(); to cache data on memory ,Then the sticky event can be delivered to subscribers or queried explicitly.
better solution
but i think you can save value in Sharedpreferences after purcahse:
SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME,
MODE_PRIVATE).edit();
editor.putBoolean("pay", true);
editor.apply();
then check this valu every Activity on onCreat method
to show adds or don't
SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
pay = prefs.getBoolean("pay", false);
if (pay) {
show();
}else dontShow();

The appropriate way to leave an Android app? (Xamarin)

I'm developing an Android using Xamarin. The app has a login modal and I would like the behavior of the back button on the login modal to essentially exit the app, as if you were at the end of the navigation stack.
Is this possible?
If you are displaying the modal on android via a new Activity, override OnBackPressed in the modal activity like so:
public override void OnBackPressed()
{
Intent intent = new Intent(Intent.ActionMain);
intent.AddCategory(Intent.CategoryHome);
intent.SetFlags(ActivityFlags.NewTask);
StartActivity(intent);
}
As the solution that #wishmaster provided does give the appearance of the behavior I was looking for, I marked his as as acceptable.
For completeness though, I did run across a more appropriate solution. There's a MoveTaskToBack() method on the Activity class. So this is what my solution was:
public void CloseApp()
{
var mainActivity = Forms.Context as MainActivity;
if (mainActivity != null)
mainActivity.MoveTaskToBack(true);
}
To explain the context a little more. I'm developing a Xamarin Forms app and this is a method on an interface that's injected into the login Forms page.

Show a splash screen with MvvmCross after Android purged an application from memory

Here is my scenario: when my app gets purged from memory by the OS and its reopened from the recent apps list, it goes straight to the Activity that was last open. When this happens I would like to initialize a couple of business objects.
My first approach to achieve this was to do the initialization in the InitializeLastChance method in the Setup class.
Inside the InitializeLastChance method I check if the current top activity is the MvxSplashScreenActivity. If it's not, I make the necessary initialization. The code is something like this:
protected override void InitializeLastChance()
{
try
{
var topActivity = Mvx.Resolve<IMvxAndroidCurrentTopActivity>().Activity;
if (topActivity.LocalClassName != _splashScreenName)
{
//Do special initialization
}
}
catch (Exception ex)
{
//Log error
}
base.InitializeLastChance();
}
This works as expected but the visual effect isn't the best: a blank screen with the name of the app appears for a couple of seconds while the initialization occurs. When the initialization is finished, the last open activity is loaded.
To avoid this blank screen, I wanted to try a different approach: show an Activity while the initialization is being done, similar to a splash screen, so that the user can have some feedback that something is happening. In this approach the ViewModel attached to the new Activity would do the initialization that I want.
To show this new activity, I tried creating a custom IMvxAppStart and register it in the App class, but that hasn't worked. My custom IMvxAppStart has the following code:
public class CustomAppStart
: MvxNavigatingObject
, IMvxAppStart
{
public void Start(object hint = null)
{
if (hint == null)
{
ShowViewModel<LoginViewModel>();
}
else
{
ShowViewModel<InitializationViewModel>();
}
}
}
I wanted to show the LoginViewModel when the app starts from scratch and in the cases where the app goes straight to the last loaded Activity, the InitializationViewModel would be shown. In the latter scenario the Start method isn't called. I've checked the MvvmCross source code and as far as I can understand, the Start method is called by the MvxSplashScreenActivity (correct me if I'm wrong). When an app is reopened and goes straight to the last opened Activity, the MvxSplashScreenActivity isn't used so the Start method in CustomAppStart is not called.
That being said, I have some questions regarding the CustomAppStart approach:
Is this approach the best way to show a splash screen or "initialization activity" in the cases where the app goes straight to the last opened activity?
How do I use the CustomAppStart in these situations, since it seems that the Start method is called only from the MvxSplashScreenActivity?
How do I pass a value to the hint parameter in the Start method?
Thanks

Android In App Billing Access from another activity

I have all the In App Billing code working fine on my MainActivity.
The problem is that I want to access that code from another activity.
The code I'm trying to reach is here on my main Activity:
public void buyCode() {
Log.d(TAG, "2. Button pressed");
mHelper.launchPurchaseFlow(MainActivity.this, SKU_TEST, 10001,
mPurchaseFinishedListener, "abc123abc1234");
}
I've tried using this code on my OnClickListener on the Other Activity but it's not working, result NullPointerException
MainActivity myMainActivity = new MainActivity();
myMainActivity.buyCode();
How can I accomplish this?
Thanks
You cannot create an instance of activity by yourself and expect it behaving as a activity created by system. It won't work because system handles all the lifecycle events like create, start, stop, pause etc.
You either need to have a dedicated activity for in-app payments or you need to embed mHelper into every activity to execute buying (which is less preferable).
In the first case your main activity must be only responsible for buying operations and handling results. Then you can call it via Intent from any other activity. In the second case you implement same buying logic in multiple activities (which is bad idea) and you don't need to call main activity any longer.
If, in your case, main activity does more, than just handling in-app payments, then you can call it with an additional flag, saying a user want to buy something. You can do it as following.
// calling activity
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("purchase", true);
startActivity(intent);
// MainActivity.java
#Override
public void onCreate(Bundle savedInstanceState) {
boolean executePurchase = getIntent().getBooleanExtra("purchase", false);
if (executePurchase) {
// start purchasing logic
...
} else {
// continue with the standard logic
...
}
}
But the MainActivity will be started in front of calling activity, and you cannot avoid this with the design you currently have.

Categories

Resources