Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deep link launches the Splash Activity and not the Main Activity

I followed this guide - Splash screens the right way to create a Splash screen for my Android application so now I have 2 Activities (MainActivity and SplashActivity)

The problem is that Deep Links miss behave now, as instead of launching the MainActivity they launch the SplashActivity.

I don't want the SplashActivity to EVER show up except for when the app starts.

What can I do?

SplashActivity:

public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, MainActivity.class);
        startActivity(intent);
        finish();
    }
}

MainActivity:

public class MainActivity extends SplashActivity implements OnImagePickerPermissionsCallback {

    private PermissionListener listener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void setPermissionListener(PermissionListener listener)
    {
        this.listener = listener;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
    {
        if (listener != null)
        {
            listener.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    @Override
    public View createSplashLayout() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(getResources().getColor(R.color.navBarColor));
        }
        LinearLayout view = new LinearLayout(this);
//        view.setBackgroundColor(getResources().getColor(R.color.catalyst_redbox_background));
        view.setBackgroundResource(R.drawable.launch_screen_radius);
        return view;
    }
}

Manifest file:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

<!-- For using react-native-fcm CLOUD MESSAGING-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />


<!-- For using react-native-image-picker -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<!-- For react-native-webview-file-upload-android -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


<!-- For using NetInfo -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-sdk
    android:minSdkVersion="16"
    android:targetSdkVersion="24" />

<application
    android:name=".MainApplication"
    android:allowBackup="true"
    android:label="@string/app_name"
    android:icon="@mipmap/ic_launcher"
    android:theme="@style/AppTheme"
    >

    <!-- The 2 services below are for react-native-fcm cloud messaging -->
    <service android:name="com.evollu.react.fcm.MessagingService" android:enabled="true" android:exported="true">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT"/>
        </intent-filter>
    </service>
    <service android:name="com.evollu.react.fcm.InstanceIdService" android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
        </intent-filter>
    </service>

    <!-- The 2 receivers below are for react-native-fcm local notifications-->
    <receiver android:name="com.evollu.react.fcm.FIRLocalMessagingPublisher"/>
    <receiver android:enabled="true" android:exported="true"  android:name="com.evollu.react.fcm.FIRSystemBootEventReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
            <action android:name="android.intent.action.QUICKBOOT_POWERON"/>
            <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </receiver>

    <activity
        android:name=".SplashActivity"
        android:label="@string/app_name"
        android:theme="@style/SplashTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:windowSoftInputMode="adjustResize"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:screenOrientation="portrait"
        android:launchMode="singleTop"
        >
        <!-- launchMode="singleTop" is for fcm cloud messaging -->
        <intent-filter>
            <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
        </intent-filter>

        <!--
            The intent filter below are for react-native-fcm click_action
            https://github.com/evollu/react-native-fcm#config-for-notification-and-click_action-in-android
        -->
        <intent-filter>
            <action android:name="fcm.ACTION.HELLO" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>

        <intent-filter>
            <!-- Sets the intent action to view the activity -->
            <action android:name="android.intent.action.VIEW" />

            <!-- Allows the deep link to be used without specifying the app name -->
            <category android:name="android.intent.category.DEFAULT" />

            <!-- Allows the link to be opened from a web browser -->
            <category android:name="android.intent.category.BROWSABLE" />

            <!-- Accepts URIs that begin with "https://www.myExampleDomain.com -->
            <data android:scheme="https" android:host="www.myExampleDomain.com" />

            <!-- Accepts URIs that begin with "myExampleDomain:// -->
            <data android:scheme="myExampleDomain"/>
        </intent-filter>
    </activity>
    <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>

Edit: Some people pointed out that I'm using SplashActivity. It's true, that's the code of the following activity. It's part of react-native-navigation the library that I'm using for screen navigation. Not sure if that helps but here's the code:

SplashActivity:

public abstract class SplashActivity extends AppCompatActivity {
    public static boolean isResumed = false;

    public static void start(Activity activity) {
        Intent intent = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
        if (intent == null) return;
        intent.setAction(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        activity.startActivity(intent);
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setSplashLayout();
        IntentDataHandler.saveIntentData(getIntent());
    }

    @Override
    protected void onResume() {
        super.onResume();
        isResumed = true;

        if (NavigationApplication.instance.getReactGateway().hasStartedCreatingContext()) {
            if (CompatUtils.isSplashOpenedOverNavigationActivity(this, getIntent())) {
                finish();
                return;
            }
            NavigationApplication.instance.getEventEmitter().sendAppLaunchedEvent();
            if (NavigationApplication.instance.clearHostOnActivityDestroy()) {
                overridePendingTransition(0, 0);
                finish();
            }
            return;
        }

        if (ReactDevPermission.shouldAskPermission()) {
            ReactDevPermission.askPermission(this);
            return;
        }

        if (NavigationApplication.instance.isReactContextInitialized()) {
            NavigationApplication.instance.getEventEmitter().sendAppLaunchedEvent();
            return;
        }

        // TODO I'm starting to think this entire flow is incorrect and should be done in Application
        NavigationApplication.instance.startReactContextOnceInBackgroundAndExecuteJS();
    }

    @Override
    protected void onPause() {
        super.onPause();
        isResumed = false;
    }

    private void setSplashLayout() {
        final int splashLayout = getSplashLayout();
        if (splashLayout > 0) {
            setContentView(splashLayout);
        } else {
            setContentView(createSplashLayout());
        }
    }

    /**
     * @return xml layout res id
     */
    @LayoutRes
    public int getSplashLayout() {
        return 0;
    }

    /**
     * @return the layout you would like to show while react's js context loads
     */
    public View createSplashLayout() {
        View view = new View(this);
        view.setBackgroundColor(Color.WHITE);
        return view;
    }
}

MainApplication.java public class MainApplication extends NavigationApplication implements ReactInstanceHolder {

    @Override
    public boolean clearHostOnActivityDestroy() {
        return false;
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) {
            // only for KITKAT_WATCH and newer versions
            MultiDex.install(this);
        }
    }

