Skip to content

Commit

Permalink
feat: corrected Android Manifest hook
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanneff committed Jan 18, 2017
1 parent 92ad6fa commit 70232f1
Showing 1 changed file with 141 additions and 194 deletions.
335 changes: 141 additions & 194 deletions hooks/lib/android/androidManifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
var pathToManifest = path.join(context.opts.projectRoot, 'platforms', 'android', 'AndroidManifest.xml')
var manifest = xmlHelper.readXmlAsJson(pathToManifest)

// TODO: early exit

console.log('BRANCH SDK: Updating AndroidManifest.xml')

// update manifest
manifest = removePreviousOptions(manifest)
manifest = updateBranchKeyMetaData(manifest, preferences)
manifest = updateBranchReferrerTracking(manifest)
manifest = updateLaunchOptionToSingleTask(manifest, preferences)
Expand All @@ -28,11 +30,62 @@
xmlHelper.writeJsonAsXml(manifest, pathToManifest)
}

function updateBranchKeyMetaData (manifest, preferences) {
var metadatas = manifest['manifest']['application'][0]['meta-data'] || []
var androidName = 'io.branch.sdk.BranchKey'

// remove old
metadatas = removeBasedOnAndroidName(metadatas, androidName)

// add new
manifest['manifest']['application'][0]['meta-data'] = metadatas.concat([{
'$': {
'android:name': androidName,
'android:value': preferences.branchKey
}
}])

return manifest
}

function updateBranchReferrerTracking (manifest) {
var receivers = manifest['manifest']['application'][0]['receiver'] || []
var androidName = 'io.branch.referral.InstallListener'

// remove old
receivers = removeBasedOnAndroidName(receivers, androidName)

// add new
manifest['manifest']['application'][0]['receiver'] = receivers.concat([{
'$': {
'android:name': androidName,
'android:exported': true
},
'intent-filter': [{
'action': [{
'$': {
'android:name': 'com.android.vending.INSTALL_REFERRER'
}
}]
}]
}])

return manifest
}

function updateLaunchOptionToSingleTask (manifest, preferences) {
manifest['manifest']['application'][0]['activity'][0]['$']['android:launchMode'] = 'singleTask'
return manifest
}

