Java Question Malformed token: Changes in Manifest for Google CAST library.

DonManfred

Expert
Licensed User
Longtime User
Hello,

i´m doing a wrap for Google CAST Companion library.

Manifest file
Your AndroidManifest.xml file needs to include certain elements and metadata for the library
and Cast SDK proper operation. Here is a list of requirements:
● Minimum SDK version: the minimum SDK version that the library works with is 10
(GingerBread 2.3.3).
● Permissions: using the cast functionality does not require any additional permissions. If,
however, you have enabled Wifi Reconnection, then you need to add the following two
permissions:
<uses‐permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses‐permission android:name="android.permission.ACCESS_WIFI_STATE" />
● Application Metadata: like any application that uses the Play Services, add the following
metadata to your application declaration:
<meta‐data android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />
● Makes sure your application’s “theme” is correctly set based on the min SDK version.
For example, you may need to use a variant of Theme.AppCompat or one that inherits from
these themes. Note that this is not a requirement for Cast, per se.
● Declare the VideoCastControllerActivity:
<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.player.V
ideoCastControllerActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName="*MY_PARENT_ACTIVITY*"
android:theme="*AN_OVERLAY_THEME*">
<meta‐data
android:name="android.support.PARENT_ACTIVITY"
android:value="*MY_PARENT_ACTIVITY*" />
<intent‐filter>
<action android:name="android.intent.action.MAIN" />
</intent‐filter>
</activity>

There are a few things that you need to set in the above snippet. Assume that user clicks
on your application’s notification from the status bar while casting. The desired behavior
is to open your application and direct user to the VideoCastControllerActivity .
However, this is not the main launcher activity of your application, so if the user were to
press the back button, she should not be sent out of the application. The correct
behavior would be to send the user to the parent activity; the one that in a normal
execution of your application would show up when user goes back from the
VideoCastControllerActivity . This special behavior can be achieved by creating a
“back stack” for the PendingIntent in the notification service. CCL can handle this for you
if you declare in the manifest what activity is the correct parent activity; you need to enter
the name of that activity (possibly a fully qualified name) in the two places in the above
snippet where the *PARENT_ACTIVITY* is used 9. In addition, the
VideoCastControllerActivity is best presented if you use an overlay Action Bar theme;
the CastVideos reference app defines one ( DemocastOverlay ) that you can copy and use,
or you can define your own theme; it should replace the *AN_OVERLAY_THEME* in the
above snippet.
● Declare the following receiver:
<receiver
android:name="com.google.android.libraries.cast.companionlibrary.remotecontrol.VideoI
ntentReceiver" />
This receiver is called when user interacts with the VideoCastNotificationService (or
from Lock Screen) where it broadcasts a message that this receiver catches and
handles appropriately. There is no need to define any Intent Filter for this receiver.
● Declare the following service (for VideoCastNotificationService ):
<service
android:name="com.google.android.libraries.cast.companionlibrary.notification.VideoCa
stNotificationService"/>

● Declare the following service (for ReconnectionService ) if Wifi Reconnection has been
enabled:
<service
android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.Re
connectionService"/>
9 See http://developer.android.com/guide/topics/ui/notifiers/notifications.html#NotificationResponse for more
information
● If you want to enable Captions in the preferences, declare the following activity in your
manifest:
<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.tracks.C
aptionsPreferenceActivity"
android:label="@string/action_settings" >
<intent‐filter>
<action android:name="android.intent.action.MAIN" />
</intent‐filter>
</activity>

I tried to use this manifest code to add the needed things:

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: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="19"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.
'************ Google Play Services Base ************
AddApplicationText(
   <activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
  android:theme="@android:style/Theme.Translucent.NoTitleBar"
  android:exported="false"/>
    <meta-data
  android:name="com.google.android.gms.version"
  android:value="@integer/google_play_services_version" />
)
'************ Google Play Services Base (end) ************

'************ Firebase Base ************
CreateResourceFromFile("google-services", "google-services.json")
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)
AddPermission(com.google.android.c2dm.permission.RECEIVE)
AddPermission(${applicationId}.permission.C2D_MESSAGE)
AddManifestText( <permission android:name="${applicationId}.permission.C2D_MESSAGE"
  android:protectionLevel="signature" />)
