3

I wanted to start an Activity only the first time my App is launched, and from that point on, the app should launch with another (the launcher) activity every time. So I implemented a solution based on this SO answer.

The solution revolves around a boolean preference (the one having startedBeforePreferenceKey as key in the following code). In the onCreate() of the launcher activity, I try to retrieve a preference with the key startedBeforePreferenceKey and store it in a variable startedBefore. If the preference does not exist, startedBefore is assigned false.

Then I check if startedBefore is false, and if so, I create the mentioned preference, give it a value of true and store it in the SharedPreferences, and start this activity which should launch the app for the first time. This way, when this check is executed next time the onCreate() is being executed, startedBefore will be assigned true and thus this launch-once activity won't start.

THE PROBLEM IS that when the app is launched for the first time, the normal launcher SHOWS MOMENTARILY before the activity which should launch the app only when the app is launched for the first time is started.

When the app is launched for the first time, the normal launcher should not show at all. Directly, the special activity which I want to show when app is launched for the first time, should show.

What should I do?

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean startedBefore = sharedPreferences.getBoolean(getString(R.string.startedBeforePreferenceKey), false);

if (!startedBefore) {
    SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
    sharedPreferencesEditor.putBoolean(getString(R.string.startedBeforePreferenceKey), true);
    sharedPreferencesEditor.commit();
    startActivity(new Intent(this, MainActivity.class)); 
} 

EDIT: @HammadTariqSahi

First, this excerpt from LogCat:

03-16 08:42:25.629: E/AndroidRuntime(1837): FATAL EXCEPTION: main
03-16 08:42:25.629: E/AndroidRuntime(1837): Process: tests.globalactivitytest, PID: 1837
03-16 08:42:25.629: E/AndroidRuntime(1837): java.lang.RuntimeException: Unable to instantiate application tests.globalactivitytest.activity.GlobalActivity: java.lang.ClassNotFoundException: Didn't find class "tests.globalactivitytest.activity.GlobalActivity" on path: DexPathList[[zip file "/data/app/tests.globalactivitytest-1/base.apk"],nativeLibraryDirectories=[/data/app/tests.globalactivitytest-1/lib/x86, /vendor/lib, /system/lib]]
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.LoadedApk.makeApplication(LoadedApk.java:578)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4680)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.-wrap1(ActivityThread.java)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.os.Handler.dispatchMessage(Handler.java:102)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.os.Looper.loop(Looper.java:148)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.ActivityThread.main(ActivityThread.java:5417)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.reflect.Method.invoke(Native Method)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
03-16 08:42:25.629: E/AndroidRuntime(1837): Caused by: java.lang.ClassNotFoundException: Didn't find class "tests.globalactivitytest.activity.GlobalActivity" on path: DexPathList[[zip file "/data/app/tests.globalactivitytest-1/base.apk"],nativeLibraryDirectories=[/data/app/tests.globalactivitytest-1/lib/x86, /vendor/lib, /system/lib]]
03-16 08:42:25.629: E/AndroidRuntime(1837):     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.Instrumentation.newApplication(Instrumentation.java:981)
03-16 08:42:25.629: E/AndroidRuntime(1837):     at android.app.LoadedApk.makeApplication(LoadedApk.java:573)
03-16 08:42:25.629: E/AndroidRuntime(1837):     ... 9 more
03-16 08:42:25.629: E/AndroidRuntime(1837):     Suppressed: java.lang.ClassNotFoundException: tests.globalactivitytest.activity.GlobalActivity
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.Class.classForName(Native Method)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
03-16 08:42:25.629: E/AndroidRuntime(1837):         at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
03-16 08:42:25.629: E/AndroidRuntime(1837):         ... 12 more
03-16 08:42:25.629: E/AndroidRuntime(1837):     Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

GlobalActivity.java:

package tests.globalactivitytest;

import android.app.Application;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;

public class GlobalActivity extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        //SharedPreferences.Editor editor = sharedPreferences.edit(); 
        boolean launchedBefore = sharedPreferences.getBoolean("launchedBefore", false);
        if (launchedBefore) {
            Intent intent = new Intent(this, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        } else {
            Intent intent = new Intent(this, LaunchOnceActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        }
    }
}

MainActivity.java:

package tests.globalactivitytest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

LaunchOnceActivity.java:

package tests.globalactivitytest;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class LaunchOnceActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_launch_once);
    }
}

Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tests.globalactivitytest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="23" />

    <application
        android:name=".activity.GlobalActivity"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".LaunchOnceActivity"
            android:label="@string/title_activity_launch_once" >
        </activity>
    </application>

</manifest>
Community
  • 1
  • 1
