Android Question item order in manifest

marcick

Well-Known Member
Licensed User
Longtime User
Hi all,
Is there any way to make sure that the Main <acitivy> attribute is always the first <activity> attribute in the generated manifest xml ?
In B4A manifest editor I have them on the top, but in the compiled XML they are after some other stuff and it creates some inconveniences.
Thanks
Marco
 

marcick

Well-Known Member
Licensed User
Longtime User
Hi.
My app consist of a main activity plus a second activity that has a separate icon and works as sms client interface. So there are two separate icon, one launch the main app and the other launch the sms client.
For an unknown (to me) reason, the very first time the app is installed, when you click on "open", the sms client activity is started and I don't like it, I want the main app is started.
In the b4A manifest the main app attributes are declared first, then follow the client attributes. But if I open the compiled xml manifest, the order is inverted. If I edit the manifest outside b4a and move the main app attributes on top, then recompile (checking "do not overwrite manifest") everything works fine, also the first time the user install and open the app, the main activity is started.
So, I was wondering if there is a way to tell B4A to respect the order of the declarations as in the B4A manifest editor.
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
@Erel

Take a look at the attached example project.
It contains 2 activity modules.

Imagine that the project represents 2 applications - the primary application and a secondary application - an sms client application.
Both applications should appear in the application drawer with their own icons.
So i've added these manifest entries:

B4X:
SetActivityAttribute(Main, android:launchMode, "singleTask")

SetActivityAttribute(SmsClient, android:launchMode, "singleTask")
SetActivityAttribute(SmsClient, android:label, "SMS Client")
AddActivityText(SmsClient, 
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>)

It is required to set the launchMode to singleTask instead of the default singleTop otherwise the incorrect application is launched from the application drawer if the other application was exited by selecting the Home key.
That is: if the primary application is running and the user selects the Home key and then selects the sms client icon in the application drawer, the primary application will be started.
Likewise, if the sms client application is running and the user selects the Home key and then selects the primary application icon in the aplication drawer, then the sms client application is started.
Changing the launchMode from singleTop to singleTask fixes this problem - the correct application is always started when the application drawer icons are selected.

Everything works fine so far.
If the project is compiled and installed from the b4a IDE then the Main activity is always the activity that is automatically started.

However, if you compile the project and transfer the compiled .apk to the device and then install the .apk, a system dialog prompts you 'do you want to start the application'.
If you select yes then the system starts the first activity in the manifest that contains the launcher intent filter.

In this example project this is not a problem as (on my laptop) the Main activity is the first activity in the manifest that contains the launcher intent filter:

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

<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="14"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>
<application
android:icon="@drawable/icon"
android:label="Main Activity"
android:theme="@android:style/Theme.Holo">
<activity
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:name=".main"
android:label="Main Activity"
android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

</activity>
<activity
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask"
android:name=".smsclient"
android:label="SMS Client"
android:screenOrientation="unspecified">

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

In marcick's project the secondary application (the sms client) is contained in a library and when the project is compiled the sms client activity is always the first activity in the manifest that contains the launcher intent filter.
When the user installs the apk file, the sms client activity is always the activity that is automatically started (from the above mentioned dialog prompt) - marcick wants his primary application activity to be started and not the sms client activity.

Currently, the solution is:
  • Develop and update the project with the b4a IDE 'Do Not Overwrite Manifest File' menu setting unchecked - it's default state.
  • When an .apk file is required for release, compile the project.
  • Check the 'Do Not Overwrite Manifest File' setting.
  • Open the AndroidManifest.xml file in a text editor.
  • Cut and paste the Main <activity> element so that it is the first child <activity> element of the <application> attribute.
  • Compile the project.
  • The compiled .apk now behaves as desired, the primary application's Main activity is started automatically by the system by the system dialog that appears after installation.
  • Uncheck the 'Do Not Overwrite Manifest File' setting.

The solution works but isn't ideal, so marcick wonders if there's any way to force the primary application's Main activity to always be the first activity element in the manifest.

Martin.
 

Attachments

  • ManifestOrder.zip
    13.4 KB · Views: 243
Last edited:
Upvote 0

warwound

