[MM-13955] Add telemetry for Android (#2719)

* add markers for Android profiling

* update per comments

* Remove beta build flag

* update per comment, latest change on telemetry server and others

* rebase and fix merge conflicts, and update per comments

* update commit hash of mattermost-redux
This commit is contained in:
Saturnino Abril 2019-05-08 00:23:59 +08:00 committed by GitHub
parent 5ce9208d32
commit e086d66ec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 554 additions and 29 deletions

View file

@ -44,6 +44,15 @@ import com.wix.reactnativenotifications.core.AppLaunchHelper;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.JsIOHelper;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactMarker;
import com.facebook.react.bridge.ReactMarkerConstants;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import android.support.annotation.Nullable;
import android.util.Log;
public class MainApplication extends NavigationApplication implements INotificationsApplication, INotificationsDrawerApplication {
@ -51,6 +60,14 @@ public class MainApplication extends NavigationApplication implements INotificat
public Boolean sharedExtensionIsOpened = false;
public Boolean replyFromPushNotification = false;
public long APP_START_TIME;
public long RELOAD;
public long CONTENT_APPEARED;
public long PROCESS_PACKAGES_START;
public long PROCESS_PACKAGES_END;
@Override
public boolean isDebug() {
return BuildConfig.DEBUG;
@ -109,6 +126,9 @@ public class MainApplication extends NavigationApplication implements INotificat
setActivityCallbacks(notificationsLifecycleFacade);
SoLoader.init(this, /* native exopackage */ false);
// Uncomment to listen to react markers for build that has telemetry enabled
// addReactMarkerListener();
}
@Override
@ -133,4 +153,36 @@ public class MainApplication extends NavigationApplication implements INotificat
public IPushNotificationsDrawer getPushNotificationsDrawer(Context context, AppLaunchHelper defaultAppLaunchHelper) {
return new CustomPushNotificationDrawer(context, defaultAppLaunchHelper);
}
private void addReactMarkerListener() {
ReactMarker.addListener(new ReactMarker.MarkerListener() {
@Override
public void logMarker(ReactMarkerConstants name, @Nullable String tag, int instanceKey) {
if (name.toString() == ReactMarkerConstants.RELOAD.toString()) {
APP_START_TIME = System.currentTimeMillis();
RELOAD = System.currentTimeMillis();
} else if (name.toString() == ReactMarkerConstants.PROCESS_PACKAGES_START.toString()) {
PROCESS_PACKAGES_START = System.currentTimeMillis();
} else if (name.toString() == ReactMarkerConstants.PROCESS_PACKAGES_END.toString()) {
PROCESS_PACKAGES_END = System.currentTimeMillis();
} else if (name.toString() == ReactMarkerConstants.CONTENT_APPEARED.toString()) {
CONTENT_APPEARED = System.currentTimeMillis();
ReactContext ctx = getReactGateway().getReactContext();
if (ctx != null) {
WritableMap map = Arguments.createMap();
map.putDouble("appReload", RELOAD);
map.putDouble("appContentAppeared", CONTENT_APPEARED);
map.putDouble("processPackagesStart", PROCESS_PACKAGES_START);
map.putDouble("processPackagesEnd", PROCESS_PACKAGES_END);
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).
emit("nativeMetrics", map);
}
}
}
});
}
}

View file

