diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b97ca20b..6418dd91d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -119,6 +119,7 @@ jobs: cd android echo -e "" > ./gradle.properties + echo -e "android.enableAapt2=false" >> ./gradle.properties if [[ $KEYSTORE ]]; then echo $KEYSTORE_BASE64 | base64 --decode > ./app/$KEYSTORE diff --git a/__tests__/__snapshots__/RoomItem.js.snap b/__tests__/__snapshots__/RoomItem.js.snap index 988f57af1..8b1bf18ed 100644 --- a/__tests__/__snapshots__/RoomItem.js.snap +++ b/__tests__/__snapshots__/RoomItem.js.snap @@ -585,7 +585,7 @@ exports[`render unread +999 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/name?random=0", + "uri": "/avatar/name", } } style={ @@ -835,7 +835,7 @@ exports[`render unread 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/name?random=0", + "uri": "/avatar/name", } } style={ @@ -1085,7 +1085,7 @@ exports[`renders correctly 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/name?random=0", + "uri": "/avatar/name", } } style={ diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap index 86fba1e32..7dc94d89e 100644 --- a/__tests__/__snapshots__/Storyshots.test.js.snap +++ b/__tests__/__snapshots__/Storyshots.test.js.snap @@ -62,7 +62,7 @@ exports[`Storyshots Avatar avatar 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/test?random=0", + "uri": "/avatar/test", } } style={ @@ -136,7 +136,7 @@ exports[`Storyshots Avatar avatar 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/aa?random=0", + "uri": "/avatar/aa", } } style={ @@ -210,7 +210,7 @@ exports[`Storyshots Avatar avatar 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/bb?random=0", + "uri": "/avatar/bb", } } style={ @@ -284,7 +284,7 @@ exports[`Storyshots Avatar avatar 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/test?random=0", + "uri": "/avatar/test", } } style={ @@ -393,7 +393,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/rocket.cat?random=0", + "uri": "/avatar/rocket.cat", } } style={ @@ -615,7 +615,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/rocket.cat?random=0", + "uri": "/avatar/rocket.cat", } } style={ @@ -841,7 +841,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/rocket.cat?random=0", + "uri": "/avatar/rocket.cat", } } style={ @@ -1086,7 +1086,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries?random=0", + "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries", } } style={ @@ -1335,7 +1335,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries?random=0", + "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries", } } style={ @@ -1580,7 +1580,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries?random=0", + "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries", } } style={ @@ -1825,7 +1825,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries?random=0", + "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries", } } style={ @@ -2070,7 +2070,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries?random=0", + "uri": "/avatar/Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries", } } style={ @@ -2315,7 +2315,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/W?random=0", + "uri": "/avatar/W", } } style={ @@ -2537,7 +2537,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/WW?random=0", + "uri": "/avatar/WW", } } style={ @@ -2759,7 +2759,7 @@ exports[`Storyshots Channel Cell Direct Messages 1`] = ` source={ Object { "priority": "high", - "uri": "/avatar/?random=0", + "uri": "/avatar/", } } style={ diff --git a/android/app/build.gradle b/android/app/build.gradle index 8c3ef1d6d..f2b8268d6 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -91,12 +91,12 @@ def enableProguardInReleaseBuilds = false android { compileSdkVersion 27 - buildToolsVersion "27.0.1" + buildToolsVersion "27.0.3" defaultConfig { applicationId "chat.rocket.reactnative" minSdkVersion 16 - targetSdkVersion 23 + targetSdkVersion 27 versionCode VERSIONCODE as Integer versionName "1" ndk { diff --git a/android/build.gradle b/android/build.gradle index d29444e96..50a29364b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,9 +3,11 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' + // classpath 'com.android.tools.build:gradle:2.2.3' + classpath 'com.android.tools.build:gradle:3.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -16,6 +18,7 @@ allprojects { repositories { mavenLocal() jcenter() + google() maven { url 'https://maven.google.com' } diff --git a/android/gradle.properties b/android/gradle.properties index d94d12020..09f80d126 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -16,5 +16,6 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +android.enableAapt2=false android.useDeprecatedNdk=true VERSIONCODE=999999999 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index dbdc05d27..05dbf0ca4 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +# distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/app/containers/Avatar.js b/app/containers/Avatar.js index 42b52c794..b25d0f7a5 100644 --- a/app/containers/Avatar.js +++ b/app/containers/Avatar.js @@ -4,7 +4,6 @@ import { connect } from 'react-redux'; import { StyleSheet, Text, View, ViewPropTypes } from 'react-native'; import FastImage from 'react-native-fast-image'; import avatarInitialsAndColor from '../utils/avatarInitialsAndColor'; -import database from '../lib/realm'; const styles = StyleSheet.create({ iconContainer: { @@ -42,37 +41,38 @@ export default class Avatar extends React.PureComponent { borderRadius: 2, forceInitials: false }; - state = { showInitials: true, user: {} }; + state = { showInitials: true }; - componentDidMount() { - const { text, type } = this.props; - if (type === 'd') { - this.users = this.userQuery(text); - this.users.addListener(this.update); - this.update(); - } - } + // componentDidMount() { + // const { text, type } = this.props; + // if (type === 'd') { + // this.users = this.userQuery(text); + // this.users.addListener(this.update); + // this.update(); + // } + // } - componentWillReceiveProps(nextProps) { - if (nextProps.text !== this.props.text && nextProps.type === 'd') { - if (this.users) { - this.users.removeAllListeners(); - } - this.users = this.userQuery(nextProps.text); - this.users.addListener(this.update); - this.update(); - } - } + // componentWillReceiveProps(nextProps) { + // if (nextProps.text !== this.props.text && nextProps.type === 'd') { + // if (this.users) { + // this.users.removeAllListeners(); + // } + // this.users = this.userQuery(nextProps.text); + // this.users.addListener(this.update); + // this.update(); + // } + // } - componentWillUnmount() { - if (this.users) { - this.users.removeAllListeners(); - } - } + // componentWillUnmount() { + // if (this.users) { + // this.users.removeAllListeners(); + // } + // } - get avatarVersion() { - return (this.state.user && this.state.user.avatarVersion) || 0; - } + // get avatarVersion() { + // // return (this.state.user && this.state.user.avatarVersion) || 0; + // return 0; + // } /** FIXME: Workaround * While we don't have containers/components structure, this is breaking tests. @@ -80,21 +80,21 @@ export default class Avatar extends React.PureComponent { * and we would have a avatar container in charge of making queries. * Also, it would make possible to write unit tests like these. */ - userQuery = (username) => { - if (database && database.databases && database.databases.activeDB) { - return database.objects('users').filtered('username = $0', username); - } - return { - addListener: () => {}, - removeAllListeners: () => {} - }; - } + // userQuery = (username) => { + // if (database && database.databases && database.databases.activeDB) { + // return database.objects('users').filtered('username = $0', username); + // } + // return { + // addListener: () => {}, + // removeAllListeners: () => {} + // }; + // } - update = () => { - if (this.users.length) { - this.setState({ user: this.users[0] }); - } - } + // update = () => { + // if (this.users.length) { + // this.setState({ user: this.users[0] }); + // } + // } render() { const { @@ -123,7 +123,7 @@ export default class Avatar extends React.PureComponent { let image; if (type === 'd' && !forceInitials) { - const uri = avatar || `${ baseUrl }/avatar/${ text }?random=${ this.avatarVersion }`; + const uri = avatar || `${ baseUrl }/avatar/${ text }`; image = uri ? ( { + this.connection.onopen = async() => { this.emit('open'); resolve(); this.ddp.emit('open'); - return this._login && this.login(this._login); + if (this._login) { + return this.login(this._login).catch(e => console.warn(e)); + } }; this.connection.onclose = debounce((e) => { this.emit('disconnected', e); @@ -260,6 +262,9 @@ export default class Socket extends EventEmitter { msg: 'method', method, params }).then(data => data.result || data.subs).catch((err) => { log('DDP call Error', err); + if (err && /you've been logged out by the server/i.test(err.reason)) { + return this.emit('forbidden'); + } return Promise.reject(err); }); } diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 715f2ff4d..1e196474a 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -1,4 +1,4 @@ -import { AsyncStorage, Platform, InteractionManager } from 'react-native'; +import { AsyncStorage, Platform } from 'react-native'; import { hashPassword } from 'react-native-meteor/lib/utils'; import foreach from 'lodash/forEach'; import RNFetchBlob from 'react-native-fetch-blob'; @@ -10,7 +10,7 @@ import database from './realm'; import log from '../utils/log'; // import * as actions from '../actions'; -import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure } from '../actions/login'; +import { setUser, setLoginServices, removeLoginServices, loginRequest, loginSuccess, loginFailure, logout } from '../actions/login'; import { disconnect, connectSuccess, connectFailure } from '../actions/connect'; import { setActiveUser } from '../actions/activeUsers'; import { starredMessagesReceived, starredMessageUnstarred } from '../actions/starredMessages'; @@ -80,11 +80,26 @@ const RocketChat = { }, async testServer(url) { if (/^(https?:\/\/)?(((\w|[0-9-_])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) { - const response = await fetch(url, { method: 'HEAD' }); - if (response.status === 200 && response.headers.get('x-instance-id') != null && response.headers.get('x-instance-id').length) { - return url; + try { + let response = await RNFetchBlob.fetch('HEAD', url); + response = response.respInfo; + if (response.status === 200 && response.headers['x-instance-id'] != null && response.headers['x-instance-id'].length) { + return url; + } + } catch (e) { + log('testServer', e); } } + // if (/^(https?:\/\/)?(((\w|[0-9-_])+(\.(\w|[0-9-_])+)+)|localhost)(:\d+)?$/.test(url)) { + // try { + // const response = await fetch(url, { method: 'HEAD' }); + // if (response.status === 200 && response.headers.get('x-instance-id') != null && response.headers.get('x-instance-id').length) { + // return url; + // } + // } catch (error) { + // console.log(error) + // } + // } throw new Error({ error: 'invalid server' }); }, _setUser(ddpMessage) { @@ -151,6 +166,8 @@ const RocketChat = { this.ddp.on('loginError', protectedFunction(err => reduxStore.dispatch(loginFailure(err)))); + this.ddp.on('forbidden', protectedFunction(() => reduxStore.dispatch(logout()))); + this.ddp.on('users', protectedFunction(ddpMessage => RocketChat._setUser(ddpMessage))); this.ddp.on('background', () => this.getRooms().catch(e => log('background getRooms', e))); @@ -163,7 +180,7 @@ const RocketChat = { })); this.ddp.once('logged', protectedFunction(({ id }) => { this.subscribeRooms(id); - this.ddp.subscribe('stream-notify-logged', 'updateAvatar', false); + // this.ddp.subscribe('stream-notify-logged', 'updateAvatar', false); })); this.ddp.on('disconnected', protectedFunction(() => { @@ -185,20 +202,22 @@ const RocketChat = { return reduxStore.dispatch(someoneTyping({ _rid, username: ddpMessage.fields.args[0], typing: ddpMessage.fields.args[1] })); })); - this.ddp.on('stream-notify-logged', (ddpMessage) => { - // this entire logic needs a better solution - // we're using it only because our image cache lib doesn't support clear cache - if (ddpMessage.fields && ddpMessage.fields.eventName === 'updateAvatar') { - const { args } = ddpMessage.fields; - InteractionManager.runAfterInteractions(() => - args.forEach((arg) => { - const user = database.objects('users').filtered('username = $0', arg.username); - if (user.length > 0) { - user[0].avatarVersion += 1; - } - })); - } - }); + // this.ddp.on('stream-notify-logged', (ddpMessage) => { + // // this entire logic needs a better solution + // // we're using it only because our image cache lib doesn't support clear cache + // if (ddpMessage.fields && ddpMessage.fields.eventName === 'updateAvatar') { + // const { args } = ddpMessage.fields; + // InteractionManager.runAfterInteractions(() => + // args.forEach((arg) => { + // const user = database.objects('users').filtered('username = $0', arg.username); + // if (user.length > 0) { + // database.write(() => { + // user[0].avatarVersion += 1; + // }); + // } + // })); + // } + // }); // this.ddp.on('stream-notify-user', protectedFunction((ddpMessage) => { // console.warn('rc.stream-notify-user') diff --git a/app/views/ProfileView/index.js b/app/views/ProfileView/index.js index c4c0b8ad7..882f8406b 100644 --- a/app/views/ProfileView/index.js +++ b/app/views/ProfileView/index.js @@ -424,6 +424,7 @@ export default class ProfileView extends LoggedView { onChangeText={value => this.setState({ typedPassword: value })} secureTextEntry testID='profile-view-typed-password' + style={styles.dialogInput} /> diff --git a/app/views/ProfileView/styles.js b/app/views/ProfileView/styles.js index 3ac375c40..bf38e0a1a 100644 --- a/app/views/ProfileView/styles.js +++ b/app/views/ProfileView/styles.js @@ -1,4 +1,4 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Platform } from 'react-native'; export default StyleSheet.create({ avatarContainer: { @@ -20,5 +20,14 @@ export default StyleSheet.create({ marginRight: 15, marginBottom: 15, borderRadius: 2 - } + }, + dialogInput: Platform.select({ + ios: {}, + android: { + borderRadius: 4, + borderColor: 'rgba(0,0,0,.15)', + borderWidth: 2, + paddingHorizontal: 10 + } + }) }); diff --git a/app/views/RoomView/ListView.js b/app/views/RoomView/ListView.js index 95443cfca..b6375b1a0 100644 --- a/app/views/RoomView/ListView.js +++ b/app/views/RoomView/ListView.js @@ -1,7 +1,7 @@ import { ListView as OldList } from 'realm/react-native'; import React from 'react'; import cloneReferencedElement from 'react-clone-referenced-element'; -import { ScrollView, ListView as OldList2, LayoutAnimation } from 'react-native'; +import { ScrollView, ListView as OldList2 } from 'react-native'; import moment from 'moment'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; @@ -57,7 +57,7 @@ export class List extends React.Component { updateState = throttle(() => { // this.setState({ this.dataSource = this.dataSource.cloneWithRows(this.data); - LayoutAnimation.easeInEaseOut(); + // LayoutAnimation.easeInEaseOut(); this.forceUpdate(); // }); }, 1000); @@ -71,7 +71,7 @@ export class List extends React.Component { onEndReachedThreshold={100} renderFooter={this.props.renderFooter} renderHeader={() => } - onEndReached={() => this.props.onEndReached(this.data[this.data.length - 1])} + onEndReached={() => this.props.onEndReached(this.data[this.data.length - 1], this.data.length)} dataSource={this.dataSource} renderRow={(item, previousItem) => this.props.renderRow(item, previousItem)} initialListSize={20} diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js index bb56f99d7..82da6864c 100644 --- a/app/views/RoomView/index.js +++ b/app/views/RoomView/index.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Text, View, Button } from 'react-native'; +import { Text, View, Button, LayoutAnimation } from 'react-native'; import { connect } from 'react-redux'; // import { bindActionCreators } from 'redux'; import equal from 'deep-equal'; @@ -91,8 +91,8 @@ export default class RoomView extends LoggedView { this.onEndReached.stop(); } - onEndReached = debounce((lastRowData) => { - if (!lastRowData) { + onEndReached = debounce((lastRowData, length) => { + if (!lastRowData || length < 20) { this.setState({ end: true }); return; } @@ -141,6 +141,7 @@ export default class RoomView extends LoggedView { } sendMessage = (message) => { + LayoutAnimation.easeInEaseOut(); RocketChat.sendMessage(this.rid, message).then(() => { this.props.setLastOpen(null); }); diff --git a/app/views/RoomsListView/Header/index.js b/app/views/RoomsListView/Header/index.js index 180f455a9..ca94f952d 100644 --- a/app/views/RoomsListView/Header/index.js +++ b/app/views/RoomsListView/Header/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import { Text, View, Platform, TouchableOpacity, TextInput } from 'react-native'; +import { Text, View, Platform, TouchableOpacity, TextInput, LayoutAnimation } from 'react-native'; import Icon from 'react-native-vector-icons/Ionicons'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; @@ -68,10 +68,13 @@ export default class RoomsListHeaderView extends React.Component { }; } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps, nextState) { if (!equal(this.props, nextProps)) { return true; } + if (!equal(this.state, nextState)) { + return true; + } return false; } @@ -89,14 +92,20 @@ export default class RoomsListHeaderView extends React.Component { } onPressCancelSearchButton() { - this.setState({ searching: false }); - this.props.setSearch(''); + requestAnimationFrame(() => { + LayoutAnimation.easeInEaseOut(); + this.setState({ searching: false }); + this.props.setSearch(''); + }); } onPressSearchButton() { - this.setState({ searching: true }); requestAnimationFrame(() => { - this.inputSearch.focus(); + LayoutAnimation.easeInEaseOut(); + this.setState({ searching: true }); + if (this.inputSearch) { + this.inputSearch.focus(); + } }); } diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js index 444ebee3b..04973616e 100644 --- a/app/views/RoomsListView/index.js +++ b/app/views/RoomsListView/index.js @@ -2,7 +2,7 @@ import ActionButton from 'react-native-action-button'; import React from 'react'; import PropTypes from 'prop-types'; import Icon from 'react-native-vector-icons/Ionicons'; -import { Platform, View, TextInput, FlatList, LayoutAnimation } from 'react-native'; +import { Platform, View, TextInput, FlatList } from 'react-native'; import { connect } from 'react-redux'; import database from '../../lib/realm'; @@ -70,7 +70,7 @@ export default class RoomsListView extends LoggedView { } updateState = debounce(() => { - LayoutAnimation.easeInEaseOut(); + // LayoutAnimation.easeInEaseOut(); this.setState({ rooms: this.data.slice() }); }) diff --git a/package-lock.json b/package-lock.json index fa0052327..ad8c505e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1243,9 +1243,9 @@ } }, "@types/react-native": { - "version": "0.55.18", - "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.55.18.tgz", - "integrity": "sha512-4HaCc8Hk04pZ+v47K0j0sG3B5m7Ez/1uKHSHre9nNjevw9viAJcjhUZ0mqhhar+Hvavp3rHnbZwqxODluyWsTA==", + "version": "0.55.19", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.55.19.tgz", + "integrity": "sha512-US1gb6YA4lYW8wYW4afUj/PM7yhmCArGVdISJlbBgLyC3ZJWpEveuKh5ukooihp0E5/uHEOspdWmg2M7su0sQA==", "requires": { "@types/react": "16.3.17" } @@ -1350,12 +1350,6 @@ "resolved": "https://registry.npmjs.org/address/-/address-1.0.3.tgz", "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==" }, - "after": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", - "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", - "dev": true - }, "agent-base": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz", @@ -1859,12 +1853,6 @@ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" }, - "arraybuffer.slice": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", - "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", - "dev": true - }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -3385,12 +3373,6 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -3436,12 +3418,6 @@ "integrity": "sha512-V6YHUbjLxN1ymqNLb1DPHoU1CpfdL7d2YTIp5W3U4hhoG4hhxNmsFDs66M9EXxBiSEke5Bt5dwdfMwwZF70iLA==", "dev": true }, - "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true - }, "base64-js": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.3.tgz", @@ -3464,15 +3440,6 @@ "tweetnacl": "0.14.5" } }, - "better-assert": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", - "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", - "dev": true, - "requires": { - "callsite": "1.0.0" - } - }, "big-integer": { "version": "1.6.28", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.28.tgz", @@ -3497,12 +3464,6 @@ "safe-buffer": "5.1.1" } }, - "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", - "dev": true - }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", @@ -3853,12 +3814,6 @@ "callsites": "0.2.0" } }, - "callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", - "dev": true - }, "callsites": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", @@ -4683,23 +4638,11 @@ "integrity": "sha512-MAAAIOdi2s4Gl6rZ76PNcUa9IOYB+5ICdT41o5uMRf09aEu/F9RK+qhe8RjXNPwcTjGV7KU7h2P/fljThFVqyQ==", "dev": true }, - "component-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", - "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", - "dev": true - }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, - "component-inherit": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", - "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", - "dev": true - }, "compressible": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.13.tgz", @@ -5853,63 +5796,6 @@ "once": "1.4.0" } }, - "engine.io-client": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.5.tgz", - "integrity": "sha512-AYTgHyeVUPitsseqjoedjhYJapNVoSPShbZ+tEUX9/73jgZ/Z3sUlJf9oYgdEBBdVhupUpUqSxH0kBCXlQnmZg==", - "dev": true, - "requires": { - "component-emitter": "1.2.1", - "component-inherit": "0.0.3", - "debug": "2.3.3", - "engine.io-parser": "1.3.2", - "has-cors": "1.1.0", - "indexof": "0.0.1", - "parsejson": "0.0.3", - "parseqs": "0.0.5", - "parseuri": "0.0.5", - "ws": "1.1.5", - "xmlhttprequest-ssl": "1.5.3", - "yeast": "0.1.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "engine.io-parser": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", - "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", - "dev": true, - "requires": { - "after": "0.8.2", - "arraybuffer.slice": "0.0.6", - "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary": "0.1.7", - "wtf-8": "1.0.0" - } - }, "enhanced-resolve": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", @@ -8274,21 +8160,6 @@ "ansi-regex": "2.1.1" } }, - "has-binary": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", - "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", - "dev": true, - "requires": { - "isarray": "0.0.1" - } - }, - "has-cors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", - "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", - "dev": true - }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", @@ -12436,12 +12307,6 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-component": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", - "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", - "dev": true - }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -12856,33 +12721,6 @@ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", "dev": true }, - "parsejson": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", - "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseqs": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", - "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, - "parseuri": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", - "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", - "dev": true, - "requires": { - "better-assert": "1.0.2" - } - }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -15117,7 +14955,7 @@ "version": "git+https://github.com/RocketChat/react-native-markdown-renderer.git#cecc6d0a2c940ac7a1e1e98c624d8b9b4d37ab68", "requires": { "@types/markdown-it": "0.0.4", - "@types/react-native": "0.55.18", + "@types/react-native": "0.55.19", "markdown-it": "8.4.1", "prop-types": "15.6.1", "react-native-fit-image": "1.5.4" @@ -15500,19 +15338,23 @@ } }, "reactotron-react-native": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/reactotron-react-native/-/reactotron-react-native-1.14.0.tgz", - "integrity": "sha512-jyx5EOmqHGrdOzL0pksso4qDFZQRiD6zycM3KgIN3Nq49HmOofDjdOEoN/b4V+W1ftvEp/gjc/S5HXWmCh+dUQ==", + "version": "2.0.0-beta.6", + "resolved": "https://registry.npmjs.org/reactotron-react-native/-/reactotron-react-native-2.0.0-beta.6.tgz", + "integrity": "sha512-9J5/F1xmhTeNQKpHJh5eNRO5Y8DKWGidHb8SsThInbn9+UGaHEw/HyOYctu2h7WlIPCau6SmI1G459lFYtKluA==", "dev": true, "requires": { - "json-stringify-safe": "5.0.1", "mitt": "1.1.3", "prop-types": "15.6.1", - "ramda": "0.24.1", - "ramdasauce": "2.1.0", - "reactotron-core-client": "1.13.0", - "rn-host-detect": "1.1.3", - "socket.io-client": "1.7.4" + "reactotron-core-client": "2.0.0-beta.6", + "rn-host-detect": "1.1.3" + }, + "dependencies": { + "reactotron-core-client": { + "version": "2.0.0-beta.6", + "resolved": "https://registry.npmjs.org/reactotron-core-client/-/reactotron-core-client-2.0.0-beta.6.tgz", + "integrity": "sha512-Wh9gNOieSlbwSPhe0DNoOur0o0qQKeYorGcmIykzbMV097lQ3+lt9lkort2yOzhosF5Sk9MfyvhhQ2qqEXZeiA==", + "dev": true + } } }, "reactotron-redux": { @@ -17215,83 +17057,6 @@ } } }, - "socket.io-client": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.4.tgz", - "integrity": "sha1-7J+CA1btme9tNX8HVtZIcXvdQoE=", - "dev": true, - "requires": { - "backo2": "1.0.2", - "component-bind": "1.0.0", - "component-emitter": "1.2.1", - "debug": "2.3.3", - "engine.io-client": "1.8.5", - "has-binary": "0.1.7", - "indexof": "0.0.1", - "object-component": "0.0.3", - "parseuri": "0.0.5", - "socket.io-parser": "2.3.1", - "to-array": "0.1.4" - }, - "dependencies": { - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "debug": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", - "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", - "dev": true, - "requires": { - "ms": "0.7.2" - } - }, - "ms": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", - "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", - "dev": true - } - } - }, - "socket.io-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", - "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", - "dev": true, - "requires": { - "component-emitter": "1.1.2", - "debug": "2.2.0", - "isarray": "0.0.1", - "json3": "3.3.2" - }, - "dependencies": { - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } - } - }, "sockjs-client": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.4.tgz", @@ -18301,12 +18066,6 @@ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=" }, - "to-array": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", - "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", - "dev": true - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -19379,12 +19138,6 @@ } } }, - "wtf-8": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", - "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", - "dev": true - }, "xcode": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/xcode/-/xcode-0.9.3.tgz", @@ -19464,12 +19217,6 @@ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" }, - "xmlhttprequest-ssl": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", - "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", - "dev": true - }, "xpipe": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/xpipe/-/xpipe-1.0.5.tgz", @@ -19542,12 +19289,6 @@ } } }, - "yeast": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", - "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", - "dev": true - }, "zip": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zip/-/zip-1.2.0.tgz", diff --git a/package.json b/package.json index c2faafedd..b2afffcae 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "snyk-protect": "snyk protect", "prepare": "exit 0", "fabric-ios": "./scripts/fabric-ios.sh", - "fabric-android": "./scripts/fabric-android.sh" + "fabric-android": "./scripts/fabric-android.sh", + "postinstall": "cp ./temp/react.gradle ./node_modules/react-native" }, "rnpm": { "assets": [ @@ -104,7 +105,7 @@ "react-dom": "^16.4.0", "react-native-bundle-visualizer": "^1.2.0", "react-test-renderer": "^16.4.0", - "reactotron-react-native": "^1.14.0", + "reactotron-react-native": "^2.0.0-beta.6", "reactotron-redux": "^1.13.0", "reactotron-redux-saga": "^1.13.0" }, diff --git a/temp/react.gradle b/temp/react.gradle new file mode 100644 index 000000000..71b8b437c --- /dev/null +++ b/temp/react.gradle @@ -0,0 +1,135 @@ +// Workaround to "error: Duplicate file. Original is here. The version qualifier may be implied. With gradle assembleRelease" +// https://github.com/facebook/react-native/issues/19211#issuecomment-389125030 +import org.apache.tools.ant.taskdefs.condition.Os + +def config = project.hasProperty("react") ? project.react : []; + +def cliPath = config.cliPath ?: "node_modules/react-native/local-cli/cli.js" +def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" +def entryFile = config.entryFile ?: "index.android.js" +def bundleCommand = config.bundleCommand ?: "bundle" + +// because elvis operator +def elvisFile(thing) { + return thing ? file(thing) : null; +} + +def reactRoot = elvisFile(config.root) ?: file("../../") +def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] +def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ; + +void runBefore(String dependentTaskName, Task task) { + Task dependentTask = tasks.findByPath(dependentTaskName); + if (dependentTask != null) { + dependentTask.dependsOn task + } +} + +gradle.projectsEvaluated { + // Grab all build types and product flavors + def buildTypes = android.buildTypes.collect { type -> type.name } + def productFlavors = android.productFlavors.collect { flavor -> flavor.name } + + // When no product flavors defined, use empty + if (!productFlavors) productFlavors.add('') + + productFlavors.each { productFlavorName -> + buildTypes.each { buildTypeName -> + // Create variant and target names + def flavorNameCapitalized = "${productFlavorName.capitalize()}" + def buildNameCapitalized = "${buildTypeName.capitalize()}" + def targetName = "${flavorNameCapitalized}${buildNameCapitalized}" + def targetPath = productFlavorName ? + "${productFlavorName}/${buildTypeName}" : + "${buildTypeName}" + + // React js bundle directories + def jsBundleDirConfigName = "jsBundleDir${targetName}" + def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?: + file("$buildDir/intermediates/assets/${targetPath}") + + def resourcesDirConfigName = "resourcesDir${targetName}" + def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?: + file("$buildDir/intermediates/res/merged/${targetPath}") + def jsBundleFile = file("$jsBundleDir/$bundleAssetName") + + // Bundle task name for variant + def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets" + + // Additional node and packager commandline arguments + def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"] + def extraPackagerArgs = config.extraPackagerArgs ?: [] + + def currentBundleTask = tasks.create( + name: bundleJsAndAssetsTaskName, + type: Exec) { + group = "react" + description = "bundle JS and assets for ${targetName}." + + // Create dirs if they are not there (e.g. the "clean" task just ran) + doFirst { + jsBundleDir.mkdirs() + resourcesDir.mkdirs() + } + + // Set up inputs and outputs so gradle can cache the result + inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) + outputs.dir jsBundleDir + outputs.dir resourcesDir + + // Set up the call to the react-native cli + workingDir reactRoot + + // Set up dev mode + def devEnabled = !(config."devDisabledIn${targetName}" + || targetName.toLowerCase().contains("release")) + + def extraArgs = extraPackagerArgs; + + if (bundleConfig) { + extraArgs = extraArgs.clone() + extraArgs.add("--config"); + extraArgs.add(bundleConfig); + } + + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", + "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraArgs) + } else { + commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}", + "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir, *extraArgs) + } + + enabled config."bundleIn${targetName}" || + config."bundleIn${buildTypeName.capitalize()}" ?: + targetName.toLowerCase().contains("release") + + doLast { + def moveFunc = { resSuffix -> + File originalDir = file("${resourcesDir}/drawable-${resSuffix}") + if (originalDir.exists()) { + File destDir = file("${resourcesDir}/drawable-${resSuffix}-v4") + ant.move(file: originalDir, tofile: destDir) + } + } + moveFunc.curry("ldpi").call() + moveFunc.curry("mdpi").call() + moveFunc.curry("hdpi").call() + moveFunc.curry("xhdpi").call() + moveFunc.curry("xxhdpi").call() + moveFunc.curry("xxxhdpi").call() + } + } + + // Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process + currentBundleTask.dependsOn("merge${targetName}Resources") + currentBundleTask.dependsOn("merge${targetName}Assets") + + runBefore("process${flavorNameCapitalized}Armeabi-v7a${buildNameCapitalized}Resources", currentBundleTask) + runBefore("process${flavorNameCapitalized}X86${buildNameCapitalized}Resources", currentBundleTask) + runBefore("processUniversal${targetName}Resources", currentBundleTask) + runBefore("process${targetName}Resources", currentBundleTask) + runBefore("dataBindingProcessLayouts${targetName}", currentBundleTask) + } + } +}