Expert
Licensed User
Longtime User
Does your source code take account of activities that are part of library .jar files?
This is the case with marcick - the sms client and it's launcher activity are in a library .jar file.

For both me and marcick the sms client activity is added to the manifest before the b4a Main activity.

Martin.
 
Upvote 0

warwound

Expert
Licensed User
Longtime User
If you compile the PreferenceActivity example project and look in the AndroidManifest.xml you'll find that the PreferenceActivity library activity - the activity that's contained within the library .jar file - is the first activity element:

<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="anywheresoftware.b4a.samples.preferenceactivity"
android:versionCode="1"
android:versionName=""
android:installLocation="internalOnly">

<uses-sdk android:minSdkVersion="4" />
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>
<application
android:icon="@drawable/icon"
android:label="PreferenceActivity example">

<activity android:name="anywheresoftware.b4a.objects.preferenceactivity"/>

<activity
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTop"
android:name=".main"
android:label="PreferenceActivity example"
android:screenOrientation="unspecified">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
</manifest>

Martin.
 
Upvote 0

marcick

Well-Known Member
Licensed User
Longtime User
Yes.
This is my manifest, and the main activity is down in the file, below the sms client activity stuff

B4X:
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="it.developer.TestApp"
    android:versionCode="420"
    android:versionName="4.20"
    android:installLocation="preferExternal">
   
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19"/>
    <supports-screens android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="false"
        android:anyDensity="true"/>
    <uses-feature android:name="android.hardware.telephony"
        android:required="false"/>
    <permission android:name="it.developer.TestApp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
    <permission
              android:name="it.developer.TestApp.permission.MAPS_RECEIVE"
              android:protectionLevel="signature"/>
          <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
        <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
    <uses-permission android:name="android.permission.VIBRATE"/>
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.WRITE_SMS"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="it.developer.TestApp.permission.MAPS_RECEIVE"/>
    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="it.developer.TestApp.permission.C2D_MESSAGE"/>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
    <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
    <application
        android:icon="@drawable/icon"
        android:label="TestApp"
        android:theme="@android:style/Theme">
       
        <activity
            android:icon="@drawable/ic_sms_app"
            android:label="@string/message_composer_activity_label"
            android:launchMode="singleTop"
            android:name="it.developer.b4a.smsclient.conversation.ComposerActivity"
            android:parentActivityName="it.developer.b4a.smsclient.conversations.ConversationsActivity"
            android:theme="@style/AppTheme">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SENDTO" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </activity>
        <activity
            android:icon="@drawable/ic_sms_app"
            android:label="@string/app_name"
            android:launchMode="singleTop"
            android:name="it.developer.b4a.smsclient.conversation.ConversationActivity"
            android:parentActivityName="it.developer.b4a.smsclient.conversations.ConversationsActivity"
            android:theme="@style/AppTheme">
        </activity>
        <activity
            android:icon="@drawable/ic_sms_app"
            android:label="@string/app_name"
            android:launchMode="singleTask"
            android:name="it.developer.b4a.smsclient.conversations.ConversationsActivity"
            android:theme="@style/AppTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:icon="@drawable/ic_sms_app"
            android:label="@string/title_select_contact"
            android:launchMode="singleTop"
            android:name="it.developer.b4a.smsclient.contactpicker.ContactsPickerActivity"
            android:parentActivityName="it.developer.b4a.smsclient.conversation.ComposerActivity"
            android:theme="@style/AppTheme">
        </activity>
        <receiver
            android:name="it.developer.b4a.smsclient.receiver.MessageDeliveredReceiver"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="it.developer.b4a.smsclient.ACTION_MESSAGE_DELIVERED" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="it.developer.b4a.smsclient.receiver.MessageSentReceiver"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="it.developer.b4a.smsclient.ACTION_MESSAGE_SENT" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="it.developer.b4a.smsclient.receiver.MmsBroadcastReceiver"
            android:permission="android.permission.BROADCAST_WAP_PUSH" >
            <intent-filter>
                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
                <data android:mimeType="application/vnd.wap.mms-message" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="it.developer.b4a.smsclient.receiver.BootCompletedReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <service
            android:name="it.developer.b4a.smsclient.service.MessageSenderService"
            android:exported="true"
            android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" >
            <intent-filter>
                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sms" />
                <data android:scheme="smsto" />
                <data android:scheme="mms" />
                <data android:scheme="mmsto" />
            </intent-filter>
        </service>
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyDQY3h-DKqTQ3BtRwHW2PDT4nwM2k5U1c4"/>
        <meta-data android:name="com.google.android.gms.version"
         android:value="@integer/google_play_services_version"
            />
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTask"
            android:name=".main"
            android:label="TestApp"
            android:screenOrientation="unspecified">
            <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
           
        </activity>
        <service android:name=".licencemanager">
        </service>
        <receiver android:name=".licencemanager$licencemanager_BR">
        </receiver>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".expired"
            android:label="TestApp"
            android:screenOrientation="unspecified">
        </activity>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".purchase"
            android:label="TestApp"
            android:screenOrientation="unspecified">
        </activity>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".nomaps"
            android:label="TestApp"
            android:screenOrientation="unspecified">
        </activity>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".locoptions"
            android:label="TestApp"
            android:screenOrientation="unspecified">
        </activity>
        <service android:name=".downloaderservice">
        </service>
        <receiver android:name=".downloaderservice$downloaderservice_BR">
        </receiver>
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".pending"
            android:label="TestApp"
            android:screenOrientation="unspecified">
        </activity>
        <service android:name=".pushservice">
        </service>
        <receiver
            android:name=".pushservice$pushservice_BR"
            android:permission="com.google.android.c2dm.permission.SEND">
           
            <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="it.developer.TestApp" />
            </intent-filter>
            <intent-filter>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="it.developer.TestApp" />
            </intent-filter>
        </receiver>
        <service android:name=".websync">
        </service>
        <receiver android:name=".websync$websync_BR">
        </receiver>
        <service android:name=".smsmonitor">
        </service>
        <receiver
            android:name=".smsmonitor$smsmonitor_BR"
            android:permission="android.permission.BROADCAST_SMS">
           
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_DELIVER" />
            </intent-filter>
        </receiver>
        <service android:name=".inboxcleaner">
        </service>
        <receiver android:name=".inboxcleaner$inboxcleaner_BR">
        </receiver>
        <service android:name="anywheresoftware.b4a.samples.httputils2.httputils2service">
        </service>
        <receiver android:name="anywheresoftware.b4a.samples.httputils2.httputils2service$httputils2service_BR">
        </receiver>
    </application>