@ -34,6 +34,8 @@ import {
} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId, getTeamByName} from 'mattermost-redux/selectors/entities/teams';
import telemetry from 'app/telemetry';
import {
getChannelByName,
getDirectChannelName,
@ -536,6 +538,12 @@ export function leaveChannel(channel, reset = false) {
}
export function setChannelLoading(loading = true) {
if (loading) {
telemetry.start(['channel:loading']);
} else {
telemetry.end(['channel:loading']);
}
return {
type: ViewTypes.SET_CHANNEL_LOADER,
loading,
@ -593,6 +601,9 @@ export function increasePostVisibility(channelId, postId) {
return true;
}
telemetry.reset();
telemetry.start(['posts:loading']);
dispatch({
type: ViewTypes.LOADING_POSTS,
data: true,
@ -630,6 +641,9 @@ export function increasePostVisibility(channelId, postId) {
}
dispatch(batchActions(actions));
telemetry.end(['posts:loading']);
telemetry.save();
return hasMorePost;
};
}

View file

@ -42,6 +42,7 @@ export default class Post extends PureComponent {
renderReplies: PropTypes.bool,
isFirstReply: PropTypes.bool,
isLastReply: PropTypes.bool,
isLastPost: PropTypes.bool,
consecutivePost: PropTypes.bool,
hasComments: PropTypes.bool,
isSearchResult: PropTypes.bool,
@ -237,6 +238,7 @@ export default class Post extends PureComponent {
channelIsReadOnly,
commentedOnPost,
highlight,
isLastPost,
isLastReply,
isSearchResult,
onHashtagPress,
@ -336,6 +338,7 @@ export default class Post extends PureComponent {
ref={'postBody'}
highlight={highlight}
channelIsReadOnly={channelIsReadOnly}
isLastPost={isLastPost}
isSearchResult={isSearchResult}
navigator={this.props.navigator}
onFailedPostPress={this.handleFailedPostPress}

View file

@ -24,6 +24,8 @@ import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown'
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import telemetry from 'app/telemetry';
let FileAttachmentList;
let PostAddChannelMember;
let PostBodyAdditionalContent;
@ -43,6 +45,7 @@ export default class PostBody extends PureComponent {
highlight: PropTypes.bool,
isFailed: PropTypes.bool,
isFlagged: PropTypes.bool,
isLastPost: PropTypes.bool,
isPending: PropTypes.bool,
isPostAddChannelMember: PropTypes.bool,
isPostEphemeral: PropTypes.bool,
@ -95,6 +98,17 @@ export default class PostBody extends PureComponent {
isLongPost: false,
};
logTelemetry = () => {
telemetry.end([
'channel:switch_initial',
'channel:switch_loaded',
'posts:list_update',
'team:switch',
'start:overall',
]);
telemetry.save();
}
measurePost = (event) => {
const {height} = event.nativeEvent.layout;
const {showLongPost} = this.props;
@ -104,6 +118,10 @@ export default class PostBody extends PureComponent {
isLongPost: true,
});
}
if (this.props.isLastPost) {
this.logTelemetry();
}
};
openLongPost = preventDoubleTap(() => {

View file

@ -15,6 +15,7 @@ import mattermostManaged from 'app/mattermost_managed';
import {makeExtraData} from 'app/utils/list_view';
import {changeOpacity} from 'app/utils/theme';
import {matchDeepLink} from 'app/utils/url';
import telemetry from 'app/telemetry';
import DateHeader from './date_header';
import NewMessagesDivider from './new_messages_divider';
@ -48,6 +49,7 @@ export default class PostList extends PureComponent {
highlightPostId: PropTypes.string,
initialIndex: PropTypes.number,
isSearchResult: PropTypes.bool,
lastPostIndex: PropTypes.number.isRequired,
lastViewedAt: PropTypes.number, // Used by container // eslint-disable-line no-unused-prop-types
navigator: PropTypes.object,
onLoadMoreUp: PropTypes.func,
@ -103,6 +105,8 @@ export default class PostList extends PureComponent {
this.handleDeepLink(this.props.deepLinkURL);
this.props.actions.setDeepLinkURL('');
}
telemetry.start(['posts:list_update']);
}
componentWillUnmount() {
@ -256,6 +260,7 @@ export default class PostList extends PureComponent {
<Post
postId={postId}
highlight={this.props.highlightPostId === postId}
isLastPost={this.props.lastPostIndex === index}
{...postProps}
/>
);

View file

@ -23,6 +23,7 @@ describe('PostList', () => {
navigator: {
showModal: jest.fn(),
},
lastPostIndex: -1,
postIds: ['post-id-1', 'post-id-2'],
serverURL,
siteURL: 'https://site-url.fake',

View file

@ -16,6 +16,8 @@ import {
I18nManager,
} from 'react-native';
import telemetry from 'app/telemetry';
const MIN_SWIPE_DISTANCE = 3;
const DEVICE_WIDTH = parseFloat(Dimensions.get('window').width);
const THRESHOLD = DEVICE_WIDTH / 2;
@ -234,6 +236,9 @@ export default class DrawerLayout extends Component {
...options,
}).start(() => {
if (this.props.onDrawerOpen) {
telemetry.end(['channel:open_drawer']);
telemetry.save();
this.props.onDrawerOpen();
}
this._emitStateChanged(IDLE);
@ -250,6 +255,7 @@ export default class DrawerLayout extends Component {
...options,
}).start(() => {
if (this.props.onDrawerClose) {
telemetry.end(['channel:close_drawer']);
this.props.onDrawerClose();
}
this._emitStateChanged(IDLE);

View file

@ -12,9 +12,28 @@ import {getCurrentTeamId, getMyTeamsCount} from 'mattermost-redux/selectors/enti
import {setChannelDisplayName, setChannelLoading} from 'app/actions/views/channel';
import {makeDirectChannel} from 'app/actions/views/more_dms';
import {isLandscape, isTablet, getDimensions} from 'app/selectors/device';
import telemetry from 'app/telemetry';
import MainSidebar from './main_sidebar.js';
export function logChannelSwitch(channelId, currentChannelId) {
return (dispatch, getState) => {
if (channelId === currentChannelId) {
return;
}
const metrics = [];
if (getState().entities.posts.postsInChannel[channelId]) {
metrics.push('channel:switch_loaded');
} else {
metrics.push('channel:switch_initial');
}
telemetry.reset();
telemetry.start(metrics);
};
}
function mapStateToProps(state) {
const {currentUserId} = state.entities.users;
@ -34,6 +53,7 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
getTeams,
joinChannel,
logChannelSwitch,
makeDirectChannel,
setChannelDisplayName,
setChannelLoading,

View file

@ -23,6 +23,8 @@ import ChannelsList from './channels_list';
import DrawerSwiper from './drawer_swipper';
import TeamsList from './teams_list';
import telemetry from 'app/telemetry';
const DRAWER_INITIAL_OFFSET = 40;
const DRAWER_LANDSCAPE_OFFSET = 150;
@ -30,6 +32,7 @@ export default class ChannelSidebar extends Component {
static propTypes = {
actions: PropTypes.shape({
getTeams: PropTypes.func.isRequired,
logChannelSwitch: PropTypes.func.isRequired,
makeDirectChannel: PropTypes.func.isRequired,
setChannelDisplayName: PropTypes.func.isRequired,
setChannelLoading: PropTypes.func.isRequired,
@ -165,11 +168,14 @@ export default class ChannelSidebar extends Component {
};
selectChannel = (channel, currentChannelId, closeDrawer = true) => {
const {setChannelLoading} = this.props.actions;
const {logChannelSwitch, setChannelLoading} = this.props.actions;
logChannelSwitch(channel.id, currentChannelId);
tracker.channelSwitch = Date.now();
if (closeDrawer) {
telemetry.start(['channel:close_drawer']);
this.closeChannelDrawer();
setChannelLoading(channel.id !== currentChannelId);
}

View file

@ -19,6 +19,7 @@ import {ListTypes, ViewTypes} from 'app/constants';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import tracker from 'app/utils/time_tracker';
import telemetry from 'app/telemetry';
import TeamsListItem from './teams_list_item';
@ -55,9 +56,15 @@ export default class TeamsList extends PureComponent {
}
selectTeam = (teamId) => {
const {actions, closeChannelDrawer, currentTeamId} = this.props;
if (teamId !== currentTeamId) {
telemetry.reset();
telemetry.start(['team:switch']);
}
StatusBar.setHidden(false, 'slide');
requestAnimationFrame(() => {
const {actions, closeChannelDrawer, currentTeamId} = this.props;
if (teamId !== currentTeamId) {
tracker.teamSwitch = Date.now();
actions.handleTeamChange(teamId);

View file

@ -45,6 +45,7 @@ import {deleteFileCache} from 'app/utils/file';
import avoidNativeBridge from 'app/utils/avoid_native_bridge';
import {t} from 'app/utils/i18n';
import LocalConfig from 'assets/config';
import telemetry from 'app/telemetry';
import App from './app';
import './fetch_preconfig';
@ -435,6 +436,11 @@ const handleAppInActive = () => {
AppState.addEventListener('change', handleAppStateChange);
const launchEntry = () => {
telemetry.start([
'start:select_server_screen',
'start:channel_screen',
]);
Navigation.startSingleScreenApp({
screen: {
screen: 'Entry',
@ -452,6 +458,8 @@ const launchEntry = () => {
},
animationType: 'fade',
});
telemetry.startSinceLaunch(['start:splash_screen']);
};
configurePushNotifications();

View file

@ -32,6 +32,8 @@ import PushNotifications from 'app/push_notifications';
import tracker from 'app/utils/time_tracker';
import LocalConfig from 'assets/config';
import telemetry from 'app/telemetry';
import ChannelNavBar from './channel_nav_bar';
import ChannelPostList from './channel_post_list';
@ -104,6 +106,8 @@ export default class Channel extends PureComponent {
}
EventEmitter.emit('renderDrawer');
telemetry.end(['start:channel_screen']);
}
componentWillReceiveProps(nextProps) {

View file

@ -17,6 +17,8 @@ import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {preventDoubleTap} from 'app/utils/tap';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
import telemetry from 'app/telemetry';
import {getUnreadsInCurrentTeam} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeamId, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@ -69,6 +71,7 @@ class ChannelDrawerButton extends PureComponent {
};
handlePress = preventDoubleTap(() => {
telemetry.start(['channel:open_drawer']);
this.props.openDrawer();
});

View file

@ -9,6 +9,8 @@ import {
View,
} from 'react-native';
import {getLastPostIndex} from 'mattermost-redux/utils/post_list';
import AnnouncementBanner from 'app/components/announcement_banner';
import PostList from 'app/components/post_list';
import PostListRetry from 'app/components/post_list_retry';
@ -190,6 +192,7 @@ export default class ChannelPostList extends PureComponent {
component = (
<PostList
postIds={visiblePostIds}
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(visiblePostIds) : -1}
extraData={loadMorePostsVisible}
onLoadMoreUp={this.loadMorePostsTop}
onPostPress={this.goToThread}

View file

@ -3,7 +3,9 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import {Platform, View} from 'react-native';
import {getLastPostIndex} from 'mattermost-redux/utils/post_list';
import PostList from 'app/components/post_list';
import {makeStyleSheetFromTheme} from 'app/utils/theme';
@ -80,6 +82,7 @@ export default class ChannelPeek extends PureComponent {
<View style={style.container}>
<PostList
postIds={visiblePostIds}
lastPostIndex={Platform.OS === 'android' ? getLastPostIndex(visiblePostIds) : -1}
renderReplies={true}
indicateNewMessages={true}
currentUserId={currentUserId}

View file

@ -28,6 +28,8 @@ import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
import {setMfaPreflightDone, getMfaPreflightDone} from 'app/utils/security';
import telemetry from 'app/telemetry';
import {RequestStatus} from 'mattermost-redux/constants';
const mfaExpectedErrors = ['mfa.validate_token.authenticate.app_error', 'ent.mfa.validate_token.authenticate.app_error'];
@ -80,6 +82,8 @@ export default class Login extends PureComponent {
}
goToChannel = () => {
telemetry.remove(['start:overall']);
const {navigator} = this.props;
tracker.initialLoad = Date.now();

View file

@ -34,6 +34,8 @@ import {preventDoubleTap} from 'app/utils/tap';
import tracker from 'app/utils/time_tracker';
import {t} from 'app/utils/i18n';
import telemetry from 'app/telemetry';
import LocalConfig from 'assets/config';
export default class SelectServer extends PureComponent {
@ -92,6 +94,9 @@ export default class SelectServer extends PureComponent {
this.certificateListener = DeviceEventEmitter.addListener('RNFetchBlobCertificate', this.selectCertificate);
this.props.navigator.setOnNavigatorEvent(this.handleNavigatorEvent);
telemetry.end(['start:select_server_screen']);
telemetry.save();
}
componentWillUpdate(nextProps, nextState) {

6
app/telemetry/index.js Normal file
View file

@ -0,0 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import telemetry from './telemetry';
export default telemetry;

View file

@ -0,0 +1,158 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import LocalConfig from 'assets/config'; // eslint-disable-line
import {store} from 'app/mattermost';
import {
saveToTelemetryServer,
getDeviceInfo,
setTraceRecord,
} from './telemetry_utils';
class Telemetry {
constructor() {
this.appStartTime = 0;
this.reactInitializedStartTime = 0;
this.reactInitializedEndTime = 0;
this.metrics = [];
this.currentMetrics = {};
this.pendingSinceLaunchMetrics = [];
}
setAppStartTime(startTime) {
this.appStartTime = startTime;
}
reset() {
this.metrics = [];
this.currentMetrics = {};
this.pendingSinceLaunchMetrics = [];
}
canSendTelemetry() {
const {config} = store.getState().entities.general;
return Boolean(!__DEV__ && config.EnableDiagnostics === 'true' && LocalConfig.TelemetryEnabled);
}
start(names = [], time = 0) {
if (this.canSendTelemetry()) {
const d = new Date();
const startTime = d.getTime();
names.forEach((name) => {
this.currentMetrics[name] = {
name,
startTime: time || startTime,
};
});
}
}
end(names = []) {
if (this.canSendTelemetry()) {
const d = new Date();
const endTime = d.getTime();
names.forEach((name) => {
const finalMetric = this.currentMetrics[name];
if (finalMetric && finalMetric.startTime > 0) {
this.metrics.push({
...finalMetric,
endTime,
});
Reflect.deleteProperty(this.currentMetrics, name);
}
});
}
}
include(metrics = []) {
if (this.canSendTelemetry()) {
metrics.forEach((metric) => {
this.metrics.push({
name: metric.name,
startTime: metric.startTime,
endTime: metric.endTime,
});
});
}
}
startSinceLaunch(names = []) {
if (this.canSendTelemetry()) {
const d = new Date();
const endTime = d.getTime();
names.forEach((name) => {
if (!this.appStartTime) {
this.pendingSinceLaunchMetrics.push({
name,
endTime,
});
return;
}
this.metrics.push({
name,
startTime: this.appStartTime,
endTime,
});
});
}
}
remove(names = []) {
names.forEach((name) => {
const currentMetric = this.currentMetrics[name];
if (currentMetric) {
Reflect.deleteProperty(this.currentMetrics, name);
}
});
}
save() {
if (!this.canSendTelemetry()) {
return;
}
this.pendingSinceLaunchMetrics.forEach((pendingMetric) => {
this.metrics.push({
...pendingMetric,
startTime: this.appStartTime,
});
});
if (this.metrics.length === 0) {
return;
}
const metrics = this.metrics.filter((m) => m.endTime && m.startTime).map((metric) => {
const {name, startTime, endTime} = metric;
let dur;
if (endTime && startTime) {
dur = (endTime - startTime) * 1000;
}
return setTraceRecord({
name,
time: startTime * 1000,
dur,
});
});
const {config} = store.getState().entities.general;
const deviceInfo = getDeviceInfo();
deviceInfo.serverVersion = config.Version;
saveToTelemetryServer({trace_events: metrics, device_info: deviceInfo});
this.reset();
}
}
const telemetry = new Telemetry();
export default telemetry;

View file

@ -0,0 +1,34 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class Telemetry {
constructor() {
this.appStartTime = 0;
this.reactInitializedStartTime = 0;
this.reactInitializedEndTime = 0;
this.metrics = [];
this.currentMetrics = {};
this.pendingSinceLaunchMetrics = [];
}
setAppStartTime = () => true;
reset = () => true;
canSendTelemetry = () => false;
start = () => true;
end = () => true;
include = () => true;
startSinceLaunch = () => true;
remove = () => true;
save = () => true;
}
const telemetry = new Telemetry();
export default telemetry;

View file

@ -0,0 +1,100 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Dimensions} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import LocalConfig from 'assets/config';
export function saveToTelemetryServer(data) {
const {
TelemetryEnabled,
TelemetryUrl,
TelemetryApiKey,
} = LocalConfig;
if (TelemetryEnabled && TelemetryUrl) {
fetch(TelemetryUrl, {
method: 'post',
body: JSON.stringify({data}),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'x-api-key': TelemetryApiKey,
},
});
}
}
const presetTID = {
'start:overall': 0,
'start:process_packages': 1,
'start:content_appeared': 2,
'start:select_server_screen': 3,
'start:channel_screen': 4,
'team:switch': 5,
'channel:loading': 6,
'channel:switch_loaded': 7,
'channel:switch_initial': 8,
'channel:close_drawer': 9,
'channel:open_drawer': 10,
'posts:loading': 11,
'posts:list_update': 12,
};
export function setTraceRecord({
name,
time: ts,
dur = 0,
instanceKey = 0,
pid = 0,
}) {
const tid = presetTID[name];
return {
cat: 'react-native',
ph: 'X',
name,
ts,
dur,
pid,
tid,
args: {
instanceKey,
tag: name,
},
};
}
export function getDeviceInfo() {
const {height, width} = Dimensions.get('window');
return {
api_level: DeviceInfo.getAPILevel(),
build_number: DeviceInfo.getBuildNumber(),
bundle_id: DeviceInfo.getBundleId(),
brand: DeviceInfo.getBrand(),
country: DeviceInfo.getDeviceCountry(),
device_id: DeviceInfo.getDeviceId(),
device_locale: DeviceInfo.getDeviceLocale().split('-')[0],
device_type: DeviceInfo.getDeviceType(),
device_unique_id: DeviceInfo.getUniqueID(),
height: height ? Math.floor(height) : 0,
is_emulator: DeviceInfo.isEmulator(),
is_tablet: DeviceInfo.isTablet(),
manufacturer: DeviceInfo.getManufacturer(),
max_memory: DeviceInfo.getMaxMemory(),
model: DeviceInfo.getModel(),
system_name: DeviceInfo.getSystemName(),
system_version: DeviceInfo.getSystemVersion(),
timezone: DeviceInfo.getTimezone(),
app_version: DeviceInfo.getVersion(),
width: width ? Math.floor(width) : 0,
};
}
export default {
getDeviceInfo,
setTraceRecord,
};

View file

@ -42,5 +42,9 @@
"ShowSentryDebugOptions": false,
"CustomRequestHeaders": {}
"CustomRequestHeaders": {},
"TelemetryEnabled": false,
"TelemetryUrl": "",
"TelemetryApiKey": ""
}

View file

@ -157,13 +157,6 @@ lane :configure do
json['SentryDsnAndroid'] = ENV['SENTRY_DSN_ANDROID']
end
# Configure Telemetry if enabled
if ENV['BETA_BUILD'] == 'true' && ENV['TELEMETRY_ENABLED'] == 'true' && ENV['TELEMETRY_URL'] != '' && ENV['TELEMETRY_API_KEY'] != ''
json['TelemetryEnabled'] = true
json['TelemetryUrl'] = ENV['TELEMETRY_URL']
json['TelemetryApiKey'] = ENV['TELEMETRY_API_KEY']
end
# Save the config.json file
save_config_json(json)
@ -361,6 +354,7 @@ platform :android do
lane :build do
unless configured
configure
configure_telemetry_android
end
update_identifiers
replace_assets
@ -383,6 +377,27 @@ platform :android do
)
end
desc 'Update config and module/listener for Android telemetry'
lane :configure_telemetry_android do
if ENV['TELEMETRY_ENABLED'] == 'true' && ENV['TELEMETRY_URL'] != '' && ENV['TELEMETRY_API_KEY'] != ''
# Update telemetry config
json = load_config_json
json['TelemetryEnabled'] = true
json['TelemetryUrl'] = ENV['TELEMETRY_URL']
json['TelemetryApiKey'] = ENV['TELEMETRY_API_KEY']
save_config_json(json)
beta_dir = './android/app/src/main/java/com/mattermost/rnbeta/'
# Listen to telemetry react markers
find_replace_string(
path_to_file: "#{beta_dir}MainApplication.java",
old_string: '// addReactMarkerListener();',
new_string: 'addReactMarkerListener();'
)
end
end
lane :update_identifiers do
# Set the name for the app
app_name = ENV['APP_NAME'] || 'Mattermost Beta'

View file

@ -2,14 +2,31 @@
// See LICENSE.txt for license information.
import 'react-native/Libraries/Core/InitializeCore';
import {AppRegistry, Platform} from 'react-native';
import {AppRegistry, DeviceEventEmitter, Platform} from 'react-native';
import 'react-native-gesture-handler';
import LocalConfig from 'assets/config';
import telemetry from 'app/telemetry';
import 'app/mattermost';
import ShareExtension from 'share_extension/android';
if (Platform.OS === 'android') {
AppRegistry.registerComponent('MattermostShare', () => ShareExtension);
if (LocalConfig.TelemetryEnabled) {
const metricsSubscription = DeviceEventEmitter.addListener('nativeMetrics', (metrics) => {
telemetry.setAppStartTime(metrics.appReload);
telemetry.include([
{name: 'start:process_packages', startTime: metrics.processPackagesStart, endTime: metrics.processPackagesEnd},
{name: 'start:content_appeared', startTime: metrics.appReload, endTime: metrics.appContentAppeared},
]);
telemetry.start(['start:overall'], metrics.appReload);
DeviceEventEmitter.removeSubscription(metricsSubscription);
});
}
}
// Uncomment the snippet below if you want to update the modules

55
package-lock.json generated
View file

@ -2700,6 +2700,7 @@
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -2744,7 +2745,8 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true
"dev": true,
"optional": true
},
"is-glob": {
"version": "4.0.0",
@ -4821,7 +4823,8 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -4839,11 +4842,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"bundled": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -4856,15 +4861,18 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -4967,7 +4975,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -4977,6 +4986,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -4989,17 +4999,20 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"bundled": true,
"optional": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -5016,6 +5029,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -5088,7 +5102,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -5098,6 +5113,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -5173,7 +5189,8 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true
"bundled": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -5203,6 +5220,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -5220,6 +5238,7 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -5258,11 +5277,13 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"bundled": true,
"optional": true
},
"yallist": {
"version": "3.0.3",
"bundled": true
"bundled": true,
"optional": true
}
}
},
@ -9839,8 +9860,8 @@
"integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
"from": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
"version": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
"from": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "3.0.0",
@ -13122,7 +13143,8 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
"dev": true,
"optional": true
},
"braces": {
"version": "2.3.2",
@ -13408,7 +13430,8 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true
"dev": true,
"optional": true
},
"micromatch": {
"version": "3.1.10",

View file

@ -18,7 +18,7 @@
"intl": "1.2.5",
"jail-monkey": "2.0.0",
"jsc-android": "236355.1.1",
"mattermost-redux": "github:mattermost/mattermost-redux#b854353ee96b875b6b87857932b83b1143131860",
"mattermost-redux": "github:mattermost/mattermost-redux#0df3ec0182e9d9ec67397157611d887f5fbc1bf1",
"mime-db": "1.40.0",
"moment-timezone": "0.5.23",
"prop-types": "15.7.2",

View file

@ -207,6 +207,9 @@ module.exports = [
'app/store/index.js',
'app/store/middleware.js',
'app/store/thunk.js',
'app/telemetry/index.js',
'app/telemetry/telemetry.android.js',
'app/telemetry/telemetry_utils.js',
'app/utils/avoid_native_bridge.js',
'app/utils/client_upgrade.js',
'app/utils/general.js',

View file

@ -179,6 +179,9 @@ module.exports = ['./node_modules/app/app.js',
'./node_modules/app/store/index.js',
'./node_modules/app/store/middleware.js',
'./node_modules/app/store/thunk.js',
'./node_modules/app/telemetry/index.js',
'./node_modules/app/telemetry/telemetry.android.js',
'./node_modules/app/telemetry/telemetry_utils.js',
'./node_modules/app/utils/avoid_native_bridge.js',
'./node_modules/app/utils/client_upgrade.js',
'./node_modules/app/utils/general.js',