AddApplicationText(
<receiver
  android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
  android:enabled="true">
  <intent-filter>
  <action android:name="com.google.android.gms.measurement.UPLOAD"/>
  </intent-filter>
  </receiver>

  <service
  android:name="com.google.android.gms.measurement.AppMeasurementService"
  android:enabled="true"
  android:exported="false"/>
   <provider
  android:authorities="${applicationId}.firebaseinitprovider"
  android:name="com.google.firebase.provider.FirebaseInitProvider"
  android:exported="false"
  android:initOrder="100" />
    <receiver
  android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
  android:enabled="true">
  <intent-filter>
  <action android:name="com.google.android.gms.measurement.UPLOAD"/>
  </intent-filter>
  </receiver>

  <service
  android:name="com.google.android.gms.measurement.AppMeasurementService"
  android:enabled="true"
  android:exported="false"/>
   <receiver
  android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
  android:exported="true"
  android:permission="com.google.android.c2dm.permission.SEND" >
  <intent-filter>
  <action android:name="com.google.android.c2dm.intent.RECEIVE" />
  <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
  <category android:name="${applicationId}" />
  </intent-filter>
  </receiver>
    <receiver
  android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
  android:exported="false" />


  <service
  android:name="com.google.firebase.iid.FirebaseInstanceIdService"
  android:exported="true">
  <intent-filter android:priority="-500">
  <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
  </intent-filter>
  </service>
)
'************ Firebase Base (end) ************

'************ Google CAST ********************
AddApplicationText(<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.player.VideoCastControllerActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName=".main"
android:theme="* am overlay theme">
<meta‐data
android:name="android.support.PARENT_ACTIVITY"
android:value=".main" />
<intent‐filter>
<action android:name="android.intent.action.MAIN" />
</intent‐filter>
</activity>
<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.tracks.CaptionsPreferenceActivity"
android:label="@string/action_settings" >
<intent‐filter>
<action android:name="android.intent.action.MAIN" />
</intent‐filter>
</activity
<receiver
android:name="com.google.android.libraries.cast.companionlibrary.remotecontrol.VideoIntentReceiver" />
<service
android:name="com.google.android.libraries.cast.companionlibrary.notification.VideoCastNotificationService"/>
<service
android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>
)

SetActivityAttribute(Main, android:theme, @style/CustomActTheme)
CreateResource(values, theme.xml,
<resources>
  <style name="CustomActTheme" parent="@android:style/ThemeOverlay.Material.Light.ActionBar">
  <item name="android:windowIsTranslucent">true</item>
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowContentOverlay">@null</item>
  <item name="android:windowNoTitle">true</item>
  <item name="android:backgroundDimEnabled">true</item>
  <item name="android:windowFullscreen">true</item>
  <item name="android:colorPrimary">#f44336</item> <!-- action bar -->
  <item name="android:colorPrimaryDark">#b71c1c</item> <!-- status bar -->
  <item name="android:colorAccent">#2DEA6A</item> <!-- Seekbar,, checkboxes,, switches,, etc. -->
  <item name="android:textColorPrimary">#00FF00</item> <!-- ? -->
  <item name="android:textColorSecondary">#FF00FF</item> <!-- inactive editText line,, scrollbar -->
  <item name="android:textColor">#000000</item> <!-- menu text,, msgbox title -->
  <item name="android:textColorLink">#b71c1c</item>
  <item name="android:textColorHighlight">#FF9F9F</item>
</style>
</resources>
)

I´m not sure if
android:parentActivityName=".main"
is set correct to the mainactivity of my app... Even not if the following is correct to reference the main activity of the app.

<meta‐data
android:name="android.support.PARENT_ACTIVITY"
android:value=".main" />

The Apps packagename is de.donmanfred.cast and i want to reference the Main activity as Parent of the Castactivity.

But when i try to compile i get the error

B4A version: 6.00
Parsing code. (0.00s)
Compiling code. (0.07s)
Compiling layouts code. (0.00s)
Organizing libraries. (0.00s)
Generating R file. Error
AndroidManifest.xml:89: error: Error parsing XML: not well-formed (invalid token)

But i dont know WHICH token is invalid.

Can anybody bring some light into this?
 

eps

Expert
Licensed User
Longtime User
Isn't the error on line 89? indicated by this "AndroidManifest.xml:89: error: Error parsing XML: not well-formed (invalid token)"