</manifest>
 
Upvote 0

marcick

Well-Known Member
Licensed User
Longtime User
Here it is

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
'
'xlarge screens are at least 960dp x 720dp
'large screens are at least 640dp x 480dp
'normal screens are at least 470dp x 320dp
'small screens are at least 426dp x 320dp
AddManifestText(
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="false"
    android:anyDensity="true"/>
<uses-feature android:name="android.hardware.telephony"
    android:required="false"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
SetApplicationAttribute(android:theme, "@android:style/Theme")

'    change default launchMode of Main activity to singleTask
SetActivityAttribute(Main, android:launchMode, "singleTask")

'    SmsClientLibrary activity to compose and send SMS
AddApplicationText(<activity
    android:icon="@drawable/ic_sms_app"
    android:label="@string/message_composer_activity_label"
    android:launchMode="singleTop"
    android:name="it.developer.b4a.smsclient.conversation.ComposerActivity"
    android:parentActivityName="it.developer.b4a.smsclient.conversations.ConversationsActivity"
    android:theme="@style/AppTheme">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <action android:name="android.intent.action.SENDTO" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
        <data android:scheme="mms" />
        <data android:scheme="mmsto" />
    </intent-filter>
</activity>)

'    SmsClientLibrary activity
AddApplicationText(<activity
    android:icon="@drawable/ic_sms_app"
    android:label="@string/app_name"
    android:launchMode="singleTop"
    android:name="it.developer.b4a.smsclient.conversation.ConversationActivity"
    android:parentActivityName="it.developer.b4a.smsclient.conversations.ConversationsActivity"
    android:theme="@style/AppTheme">
</activity>)

'    SmsClientLibrary activity
AddApplicationText(<activity
    android:icon="@drawable/ic_sms_app"
    android:label="@string/app_name"
    android:launchMode="singleTask"
    android:name="it.developer.b4a.smsclient.conversations.ConversationsActivity"
    android:theme="@style/AppTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>)

'    SmsClientLibrary activity
AddApplicationText(<activity
    android:icon="@drawable/ic_sms_app"
    android:label="@string/title_select_contact"
    android:launchMode="singleTop"
    android:name="it.developer.b4a.smsclient.contactpicker.ContactsPickerActivity"
    android:parentActivityName="it.developer.b4a.smsclient.conversation.ComposerActivity"
    android:theme="@style/AppTheme">
</activity>)

'    SmsClientLibrary receiver
AddApplicationText(<receiver
    android:name="it.developer.b4a.smsclient.receiver.MessageDeliveredReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="it.developer.b4a.smsclient.ACTION_MESSAGE_DELIVERED" />
    </intent-filter>
</receiver>)

'    SmsClientLibrary receiver
AddApplicationText(<receiver
    android:name="it.developer.b4a.smsclient.receiver.MessageSentReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="it.developer.b4a.smsclient.ACTION_MESSAGE_SENT" />
    </intent-filter>
</receiver>)

'    SmsClientLibrary receiver for MMS
AddApplicationText(<receiver
    android:name="it.developer.b4a.smsclient.receiver.MmsBroadcastReceiver"
    android:permission="android.permission.BROADCAST_WAP_PUSH" >
    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>
</receiver>)

'    SmsClientLibrary receiver to display unread messages notification at boot
AddApplicationText(<receiver
    android:name="it.developer.b4a.smsclient.receiver.BootCompletedReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>)

'    SmsClientLibrary service to send SMS with no user interaction
AddApplicationText(<service
    android:name="it.developer.b4a.smsclient.service.MessageSenderService"
    android:exported="true"
    android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE" >
    <intent-filter>
        <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
        <data android:scheme="mms" />
        <data android:scheme="mmsto" />
    </intent-filter>
</service>)

AddPermission(android.permission.READ_SMS)
AddPermission(android.permission.RECEIVE_SMS)
AddPermission(android.permission.SEND_SMS)
AddPermission(android.permission.WRITE_SMS)
AddPermission(android.permission.READ_CONTACTS)

'    b4a service as broadcast receiver for SMS received
AddReceiverText(SmsMonitor,
<intent-filter>
    <action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>)
SetReceiverAttribute(SmsMonitor, android:permission, "android.permission.BROADCAST_SMS")


'C2DM Permissions
AddManifestText(<permission android:name="$PACKAGE$.permission.C2D_MESSAGE" android:protectionLevel="signature" />)
AddPermission($PACKAGE$.permission.C2D_MESSAGE)
AddPermission(com.google.android.c2dm.permission.RECEIVE)
' Push Service Receiver Attribute
SetReceiverAttribute(PushService, android:permission, "com.google.android.c2dm.permission.SEND")
' Service Receiver Text
AddReceiverText(PushService,
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="$PACKAGE$" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="$PACKAGE$" />
</intent-filter>)

AddManifestText( <permission
          android:name="$PACKAGE$.permission.MAPS_RECEIVE"
          android:protectionLevel="signature"/>
      <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>)

AddApplicationText(<meta-data
    android:name="com.google.android.maps.v2.API_KEY"
    android:value="AIzaSyDQY3h-DKqTQ3BtRwHW2PDT4nwM2k5U1c4"/>
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"
    />)
AddPermission(android.permission.ACCESS_NETWORK_STATE)

' to uninstall shortcut TestApp SMS of previous app version
AddPermission(com.android.launcher.permission.UNINSTALL_SHORTCUT)

'End of default text.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
AddApplicationText items come before the standard activities. It is not possible to change the order.

However you can workaround this issue with a creative AddReplacement. You can add a stub string with AddApplicationText and then replace it with the Main activity declaration with AddReplacement.
Use another AddReplacement to comment out the original Main activity declaration.
 
Upvote 0

marcick

Well-Known Member
Licensed User
Longtime User
Ok, I'll manually move up the main activity declaration in the xml everytime I'll need to publish an update for the app.
I hope this will not create any sort of strange behaviour of the app.
Thanks
Marco
 
Upvote 0
Top