I am working on an application, wherein after say 5 times the app is opened by a user, at 6th attempt the app should ask for feedback from user. I tried using Activity OnStart,OnResume, but its not working out since even after leaving and re-entering activity these methods are called. Also as per android functionality, I cannot quit app so that I can find it out from the first activity called. How do I find how many times the app was launched?
I hope this is not confusing.
Edit
Alternatively is there a way, wherein I can always resume my app from the first activity( or welcome page for eg.), once user presses home to quit the app.
This is actually quite simple. Using SharedPreference or the Database.
during OnCreate add 1 to the numberofTimes counter and commit.
OnCreate (Bundle bundle){
mPref = getPreferences();
int c = mPref.getInt("numRun",0);
c++;
mPref.edit().putInt("numRun",c).commit();
//do other stuff...
}
OnCreate is called regardless of you start the app or you resume the app, but isFinishing() returns true if and only iff the user (or you) called finish() on the app (and it was not being destroyed by the manager)
This way you only increment when you are doing fresh start.
the isFinishing() Method inside of a OnPause method to check to see if the activity is being finish() or just being paused.
#Override
protected void OnPause(){
if(!isFinishing()){
c = mPref.getInt("numRun",0);
c--;
mPref.edit().putInt("numRun",c).commit();
}
//Other pause stuff.
}
This covers all your scenarios:
1. user starts app/activity (+1)-> finishes app, exit with finish()
2. user starts app (+1) -> pause (-1) -> returns (+1)-> finish
3. user starts app (+1) -> pause (-1) -> android kills process (0) -> user returns to app (+1) -> user finish.
every scenario you only increment the "times run" counter once per "run" of the activity
Just:
declare:
private SharedPreferences prefs;
private SharedPreferences.Editor editor;
private int totalCount;
initialize in onCreate():
prefs = getPreferences(Context.MODE_PRIVATE);
editor = prefs.edit();
print or count wherever you want (any where in onCreate() or any specific click as you specified):
totalCount = prefs.getInt("counter", 0);
totalCount++;
editor.putInt("counter", totalCount);
editor.commit();
now print totalCount where you want to count e.g.:
System.out.println("Total Application counter Reach to :"+totalCount);
if you have a starting activity for app launch then you can implement it in following ways
1. Database:- through database you can save your application launch count and retrieve it on create of activity.
Static Variable:- static variable also retain values during application start and end
Application Preference:-you can store value in application preference and use it
problem with 2 and 3 approach is that if you switch off and on again your phone you will loose data. but if you still want to use 2 or 3 approach then 2 approach is very simple and
sample code for 3rd approach here
well you have to extends Application class and create a subclass from that
public class MyApp extends Application{
int visitCount;
onCreate(){
visitCount=0;
}
and you can mention it in your menifest file like
<application name="MyApp">
.....
</application>
and in onCreate of your activity you can get it by
MyApp myApp=(MyApp)getApplicationContext();
Edit1:
subclass your activity and override method
public class myActivity extends Activity{
#Override
onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
counterFlag=true;
}
}
it is get called when user press home button
and again override onResume() and check whether your counter flag is enabled or not
and create all your activity by subclassing your MyActivity
also if any other activity has exit point on click of back button then you can override
#Override
public void back_pressed(){
}
and do your task accordingly
I think this would be the best option in order to cover all scenarios:
private static boolean valueOfLaunchCountModified = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(!valueOfCountModified){
preferences = getPreferences(MODE_PRIVATE);
launchCount= preferences.getInt("launchCount", 0);
if(preferences.edit().putInt("launchCount", ++launchCount).commit()){
valueOfCountModified = true;
if(launchCount == 5){
//Do whatever you want
}
}
}
}
If we remember the definition of a static variable ("...They are associated with the class, rather than with any object. Every instance of the class shares a class variable...") we will discover that is perfect for us.
When onPause method or an orientation change is executed the value of "valueOfLaunchCountModified" doesn't change; however, if the app process is destroyed, the value of "valueOfLaunchCountModified" changes to false.
If you only want to count "true" invocations then extend Application and place counter logic into Application#onCreate. This could be a simple preference
I prefer to use onResume to track launch count since it’s getting called in every scenario (refer to Android Activity Lifecycle) when the activity is shown.
onResume could be called quite frequently depending on usage pattern, so instead of tracking launch count, it would be better to track launch session (as in only 1 launch count would be tracked per hour).
#Synchronized fun appSessionCount(sharedPref: SharedPreferences): Boolean {
val now = LocalDateTime.now(ZoneOffset.UTC)
val firstSeconds = sharedPref.getLong(KEY_FIRST_LAUNCH_DATE, 0)
if (firstSeconds == 0L) {
sharedPref.edit {
putLong(KEY_FIRST_LAUNCH_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
}
val seconds = sharedPref.getLong(KEY_LAST_SESSION_DATE, 0)
val lastDate = if (seconds > 0) LocalDateTime.ofInstant(Instant.ofEpochSecond(seconds), ZoneOffset.UTC) else null
var count = sharedPref.getLong(KEY_SESSION_COUNT, 0)
// first time or 1 hour ago
if (lastDate == null || Duration.between(lastDate, now).toHours() >= 1) {
sharedPref.edit {
putLong(KEY_SESSION_COUNT, count + 1)
putLong(KEY_LAST_SESSION_DATE, now.atZone(ZoneOffset.UTC).toEpochSecond())
}
return true
}
return false
}
I run the code at onResume of my main activity.
class MainActivity : AppCompatActivity() {
lateinit var sharedPref: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
sharedPref = getSharedPreferences("LuaApp", Context.MODE_PRIVATE)
}
override fun onResume() {
super.onResume()
appSessionCount(sharedPref)
}
}
https://code.luasoftware.com/tutorials/android/android-track-app-launch-count/
Related
I am creating an app having a navigation drawer activity with fragments. At every cold start of the app, I am executing some initialization code where I load the following things:
The user session(if the user is logged in or not)
Registering Retrofit services
Getting some data from the server to proceed with startup of the app.
This is the flow of my app when doing a cold start:
Starting MainActivity and verifying the user session.
If the session is valid, then we open the CoreActivity.
If not, then we open the LoginActivity.
When the app is brought to the foreground after some inactivity Android tries to restart the current Activity. This means my initialization code is bypassed and CoreActivity.onCreate() is executed.
All my activities(except MainActivity) are extending the following super activity:
public abstract class MasterActivity extends AppCompatActivity {
#Override
protected final void onCreate(Bundle savedInstanceState) {
this.supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
if (!CrmContext.getInstance().verifyContextSet(this)) {
return;
}
super.onCreate(savedInstanceState);
onCreateAfterContext(savedInstanceState);
}
In CrmContext:
public boolean verifyContextSet(final Context context) {
boolean isContextSet = applicationContext != null;
if (isContextSet) {
return true;
}
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
return false;
}
In verifyContextSet() I am doing some checks to be sure that the app has been correctly loaded. If the user session is not properly loaded.
My problem:
If the app is brought to the front the CoreActivity.onCreate() is executed and verifyContextSet() returns false. In this case I want to cancel the creation of CoreActivity and open MainActivity again.
When I do my verifyContextSet() before super.onCreate(), then I get this exception:
android.util.SuperNotCalledException: Activity {nl.realworks.crm/nl.realworks.crm.view.CoreActivity} did not call through to super.onCreate()
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2287)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
I tried to execute super.onCreate() first, but then the Fragment inside the activity is created first. This means that my Fragment is recreated before my verifyContextSet() is executed.
So, If I try to cancel()/finish() the onCreate() before super.onCreate() has been called, then I get the SuperNotCalledException. If I execute super.onCreate() first, then the Fragment is initialized which is not allowed when verifyContextSet() returns false.
I want to do the following:
Bringing the app to the foreground
Check if the app has been initialized
If not, then finish() the current activity and then restart the app to open MainActivity.
put your checking/validating code in an Application sub class
public class MyApp extends Application {
//in your oncreate create sessions etc.
now whether MainActivity restarts or not, you have already validated.
Note: Application class' onCreate() is the firs to run before any body.
What you need to do is to have your onCreate like this
super.onCreate();
if(<activity is not valid>) {
startAnotherActivity()
finish()
return;
}
This will make sure that no other activity lifecycle method is called except onDestroy i.e. onResume, onPause, onStop, onStart.
I think code should look like that
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Your code
}
super.onCreate(savedInstanceState) allways as first line.
You can use ViewModel and Observer. Basically your remaining onCreate code will only execute if Observer triggered.
onResume {
// THIS WILL TRIGGER THE OBSERVER
viewModel._needVerification.value = true
}
onCreate {
super.onCreate()
... CREATE VIEWMODEL
// THIS NEEDED TO HANDLE RECREATED ACTIVITY CAUSE BY SCREEN ORIENTATION ETC
viewModel._verificationFinished.value = false
viewModel.needVerification.observe(this, Observer{
if (it == true) {
verifyContextSet(final Context context) {
if (isContextSet) {
if (viewModel.verificationFinished.value != true) {
... DO REMAINING ONCREATE CODE
viewModel._verificationFinished.value = true
}
} else {
Start MainActivity
}
}
}
})
}
Instead of only returning from onCreate you need to finish the Activity first to stop other initialization callbacks to be triggered.
Just change this code
if (!CrmContext.getInstance().verifyContextSet(this)) {
return;
}
to this
if (!CrmContext.getInstance().verifyContextSet(this)) {
finish();
return;
}
I have two activities. Activity A has a button which starts activity B. When you hit the back button in activity B it restarts activity A. Also when the back button is hit it goes to the onStop method for activity B and in this point I update shared preferences for the user. Basically I'm storing player data on the shared preferences but the problem is when activity A is restarted I also load the same shared preferences but it doesn't show the updated data that I saved while activity B was stopped. I reload the app and it shows the up to date data. So it seems that while it is being saved while activity B is being stopped there isn't enough time for when it is loaded while activity A is being restarted. So how can I tell whether the shared preferences have finished being written to or not? Basically I want to load the shared preferences once I know for sure the shared preferences has been updated, is that possible?
I am using commit method:
#Override
protected void onStop()
{
Long dist = (long)treadmill_.Mod.distance_;
Long newdist = dist + PlayerInfoManager.getInstance().getDistanceTraveled();
PlayerInfoManager.getInstance().setDistanceTraveled(newdist);
float fastestSaved = PlayerInfoManager.getInstance().getFastestSpeedAcheived();
float fastestInSess = treadmill_.Mod.fastestSpeed;
if(fastestSaved<fastestInSess)
PlayerInfoManager.getInstance().setFastestSpeedAcheived(fastestInSess);
Long time = (long)treadmill_.Mod.time_;
Long newTime = time + PlayerInfoManager.getInstance().getTotalTimeRan();
PlayerInfoManager.getInstance().setTotalTimeRan(newTime);
Log.e("OnStop", "at Run, run, run");
super.onStop();
}
In PlayerInfoManager.getinstance().setDistanceTraveled(newdist):
public void setDistanceTraveled(Long distanceTraveled)
{
DistanceTraveled = distanceTraveled;
Editor edit = SP.edit();
edit.putLong("DistanceTraveled", distanceTraveled);
edit.commit();
}
SP is the sharedPreference instance:
private SharedPreferences SP;
For activity A when it restarts:
#Override
protected void onRestart() {
super.onRestart();
LoadStats();
Log.e("onrestart", " ");
}
public void LoadStats()
{
PIM.loadAll();
Dis.setText(Long.toString(PIM.getDistanceTraveled()));
FastestSpeed.setText(Float.toString(PIM.getFastestSpeedAcheived()));
TotalTime.setText(Long.toString(PIM.getTotalTimeRan()));
KeepItUp.setText(Long.toString(PIM.getLongestTimeInKeepItUp()));
}
public void loadAll()
{// load all saved player data from sharedpreferences
DistanceTraveled = SP.getLong("DistanceTraveled", 0L);
FastestSpeedAcheived = SP.getFloat("FastestSpeed", 0.0f);
TotalTimeRan = SP.getLong("TotalTime", 0L);
LongestTimeInKeepItUp = SP.getLong("KeepItUp", 0L);
}
using .commit() method will make sure your data has been saved.
see the android doc for sharedPreferences.
see the commit and apply method
Use onPause instead since this occurs before onStop
From the documentation
Called when the system is about to start resuming a previous activity. This is typically used to commit unsaved changes to persistent data, stop animations and other things that may be consuming CPU, etc. Implementations of this method must be very quick because the next activity will not be resumed until this method returns.
You might want to see the description of an Activity's lifecycle.
I am using a LoaderManager to get some data and when it finishes a child fragment should be shown. In some cases this happens when the activity is already in paused state and can not perform the fragment transaction.
Is there a way to get the current state of the activity (seems to have a mResume flag)? Or do I have to maintain my own boolean?
The new Architecture Components allow you to do it with:
this.getLifecycle().getCurrentState()
A quick look in the Activity source code indicates that the Activity class does keep track on the resume state with the member mResumed. But since mResume is not public and isResumed() is hidden, we can't use them.
You can have a simple solution to provide you with that information for all your classes. Simply create a base Activity class that store the state. For example:
public class ActivityBase extends Activity {
private boolean mIsResumed = false;
#Override
public void onResume() {
super.onResume()
mIsResumed = true;
}
#Override
public void onPaused() {
super.onPaused()
mIsResumed = false;
}
public boolean isResumed() {
return mIsResumed
}
}
Simply extend this class with your class:
public class MyActivity extends ActivityBase {
private void onLoadDone() {
if (isResumed()) {
// Show the fragment
}
}
}
One way it could be achieved is by using breakpoints on your Activity (for instance, putting a breakpoint in your onResume method), and using the Evaluate Expression window that you can open by clicking a right click on your Debug menu window, and selecting it from there (OR SHIFT + F8) for mac. Once opened, you can intercept the current state (depending where your breakpoint is) using this line in your Evaluate Expression Window:
getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
If it returns true, that means your Activity is currently in the resume state.
If false, it's in another state.
They have plenty of other states you can play with, just check here
I'm trying to make a certain function to start only when a user,
Opens the app for the first time,
Goes back to an app from home.
But not start if the user switches between activities within the app.
I have looked through this topic,and the best answer is to use singleTask with onNewIntent(). So, if a user is goes back to the app from Home, a onNewIntent call with the launcher intent passed to it can be used.
However, here is my code:
public class AdMobSDK_DFP_Interstitial extends Activity implements AdListener {
private static final String MOBMAX_INTERSTITIAL_AD_UNIT_ID = "/7732/test_portal7/android_app1_test_portal7/splash_banner_android_app1_test_portal7";
private DfpInterstitialAd interstitialAd;
private int num = 0;
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
Log.d("flow", "onNewIntent");
}
If I switch between different activities in the app, onNewIntent() is always called, which is the same as I go back to the app from Home.
First thing you can do is to implement your own "Application" object and have it run the needed function when it is created.
public class MyApplication extends Application {
#Override
public void onCreate() {
// Call your function
}
}
Your application object will be live as long as your app is alive (any activity/service is still running), but note that the Application object is not destroyed immediately when the user presses "Home", and might stay alive for a while and a user can return to it without the function being called.
If you need this function to run as part of your main activity, just save a flag in your Application context :
public boolean alreadyDisplayed = false; and then in your activity's onStart you can just call
if ((MyApplication)getApplication().alreadyDisplayed ) {
// Call your function
(MyApplication)getApplication().alreadyDisplayed = true;
}
** If this solution is not enough for you and you need to call your function every time your main activity is displayed from the home page you'll need to do something not as nice... one suggestion I can give you is to implement the same Application object but this time with an "open activity" counter:
public class MyApplication extends Application {
public int mActivityCounter = 0;
}
Then you can increment this counter on every onStart of activity in your app and decrement on every onStop (of course this can be done by implementing a class MyActivity and make all your relevant activities inherit it. Then you can use this counter to know if there are any other activities opened. Note that you'll have to make sure the access to this counter is synchronized and work your way with it as you need.
I hope this helps...
I have two activities A and B.
public class A extends Activity
{
....
#Override
protected void onResume()
{
super.onResume();
if(MyStaticVarz.myFlag)
{
MyStaticVarz.myFlag= false;
SomeTask();
}
}
MyStaticVarz.java :
public class MyStaticVarz
{
public static boolean myFLag = false;
}
Go from A to B and change myFlag to true like:
MyStaticVarz.myFlag = true;
and go back to A again,but in onResume if(MyStaticVarz.myFlag) is false and SomeTask() not reached.
Going from A to B like :
Intent i = new Intent(A.this, B.class);
startActivity(i);
UPDATE
SomeTask() is for change fontsize of a text in A and B.
myFlag is for on demand reinitialize of UI that if font setting changed,then SomeTask() run.
When click on optionMenu in B,and change font size,and go to B,i see changes,but when go back to A,text font size not happen.
Maybe Important: when i'm back to A and font size is not ok and myFlag is false too,if i change oriantation,text fontsize is ok but myFlag is false again!
If you access your "myFlag" variable from different threads, each may have a locally stored copy, so a change in the variable's value might not be seen by different threads immediately.
Define your variable as volatile to force all threads to see the same copy:
public static volatile boolean myFLag = false;
See this nice answer for a more detailed explanation
Because you are accessing a single static variable in your code from 2 threads you need to add some locking around that access.
I would look at something like:
public class MasterBB extends Activity
{
private final ReentrantLock lock = new ReentrantLock();
#Override
protected void onResume() {
super.onResume();
lock.lock();
try {
if(MyStaticVarz.myFlag) {
MyStaticVarz.myFlag= false;
SomeTask();
}
}
finally {
lock.unlock();
}
}
}
I'm afraid my Java is a bit rusty, there might be a more up to date way too do it, but this is the general idea. You need to ensure that myFlag does not get the chance to get modified while that block of code is running.
Your answer is simple, when you change MyStaticVarz.myFlag to true in getView, then you back to activity B, in onResume() the value of MyStaticVarz.myFlag change from true to false and when you back to activity A , the value of MyStaticVarz.myFlag is false not true and is obvious that activity A never get true value.
To solve this problem you must save changed value :
String value = entryValues[index].toString();
to static String in MyStaticVarz and in activity A and B onResume() event check the static String with local String to understand change.
Is false the initial value of the member? Perhaps your application's variables are being reset during the process. If I remember Android correctly, onResume can be called as a part of app termination, which I am almost certain destroys all of your non-stored data. You'd need to store that information in a Bundle and restore from it as well.