Solace
  • 8,612
  • 22
  • 95
  • 183
  • 3
    You can't really handle this without some sort of "flash". My recommendation, have a splash screen view act as your launched activity and handle the redirect from there, that way you get a constant experience regardless of which activity is loaded. – zgc7009 Mar 15 '16 at 16:40
  • 1
    You should make use of some sort of SplashScreen (as @zgc7009 said) plus the use of application version and so on (shared preferences would be a good place to store data). Officially Android doesn't offer what you are intending to do, this is the drawback. – joninx Mar 15 '16 at 16:43
  • @zgc7009 Have you seen Instagram? Instagram, Quora, WhatsApp, they all require you to register or login when the app is launched for the first time. Do they use such hacks? I don't think they would use hacky solutions, because they are tech giants and have best engineers. :s – Solace Mar 15 '16 at 17:16
  • 1
    Yea, absolutely. This isn't a hack at all, it is actually a pretty common part of Android dev, a bunch of my apps have splash screens. They are even documented in the official docs. Mister Smiths has an answer below that will work. – zgc7009 Mar 15 '16 at 17:25
  • Try to use visibility. – António Paulo Mar 15 '16 at 17:26
  • because your GlobalActivity class is in globalactivitytest package but you are referencing it as globalactivitytest.activity.GlobalActivity. due to which you are getting classNotFound exception. there is no activity package in your project as I checked your code also and send you updated apk and code. – Developine Mar 17 '16 at 04:55
  • updated code in answer also. please check, – Developine Mar 17 '16 at 05:06

3 Answers3

5

Above answers are also fine but if you need to do it without splash activity then you can use below method

Step 1:Create a new class and extends from Application and add below code (do not forget to change your activity name accordingly). The most important part of it is setting appropriate Intent Flags before starting any of the activities.

This GlobalActivity will be called before your launcher Activity

public class GlobalActivity extends Application {


@Override
public void onCreate() {
    super.onCreate();
  SharedPreferences userInfo = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  SharedPreferences.Editor  editor = userInfo.edit();
  boolean logedIn =  userInfo.getBoolean("loggedIn", false);
    if (logedIn)
    {
        Intent intent = new Intent(this,MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }
    else {
        Intent intent = new Intent(this,LaunchOnceActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
    }
}
}

Step 2: Your manifest file should be like below

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.app.appid" >


<application
android:name=".GlobalActivity"
android:allowBackup="true"
android:icon="@drawable/launch"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
    android:name=".MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity
    android:name=".LaunchOnceActivity"
    android:label="Launch Once" >
</activity>
</application>
</manifest>

And then change loggedIn varibale in any of the activity accordingly for future use.

Solace
  • 8,612
  • 22
  • 95
  • 183
Developine
  • 12,483
  • 8
  • 38
  • 42
  • Is there a reason to use `getApplicationContext()` instead of `this` in the second statement in `onCreate()`? Because you have used `this` everywhere else where the `Context` is required (i.e. in the `startActivity()` statements)? – Solace Mar 16 '16 at 11:19
  • And where should I add the launcher intent filter? – Solace Mar 16 '16 at 11:36
  • 1
    launcher intent filter will be same as usual, nothing to do with it, just add in activity which you want to launch first, above onCreate method of GlobalActivity will be called before onCreate of Launcher activity. – Developine Mar 16 '16 at 12:17
  • 1
    if you want to decide launcher here in GlobalActivity, you can like above. just set any activity to launcher. – Developine Mar 16 '16 at 12:19
  • 1
    getApplicationContext() and this are both same here, since you are extending from application. – Developine Mar 16 '16 at 12:21
  • It's not working for me. I put together a lil test and I'll post it as an edit to the quesion. – Solace Mar 16 '16 at 12:44
  • did you updated loggedIn variable in your next activity – Developine Mar 16 '16 at 13:04
3

You could create a splash activity and from its onCreate method check which activity to run next, wait a few seconds, launch the activity and then immediately finish the splash.

If you don't want to create a splash, then create a fake splash activity with no GUI content and translucent theme, and do the routing in the onCreate, then finish immediately. This will be so fast the user won't notice.

Mister Smith
  • 27,417
  • 21
  • 110
  • 193
  • I like this response, it provides an option that doesn't force a visual splash screen but still hides the blip/flash. If you had some time to post an example it would be awesome, but still a good suggestion. – zgc7009 Mar 15 '16 at 17:03
  • Have you seen Instagram? Instagram, Quora, WhatsApp, they all require you to register or login when the app is launched for the first time. Do they use such hacks? I don't think they would use hacky solutions, because they are tech giants. :s – Solace Mar 15 '16 at 17:16
  • @Solace this handles that situation. – zgc7009 Mar 15 '16 at 17:25
  • 1
    @Solace Well if you manage to find how those companies do it then please post it here, but I'm afraid its not open source code :) Still curious about their method. – Mister Smith Mar 15 '16 at 17:26
  • 1
    @Solace also note that Instagram, WhatsApp, Facebook and other apps might not have the best or most user friendly practices. Just take Facebook for example. The app uses as much resources as 20% of what the whole Android OS does (tested on Nexus 5X). I wouldn't necessarily try to follow their footsteps. – SoroushA Mar 16 '16 at 13:06
  • I guessed something about how they work. If we look closely, when we install Instagram or Quora for the first time, there is a white blank screen there momentarily (splash?) from where it goes to the login page. Quora even has an undetermined progresswheel on the white screen. But the confusion is that for the future launches, that blank white screen is not shown. – Solace Mar 17 '16 at 05:44
1

You can wrap your activity and the normal launcher in fragments. Then, create another activity with a fragmentmanager in its onCreate and make that your launcher at all times. Check for the preference before putting the right fragment on the screen in that activity.

SoroushA
  • 2,043
  • 1
  • 13
  • 29