    @Override
    public boolean isDebug() {
        // Make sure you are using BuildConfig from your own application
        return BuildConfig.DEBUG;
    }

    @Override
    public String getJSMainModuleName() {
        return "index";
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Log.v(TAG, "onConfigChange"+newConfig);
        Intent intent = new Intent("onConfigurationChanged");
        intent.putExtra("newConfig", newConfig);
        this.sendBroadcast(intent);
    }


    // 2. Override the getJSBundleFile method in order to let
    // the CodePush runtime determine where to get the JS
    // bundle location from on each app start
    @Override
    public String getJSBundleFile() {
        return CodePush.getJSBundleFile();
    }

    @NonNull
    @Override
    public List<ReactPackage> createAdditionalReactPackages() {
        return Arrays.<ReactPackage>asList(
            new LinearGradientPackage(),
            new OrientationPackage(),
            new VectorIconsPackage(),
            new KeychainPackage(),
            new BackgroundTimerPackage(),
            new RNI18nPackage(),
            BugsnagReactNative.getPackage(),
            new BlurViewPackage(),
            new PickerViewPackage(),
            new ImagePickerPackage(),
            new RNFetchBlobPackage(),
            new MapsPackage(),
            new FIRMessagingPackage(),
            new RNAmplitudeSDKPackage(MainApplication.this),
            new RNVersionCheckPackage(),
            new RNCardIOPackage(),
            new AndroidWebViewPackage(),
            new WheelPackage()
        );
    }


    @Override
    public void onCreate() {
        super.onCreate();
        setActivityCallbacks(new ActivityCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivityResult(int requestCode, int resultCode, Intent data) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
//            @Override
//            public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
//                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//            }

        });
    }

    @Override
    public ReactInstanceManager getReactInstanceManager() {
        return getReactNativeHost().getReactInstanceManager();
    }
}

Any hints would be greatly appreciated, thank you!

like image 551
SudoPlz Avatar asked Jan 03 '18 22:01

SudoPlz


People also ask

What is the purpose of deep linking?

Deep links are a type of link that send users directly to an app instead of a website or a store. They are used to send users straight to specific in-app locations, saving users the time and energy locating a particular page themselves – significantly improving the user experience.

What is the difference between deep linking and deferred deep linking?

In the context of mobile apps, deep linking consists of using a uniform resource identifier (URI) that links to a specific location within a mobile app rather than simply launching the app. Deferred deep linking allows users to deep link to content even if the app is not already installed.

What is the difference between deep links and app links?

When a user click an URL, it might open a dialog which asks the user to select one of multiple apps handling the given URL. On the other hand, An Android App Link is a deep link based on your website URL that has been verified to belong to your website. When user clicks that URL, it opens your app.

What is deep linking on the new net banking?

What Is Deep Linking? Similar to how you give someone a web link, deep linking allows you to do the same thing with mobile apps. Up until several years ago, you could only link to the start of a mobile app. Now, with deep linking, you can send users to any specific page or section of the app.


3 Answers

Ex: Your url will be something like https://example.com and you have the intent filter in Android Manifest as below:

<activity
    android:name="com.droid.MainActivity"
    android:label="@string/app_name" >

    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="example.com"
            android:scheme="https" />
    </intent-filter>
</activity>

Use the URL Mapping editor to easily add URL intent filters to your Activities with Android App Links (Android Studio > Tools > Android App Links).

like image 183
Nirav Joshi Avatar answered Sep 19 '22 07:09

Nirav Joshi


Found what caused the problem.

It was actually this line of code on MainApplication.java:

public boolean clearHostOnActivityDestroy() {
    return false;
}

when I changed that value to true, everything started working the way they were supposed to.

That was an issue caused by my navigation library: https://github.com/wix/react-native-navigation

like image 34
SudoPlz Avatar answered Sep 18 '22 07:09

SudoPlz


The android system looks at the manifest for the first tag with:

   <category android:name="android.intent.category.LAUNCHER" />

Set MainActivity to use this tag instead of SplashActivity.

Launch MainActivity always first and in your MainActivity onCreate perform any checks on Intent.

    //replace getStringExtra with whatever you use to identify deeplink.
Boolean isDeepLink= getIntent().getStringExtra("").startsWith("mydeeplink://");

if(!isDeepLink)
    {
        Intent splashIntent=new Intent(this, SplashActivity.class);
        startActivity(splashIntent);
    }
like image 43
Mustafa Dualeh Avatar answered Sep 18 '22 07:09

Mustafa Dualeh