function updateBranchURIScheme (manifest, preferences) {
var intentFilter = manifest['manifest']['application'][0]['activity'][0]['intent-filter'] || []
// TODO: need to validate main activity (second [0])
var intentFilters = manifest['manifest']['application'][0]['activity'][0]['intent-filter'] || []

manifest['manifest']['application'][0]['activity'][0]['intent-filter'] = intentFilter.concat([{
intentFilters = removeBasedOnIntentFilter(intentFilters)

// add new
manifest['manifest']['application'][0]['activity'][0]['intent-filter'] = intentFilters.concat([{
'action': [{
'$': {
'android:name': 'android.intent.action.VIEW'
Expand All @@ -58,147 +111,117 @@
return manifest
}

function updateLaunchOptionToSingleTask (manifest, preferences) {
manifest['manifest']['application'][0]['activity'][0]['$']['android:launchMode'] = 'singleTask'
return manifest
}

function updateBranchReferrerTracking (manifest) {
var receiver = manifest['manifest']['application'][0]['receiver'] || []
function updateBranchAppLinks (manifest, preferences) {
var intentFilters = manifest['manifest']['application'][0]['activity'][0]['intent-filter'] || []
var data = getAppLinkIntentFilterData(preferences)

manifest['manifest']['application'][0]['receiver'] = receiver.concat([{
// remove old (already done in updateBranchURIScheme)
// add new
manifest['manifest']['application'][0]['activity'][0]['intent-filter'] = intentFilters.concat([{
'$': {
'android:name': 'io.branch.referral.InstallListener',
'android:exported': true
'android:autoVerify': 'true'
},
'intent-filter': [{
'action': [{
'$': {
'android:name': 'com.android.vending.INSTALL_REFERRER'
}
}]
}]
}])

return manifest
}

function updateBranchKeyMetaData (manifest, preferences) {
var metadata = manifest['manifest']['application'][0]['meta-data'] || []

// loop through
// if exists, update
// if not, append
manifest['manifest']['application'][0]['meta-data'] = metadata.concat([{
'$': {
'android:name': 'io.branch.sdk.BranchKey',
'android:value': preferences.branchKey
}
'action': [{
'$': {
'android:name': 'android.intent.action.VIEW'
}
}],
'category': [{
'$': {
'android:name': 'android.intent.category.DEFAULT'
}
}, {
'$': {
'android:name': 'android.intent.category.BROWSABLE'
}
}],
'data': data
}])

return manifest
}

function removePreviousOptions (manifest) {
var activities = manifest['manifest']['application'][0]['activity']

activities.forEach(removeIntentFiltersFromActivity)
manifest['manifest']['application'][0]['activity'] = activities

return manifest
}

function removeIntentFiltersFromActivity (activity) {
var oldIntentFilters = activity['intent-filter']
var newIntentFilters = []
function getAppLinkIntentFilterData (preferences) {
var intentFilterData = []

if (oldIntentFilters == null || oldIntentFilters.length === 0) {
return
}
if (preferences.linkDomain.indexOf('app.link') !== -1) {
// app.link
var first = preferences.linkDomain.split('.')[0]
var rest = preferences.linkDomain.split('.').slice(2).join('.')
var alternate = first + '-alternate' + '.' + rest

oldIntentFilters.forEach(function (intentFilter) {
if (!isIntentFilterForUniversalLinks(intentFilter)) {
newIntentFilters.push(intentFilter)
intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain))
intentFilterData.push(getAppLinkIntentFilterDictionary(alternate))
} else if (preferences.linkDomain.indexOf('bnc.lt') !== -1) {
// bnc.lt
if (preferences.androidPrefix == null) {
throw new Error('Branch SDK plugin is missing "android-prefix" in <branch-config> in your config.xml')
}
})

activity['intent-filter'] = newIntentFilters
}

function isIntentFilterForUniversalLinks (intentFilter) {
var actions = intentFilter['action']
var categories = intentFilter['category']
var data = intentFilter['data']
intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain, preferences.androidPrefix))
} else {
// custom
intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain))
}

return isActionForUniversalLinks(actions) && isCategoriesForUniversalLinks(categories) && isDataTagForUniversalLinks(data)
return intentFilterData
}

function isActionForUniversalLinks (actions) {
// there can be only 1 action
if (actions == null || actions.length !== 1) {
return false
function getAppLinkIntentFilterDictionary (linkDomain, androidPrefix) {
var scheme = 'https'
var output = {
'$': {
'android:host': linkDomain,
'android:scheme': scheme
}
}

var action = actions[0]['$']['android:name']

return action === 'android.intent.action.VIEW'
}

function isCategoriesForUniversalLinks (categories) {
// there can be only 2 categories
if (categories == null || categories.length !== 2) {
return false
if (androidPrefix) {
output['$']['android:pathPrefix'] = androidPrefix
}

var isBrowsable = false
var isDefault = false
return output
}

// check intent categories
categories.forEach(function (category) {
var categoryName = category['$']['android:name']
if (!isBrowsable) {
isBrowsable = categoryName === 'android.intent.category.BROWSABLE'
}
function removeBasedOnIntentFilter (items) {
var without = []
for (var i = 0; i < items.length; i++) {
var item = items[i]
if (item.hasOwnProperty('action') && item.hasOwnProperty('category') && item.hasOwnProperty('data')) {
var actions = item['action']
var categories = item['category']
var data = item['data']

if (actions.length === 1 && actions[0]['$'].hasOwnProperty('android:name') && actions[0]['$']['android:name'] === 'android.intent.action.VIEW' && categories.length === 2 && categories[0]['$'].hasOwnProperty('android:name') && (categories[0]['$']['android:name'] === 'android.intent.category.DEFAULT' || categories[0]['$']['android:name'] === 'android.intent.category.BROWSABLE') && categories[1]['$'].hasOwnProperty('android:name') && (categories[1]['$']['android:name'] === 'android.intent.category.DEFAULT' || categories[1]['$']['android:name'] === 'android.intent.category.BROWSABLE') && data.length > 0 && data.length < 3) {
// URI Scheme
if (data[0]['$'].hasOwnProperty('android:scheme') && data[0]['$'].hasOwnProperty('android:host') && data[0]['$']['android:host'] === 'open' && !item.hasOwnProperty('$')) {
continue
}

if (!isDefault) {
isDefault = categoryName === 'android.intent.category.DEFAULT'
// AppLink
if (data[0]['$'].hasOwnProperty('android:host') && data[0]['$'].hasOwnProperty('android:scheme') && data[0]['$']['android:scheme'] === 'https' && item.hasOwnProperty('$') && item['$'].hasOwnProperty('android:autoVerify') && item['$']['android:autoVerify'] === 'true') {
continue
}
}
}
})

return isDefault && isBrowsable
}

function isDataTagForUniversalLinks (data) {
// can have only 1 data tag in the intent-filter
if (data == null || data.length !== 1) {
return false
without.push(item)
}

var dataHost = data[0]['$']['android:host']
var dataScheme = data[0]['$']['android:scheme']
var hostIsSet = dataHost != null && dataHost.length > 0
var schemeIsSet = dataScheme != null && dataScheme.length > 0

return hostIsSet && schemeIsSet
return without
}

function updateBranchAppLinks (manifest, preferences) {
var activitiesList = manifest['manifest']['application'][0]['activity']
var launchActivityIndex = getMainLaunchActivityIndex(activitiesList)
var intentFilters = createAppLinkIntentFilter(preferences)
var launchActivity

if (launchActivityIndex < 0) {
throw new Error('BRANCH SDK - Could not find launch activity in the AndroidManifest file. Can\'t inject Universal Links preferences.')
function removeBasedOnAndroidName (items, androidName) {
var without = []
for (var i = 0; i < items.length; i++) {
var item = items[i]
if (item.hasOwnProperty('$') && item['$'].hasOwnProperty('android:name')) {
var key = item['$']['android:name']
if (key === androidName) {
continue
}
without.push(item)
}
}

// get launch activity
launchActivity = activitiesList[launchActivityIndex]

// add Universal Links intent-filters to the launch activity
launchActivity['intent-filter'] = launchActivity['intent-filter'].concat(intentFilters)

return manifest
return without
}

function getMainLaunchActivityIndex (activities) {
Expand Down Expand Up @@ -240,80 +263,4 @@

return isLauncher
}

function createAppLinkIntentFilter (preferences) {
var data = getAppLinkIntentFilterData(preferences)
var intentFilter = [{
'$': {
'android:autoVerify': 'true'
},
'action': [{
'$': {
'android:name': 'android.intent.action.VIEW'
}
}],
'category': [{
'$': {
'android:name': 'android.intent.category.DEFAULT'
}
}, {
'$': {
'android:name': 'android.intent.category.BROWSABLE'
}
}],
'data': data
}]

return intentFilter
}

function getAppLinkIntentFilterData (preferences) {
var intentFilterData = []

if (preferences.linkDomain.indexOf('app.link') !== -1) {
// app.link
var first = preferences.linkDomain.split('.')[0]
var rest = preferences.linkDomain.split('.').slice(2).join('.')
var alternate = first + '-alternate' + '.' + rest

intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain))
intentFilterData.push(getAppLinkIntentFilterDictionary(alternate))
} else if (preferences.linkDomain.indexOf('bnc.lt') !== -1) {
// bnc.lt
if (preferences.androidPrefix == null) {
throw new Error('Branch SDK plugin is missing "android-prefix" in <branch-config> in your config.xml')
}

intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain, preferences.androidPrefix))
} else {
// custom
intentFilterData.push(getAppLinkIntentFilterDictionary(preferences.linkDomain))
}

return intentFilterData
}

function getAppLinkIntentFilterDictionary (linkDomain, androidPrefix) {
var scheme = 'https'
var output

if (androidPrefix) {
output = {
'$': {
'android:host': linkDomain,
'android:scheme': scheme,
'android:pathPrefix': androidPrefix
}
}
} else {
output = {
'$': {
'android:host': linkDomain,
'android:scheme': scheme
}
}
}

return output
}
})()

0 comments on commit 70232f1

Please sign in to comment.