AddApplicationText(<activity

This doesn't look quite right - as long as it's the complete manifest file, which I assume it is. Alternatively grab the contents of the actual androidmanifest.XML file and try the online W3C XML validator which is free to use.
 

DonManfred

Expert
Licensed User
Longtime User
Update

Actual manifest

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: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="19"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

'************ Google Play Services Base ************
AddApplicationText(
   <activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
  android:theme="@android:style/Theme.Translucent.NoTitleBar"
  android:exported="false"/>
    <meta-data
  android:name="com.google.android.gms.version"
  android:value="@integer/google_play_services_version" />
)
'************ Google Play Services Base (end) ************

'************ Firebase Base ************
CreateResourceFromFile("google-services", "google-services.json")
'************ Firebase Base (end) ************
' ******************* GOOGLE CAST *****************
AddApplicationText(<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.player.VideoCastControllerActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName="de.donmanfred.cast.main"
android:theme="*AN_OVERLAY_THEME*">
<meta‐data android:name="android.support.PARENT_ACTIVITY"
android:value="de.donmanfred.cast.main" />
<intent‐filter>
<action android:name="android.intent.action.MAIN" />
</intent‐filter>
</activity>)

castsdk0053.png


i guess it is this line which causes the error (line 34):
android:parentActivityName="de.donmanfred.cast.main"

Reading this i guess it should be correct


I also need to set a OVERLAY Theme in this line:

android:theme="*AN_OVERLAY_THEME*"

But first i need to get rid of the malformed token :)
 

NJDude

Expert
Licensed User
Longtime User
Maybe there's an illegal character, type that line manually, perhaps the copy-past added one.
 

eps

Expert
Licensed User
Longtime User
Hmm... but the actual manifest file should look like this...

B4X:
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="mindware.html5"
    android:versionCode="1"
    android:versionName=""
    android:installLocation="internalOnly">
   
    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14"/>
    <supports-screens android:largeScreens="true" 
       android:normalScreens="true" 
       android:smallScreens="true" 
       android:anyDensity="true"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:icon="@drawable/icon"
        android:label="Demo">
        <activity
            android:windowSoftInputMode="stateHidden"
            android:launchMode="singleTop"
            android:name=".main"
            android:label="Demo"
            android:screenOrientation="landscape"
            android:configChanges="orientation|keyboardHidden|screenSize">
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
           
        </activity>
    </application>
</manifest>

There will be something in there that is fairly innoccous but that XML doesn't expect to find. Usually it's a & character that gets in there....
 

MarcoRome

Expert
Licensed User
Longtime User
Hi Don. i see in your manifest:
B4X:
1 - AddApplicationText(<activity
2 - android:name="com.google.android.libraries.cast.companionlibrary.cast.player.VideoCastControllerActivity"
3 - android:screenOrientation="portrait"
4 - android:label="@string/app_name"
5 - android:launchMode="singleTask"
6 - android:parentActivityName="de.donmanfred.cast.main"
7 - android:theme="*AN_OVERLAY_THEME*">
8 - <meta‐data android:name="android.support.PARENT_ACTIVITY"
9 - android:value="de.donmanfred.cast.main" />
10 - <intent‐filter>
11- <action android:name="android.intent.action.MAIN" />
12 - </intent‐filter>
13 - </activity>)

where :

line 1 close with line 13 ( <activity .... </activity> )
line 8 close with 9 ( <meta‐data ... /> )
line 10 close with 12 ( <intent‐filter> ... </intent‐filter> )

But line 7 ( ..."*AN_OVERLAY_THEME*"> ) close with ???
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Open the generated manifest file and you will see that there are squares instead of hyphens:

SS-2016-07-26_08.35.52.png


The hyphen character in the snippet you posted is this one: http://unicode-table.com/en/#2010

Instead of http://unicode-table.com/en/#002D

There was also a missing > character.

Fixed code:
B4X:
'************ Google CAST ********************
AddApplicationText(<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.player.VideoCastControllerActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName=".main"
android:theme="* am overlay theme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".main" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="com.google.android.libraries.cast.companionlibrary.cast.tracks.CaptionsPreferenceActivity"
android:label="@string/action_settings" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<receiver
android:name="com.google.android.libraries.cast.companionlibrary.remotecontrol.VideoIntentReceiver" />
<service
android:name="com.google.android.libraries.cast.companionlibrary.notification.VideoCastNotificationService"/>
<service
android:name="com.google.android.libraries.cast.companionlibrary.cast.reconnection.ReconnectionService"/>
)
 

Syd Wright

Well-Known Member
Licensed User
My goodness, this is complicated material! Good to see that people like Don are experimenting with the wonderful Google Chromecast. Isn't there an easier way to just send a Radio stream, TV stream or an Audio or Video file to the Chromecast? I have looked at the API, but it is way over my head...

I am also looking for a way to make the Chromecast automatically reconnect to the Android device (to mirror its screen).
Sadly currently it is necessary to re-connect manually everytime the Chromecast and/or the device is switched off. This involves half a dozen manual operations... So far all I have found are solutions that use "Tasker".
 
Top