Merge pull request #166 from lbryio/first-run-improvements
implement extendable first run experience starting with welcome page
This commit is contained in:
commit
31168d9ed8
19 changed files with 333 additions and 78 deletions
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import AboutPage from '../page/about';
|
||||
import DiscoverPage from '../page/discover';
|
||||
import FilePage from '../page/file';
|
||||
import FirstRunScreen from '../page/firstRun';
|
||||
import SearchPage from '../page/search';
|
||||
import TrendingPage from '../page/trending';
|
||||
import SettingsPage from '../page/settings';
|
||||
|
@ -99,6 +100,12 @@ const drawer = DrawerNavigator({
|
|||
});
|
||||
|
||||
export const AppNavigator = new StackNavigator({
|
||||
FirstRun: {
|
||||
screen: FirstRunScreen,
|
||||
navigationOptions: {
|
||||
drawerLockMode: 'locked-closed'
|
||||
}
|
||||
},
|
||||
Splash: {
|
||||
screen: SplashScreen,
|
||||
navigationOptions: {
|
||||
|
@ -114,7 +121,7 @@ export const AppNavigator = new StackNavigator({
|
|||
|
||||
class AppWithNavigationState extends React.Component {
|
||||
static supportedDisplayTypes = ['toast'];
|
||||
|
||||
|
||||
componentWillMount() {
|
||||
AppState.addEventListener('change', this._handleAppStateChange);
|
||||
BackHandler.addEventListener('hardwareBackPress', function() {
|
||||
|
@ -129,7 +136,7 @@ class AppWithNavigationState extends React.Component {
|
|||
return true;
|
||||
}
|
||||
if (nav.routes[0].routeName === 'Main') {
|
||||
if (nav.routes[0].routes[0].routes[0].index > 0) {
|
||||
if (nav.routes[0].routes[0].routes[0].index > 0) {
|
||||
dispatch(NavigationActions.back());
|
||||
return true;
|
||||
}
|
||||
|
@ -138,17 +145,17 @@ class AppWithNavigationState extends React.Component {
|
|||
return false;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
Linking.addEventListener('url', this._handleUrl);
|
||||
}
|
||||
|
||||
|
||||
componentWillUnmount() {
|
||||
AppState.removeEventListener('change', this._handleAppStateChange);
|
||||
BackHandler.removeEventListener('hardwareBackPress');
|
||||
Linking.removeEventListener('url', this._handleUrl);
|
||||
}
|
||||
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
const { dispatch } = this.props;
|
||||
const { notification } = nextProps;
|
||||
|
@ -166,15 +173,15 @@ class AppWithNavigationState extends React.Component {
|
|||
} else if (AppWithNavigationState.supportedDisplayTypes.indexOf(displayType) > -1) {
|
||||
currentDisplayType = displayType;
|
||||
}
|
||||
|
||||
|
||||
if ('toast' === currentDisplayType) {
|
||||
ToastAndroid.show(message, ToastAndroid.SHORT);
|
||||
}
|
||||
|
||||
|
||||
dispatch(doHideNotification());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_handleAppStateChange = (nextAppState) => {
|
||||
// Check if the app was suspended
|
||||
if (AppState.currentState && AppState.currentState.match(/inactive|background/)) {
|
||||
|
@ -187,7 +194,7 @@ class AppWithNavigationState extends React.Component {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_handleUrl = (evt) => {
|
||||
const { dispatch } = this.props;
|
||||
if (evt.url) {
|
||||
|
@ -199,7 +206,7 @@ class AppWithNavigationState extends React.Component {
|
|||
dispatch(navigateAction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { dispatch, nav } = this.props;
|
||||
return (
|
||||
|
@ -220,5 +227,5 @@ const mapStateToProps = state => ({
|
|||
keepDaemonRunning: makeSelectClientSetting(SETTINGS.KEEP_DAEMON_RUNNING)(state),
|
||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_NSFW)(state)
|
||||
});
|
||||
|
||||
|
||||
export default connect(mapStateToProps)(AppWithNavigationState);
|
||||
|
|
|
@ -61,7 +61,7 @@ function enableBatching(reducer) {
|
|||
}
|
||||
|
||||
const router = AppNavigator.router;
|
||||
const navAction = router.getActionForPathAndParams('Splash');
|
||||
const navAction = router.getActionForPathAndParams('FirstRun');
|
||||
const initialNavState = router.getStateForAction(navAction);
|
||||
const navigatorReducer = (state = initialNavState, action) => {
|
||||
const nextState = AppNavigator.router.getStateForAction(action, state);
|
||||
|
@ -116,21 +116,11 @@ persistStore(store, persistOptions, err => {
|
|||
});
|
||||
|
||||
class LBRYApp extends React.Component {
|
||||
componentDidMount() {
|
||||
AsyncStorage.getItem('hasLaunched').then(value => {
|
||||
if (value == null || value !== 'true') {
|
||||
AsyncStorage.setItem('hasLaunched', 'true');
|
||||
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
||||
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<AppWithNavigationState />
|
||||
</Provider>
|
||||
<AppWithNavigationState />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
6
app/src/page/firstRun/index.js
Normal file
6
app/src/page/firstRun/index.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import FirstRun from './view';
|
||||
|
||||
const perform = dispatch => ({});
|
||||
|
||||
export default connect(null, perform)(FirstRun);
|
21
app/src/page/firstRun/internal/welcome-page.js
Normal file
21
app/src/page/firstRun/internal/welcome-page.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { View, Text, Linking } from 'react-native';
|
||||
import Colors from '../../../styles/colors';
|
||||
import firstRunStyle from '../../../styles/firstRun';
|
||||
|
||||
class WelcomePage extends React.PureComponent {
|
||||
render() {
|
||||
return (
|
||||
<View style={firstRunStyle.container}>
|
||||
<Text style={firstRunStyle.title}>Welcome to LBRY.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>LBRY is a decentralized peer-to-peer content sharing platform where
|
||||
you can upload and download videos, music, ebooks and other forms of digital content.</Text>
|
||||
<Text style={firstRunStyle.paragraph}>We make use of a blockchain which needs to be synchronized before
|
||||
you can use the app. Synchronization may take a while because this is the first app launch.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default WelcomePage;
|
96
app/src/page/firstRun/view.js
Normal file
96
app/src/page/firstRun/view.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import {
|
||||
Linking,
|
||||
NativeModules,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import Colors from '../../styles/colors';
|
||||
import WelcomePage from './internal/welcome-page';
|
||||
import firstRunStyle from '../../styles/firstRun';
|
||||
|
||||
class FirstRunScreen extends React.PureComponent {
|
||||
static pages = ['welcome'];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
currentPage: null,
|
||||
launchUrl: null,
|
||||
isFirstRun: false
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Linking.getInitialURL().then((url) => {
|
||||
if (url) {
|
||||
this.setState({ launchUrl: url });
|
||||
}
|
||||
});
|
||||
|
||||
if (NativeModules.FirstRun) {
|
||||
NativeModules.FirstRun.isFirstRun().then(firstRun => {
|
||||
this.setState({ isFirstRun: firstRun });
|
||||
if (firstRun) {
|
||||
this.setState({ currentPage: FirstRunScreen.pages[0] });
|
||||
} else {
|
||||
// Not the first run. Navigate to the splash screen right away
|
||||
this.launchSplashScreen();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// The first run module was not detected. Go straight to the splash screen.
|
||||
this.launchSplashScreen();
|
||||
}
|
||||
}
|
||||
|
||||
launchSplashScreen() {
|
||||
const { navigation } = this.props;
|
||||
const resetAction = NavigationActions.reset({
|
||||
index: 0,
|
||||
actions: [
|
||||
NavigationActions.navigate({ routeName: 'Splash', params: { launchUri: this.state.launchUri } })
|
||||
]
|
||||
});
|
||||
navigation.dispatch(resetAction);
|
||||
}
|
||||
|
||||
handleContinuePressed = () => {
|
||||
const pageIndex = FirstRunScreen.pages.indexOf(this.state.currentPage);
|
||||
if (pageIndex === (FirstRunScreen.pages.length - 1)) {
|
||||
// Final page. Let the app know that first run experience is completed.
|
||||
if (NativeModules.FirstRun) {
|
||||
NativeModules.FirstRun.firstRunCompleted();
|
||||
}
|
||||
|
||||
// Navigate to the splash screen
|
||||
this.launchSplashScreen();
|
||||
} else {
|
||||
// TODO: Page transition animation?
|
||||
this.state.currentPage = FirstRunScreen.pages[pageIndex + 1];
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let page = null;
|
||||
if (this.state.currentPage === 'welcome') {
|
||||
// show welcome page
|
||||
page = (<WelcomePage />);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={firstRunStyle.screenContainer}>
|
||||
{page}
|
||||
{this.state.currentPage &&
|
||||
<TouchableOpacity style={firstRunStyle.button} onPress={this.handleContinuePressed}>
|
||||
<Text style={firstRunStyle.buttonText}>Continue</Text>
|
||||
</TouchableOpacity>}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default FirstRunScreen;
|
|
@ -1,15 +1,16 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { View, Text, Linking, NativeModules } from 'react-native';
|
||||
import { ActivityIndicator, View, Text, Linking, NativeModules } from 'react-native';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import PropTypes from 'prop-types';
|
||||
import Colors from '../../styles/colors';
|
||||
import splashStyle from '../../styles/splash';
|
||||
|
||||
class SplashScreen extends React.PureComponent {
|
||||
static navigationOptions = {
|
||||
title: 'Splash'
|
||||
};
|
||||
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
details: 'Starting daemon',
|
||||
|
@ -18,6 +19,21 @@ class SplashScreen extends React.PureComponent {
|
|||
isLagging: false,
|
||||
launchUrl: null
|
||||
});
|
||||
|
||||
if (NativeModules.DaemonServiceControl) {
|
||||
NativeModules.DaemonServiceControl.startService();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Start measuring the first launch time from the splash screen (time from daemon start to user interaction)
|
||||
AsyncStorage.getItem('hasLaunched').then(value => {
|
||||
if (value == null || value !== 'true') {
|
||||
AsyncStorage.setItem('hasLaunched', 'true');
|
||||
// only set firstLaunchTime since we've determined that this is the first app launch ever
|
||||
AsyncStorage.setItem('firstLaunchTime', String(moment().unix()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
|
@ -25,7 +41,7 @@ class SplashScreen extends React.PureComponent {
|
|||
this._updateStatusCallback(status);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
_updateStatusCallback(status) {
|
||||
const startupStatus = status.startup_status;
|
||||
if (startupStatus.code == 'started') {
|
||||
|
@ -44,7 +60,7 @@ class SplashScreen extends React.PureComponent {
|
|||
// Leave the splash screen
|
||||
const { balanceSubscribe, navigation } = this.props;
|
||||
balanceSubscribe();
|
||||
|
||||
|
||||
const resetAction = NavigationActions.reset({
|
||||
index: 0,
|
||||
actions: [
|
||||
|
@ -52,9 +68,10 @@ class SplashScreen extends React.PureComponent {
|
|||
]
|
||||
});
|
||||
navigation.dispatch(resetAction);
|
||||
|
||||
if (this.state.launchUrl) {
|
||||
navigation.navigate({ routeName: 'File', key: this.state.launchUrl, params: { uri: this.state.launchUrl } });
|
||||
|
||||
const launchUrl = navigation.state.params.launchUrl || this.state.launchUrl;
|
||||
if (launchUrl) {
|
||||
navigation.navigate({ routeName: 'File', key: launchUrl, params: { uri: launchUrl } });
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
@ -78,7 +95,7 @@ class SplashScreen extends React.PureComponent {
|
|||
this.updateStatus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
if (NativeModules.Mixpanel) {
|
||||
NativeModules.Mixpanel.track('App Launch', null);
|
||||
|
@ -89,7 +106,7 @@ class SplashScreen extends React.PureComponent {
|
|||
this.setState({ launchUrl: url });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Lbry
|
||||
.connect()
|
||||
.then(() => {
|
||||
|
@ -107,10 +124,11 @@ class SplashScreen extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { message, details, isLagging, isRunning } = this.state;
|
||||
|
||||
|
||||
return (
|
||||
<View style={splashStyle.container}>
|
||||
<Text style={splashStyle.title}>LBRY</Text>
|
||||
<ActivityIndicator color={Colors.White} style={splashStyle.loading} size={"small"} />
|
||||
<Text style={splashStyle.message}>{message}</Text>
|
||||
<Text style={splashStyle.details}>{details}</Text>
|
||||
</View>
|
||||
|
|
44
app/src/styles/firstRun.js
Normal file
44
app/src/styles/firstRun.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const firstRunStyle = StyleSheet.create({
|
||||
screenContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.LbryGreen
|
||||
},
|
||||
container: {
|
||||
flex: 9,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: Colors.LbryGreen
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-SemiBold',
|
||||
fontSize: 40,
|
||||
marginLeft: 32,
|
||||
marginRight: 32,
|
||||
marginBottom: 32,
|
||||
color: Colors.White
|
||||
},
|
||||
paragraph: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 18,
|
||||
lineHeight: 24,
|
||||
marginLeft: 32,
|
||||
marginRight: 32,
|
||||
marginBottom: 20,
|
||||
color: Colors.White
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
alignSelf: 'flex-end',
|
||||
marginLeft: 32,
|
||||
marginRight: 32
|
||||
},
|
||||
buttonText: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 28,
|
||||
color: Colors.White
|
||||
}
|
||||
});
|
||||
|
||||
export default firstRunStyle;
|
|
@ -1,30 +1,34 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
import Colors from './colors';
|
||||
|
||||
const splashStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#40b89a'
|
||||
backgroundColor: Colors.LbryGreen
|
||||
},
|
||||
title: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 64,
|
||||
textAlign: 'center',
|
||||
marginBottom: 48,
|
||||
color: '#ffffff'
|
||||
color: Colors.White
|
||||
},
|
||||
loading: {
|
||||
marginBottom: 36
|
||||
},
|
||||
details: {
|
||||
fontFamily: 'Metropolis-Regular',
|
||||
fontSize: 14,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
color: '#ffffff',
|
||||
color: Colors.White,
|
||||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
fontFamily: 'Metropolis-Bold',
|
||||
fontSize: 18,
|
||||
color: '#ffffff',
|
||||
color: Colors.White,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
marginBottom: 4,
|
||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
|||
|
||||
# (list) Application requirements
|
||||
# comma seperated e.g. requirements = sqlite3,kivy
|
||||
requirements = openssl, sqlite3, hostpython2, android, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
|
||||
# (str) Custom source folders for requirements
|
||||
# Sets custom source for any requirements with recipes
|
||||
|
|
|
@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
|
|||
|
||||
# (list) Application requirements
|
||||
# comma seperated e.g. requirements = sqlite3,kivy
|
||||
requirements = openssl, sqlite3, hostpython2, android, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
|
||||
|
||||
# (str) Custom source folders for requirements
|
||||
# Sets custom source for any requirements with recipes
|
||||
|
|
|
@ -371,7 +371,7 @@ main.py that loads it.''')
|
|||
args=args,
|
||||
url_scheme=url_scheme,
|
||||
private_version=str(time.time()))
|
||||
|
||||
|
||||
# add colors.xml
|
||||
render(
|
||||
'colors.tmpl.xml',
|
||||
|
@ -380,6 +380,14 @@ main.py that loads it.''')
|
|||
url_scheme=url_scheme,
|
||||
)
|
||||
|
||||
# add themes.xml
|
||||
render(
|
||||
'themes.tmpl.xml',
|
||||
'src/main/res/values/themes.xml',
|
||||
args=args,
|
||||
url_scheme=url_scheme,
|
||||
)
|
||||
|
||||
# add activity_service_control
|
||||
render(
|
||||
'activity_service_control.xml',
|
||||
|
@ -396,12 +404,12 @@ main.py that loads it.''')
|
|||
aars=aars,
|
||||
android_api=android_api,
|
||||
build_tools_version=build_tools_version)
|
||||
|
||||
|
||||
render(
|
||||
'settings.tmpl.gradle',
|
||||
'settings.gradle'
|
||||
)
|
||||
|
||||
|
||||
# copy icon drawables
|
||||
for folder in ('drawable-hdpi', 'drawable-mdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'):
|
||||
shutil.copy(
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<application android:label="@string/app_name"
|
||||
android:icon="@drawable/icon"
|
||||
android:allowBackup="true"
|
||||
android:theme="@android:style/Theme.Material.Light"
|
||||
android:theme="@style/LbryAppTheme"
|
||||
android:hardwareAccelerated="true">
|
||||
|
||||
{% for m in args.meta_data %}
|
||||
|
@ -64,7 +64,7 @@
|
|||
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
||||
android:screenOrientation="{{ args.orientation }}"
|
||||
-->
|
||||
|
||||
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
<activity android:name="io.lbry.browser.MainActivity"
|
||||
|
@ -74,7 +74,7 @@
|
|||
android:screenOrientation="{{ args.orientation }}"
|
||||
android:launchMode="singleInstance"
|
||||
>
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
@ -99,9 +99,9 @@
|
|||
{{- args.intent_filters -}}
|
||||
{%- endif -%}
|
||||
</activity>
|
||||
|
||||
|
||||
<receiver android:name="io.lbry.browser.receivers.NotificationDeletedReceiver" />
|
||||
|
||||
|
||||
{% if args.launcher %}
|
||||
<activity android:name="org.kivy.android.launcher.ProjectChooser"
|
||||
android:icon="@drawable/icon"
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
|
||||
<color name="red">#FF0000</color>
|
||||
<color name="green">#00C000</color>
|
||||
<color name="lbrygreen">#40B89A</color>
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<resources>
|
||||
<style name="LbryAppTheme" parent="@android:style/Theme.Material.Light">
|
||||
<item name="android:windowBackground">@color/lbrygreen</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -27,27 +27,27 @@ import io.lbry.browser.reactpackages.LbryReactPackage;
|
|||
import io.lbry.browser.reactmodules.DownloadManagerModule;
|
||||
|
||||
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||
|
||||
|
||||
private static final int OVERLAY_PERMISSION_REQ_CODE = 101;
|
||||
|
||||
private static final int STORAGE_PERMISSION_REQ_CODE = 201;
|
||||
|
||||
|
||||
private static final int STORAGE_PERMISSION_REQ_CODE = 201;
|
||||
|
||||
private ReactRootView mReactRootView;
|
||||
|
||||
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
|
||||
public static final String SHARED_PREFERENCES_NAME = "LBRY";
|
||||
|
||||
|
||||
/**
|
||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||
* onResume method.
|
||||
*/
|
||||
private boolean serviceRunning;
|
||||
|
||||
|
||||
protected String getMainComponentName() {
|
||||
return "LBRYApp";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
@ -63,15 +63,15 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
||||
// Start the daemon service if it is not started
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
|
||||
mReactRootView = new ReactRootView(this);
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
|
@ -87,7 +87,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
|
||||
setContentView(mReactRootView);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
|
||||
|
@ -98,7 +98,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
|
@ -127,30 +127,35 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
public void invokeDefaultOnBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostPause(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
|
||||
SharedPreferences sp = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
if (!sp.getBoolean("firstRun", true)) {
|
||||
// We're not showing the welcome page, so it's okay to start the daemon service
|
||||
// because this is not the first run experience
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostResume(this, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// check service running setting and end it here
|
||||
|
@ -164,7 +169,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
}
|
||||
|
||||
super.onDestroy();
|
||||
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostDestroy(this);
|
||||
}
|
||||
|
@ -178,7 +183,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
if (mReactInstanceManager != null) {
|
||||
|
@ -186,7 +191,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
}
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
|
||||
|
||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
|
|
|
@ -24,6 +24,11 @@ public class DaemonServiceControlModule extends ReactContextBaseJavaModule {
|
|||
return "DaemonServiceControl";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startService() {
|
||||
ServiceHelper.start(context, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void stopService() {
|
||||
ServiceHelper.stop(context, LbrynetService.class);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
|
||||
public class FirstRunModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
private SharedPreferences sp;
|
||||
|
||||
public FirstRunModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
this.sp = reactContext.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FirstRun";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void isFirstRun(final Promise promise) {
|
||||
// If firstRun flag does not exist, default to true
|
||||
boolean firstRun = sp.getBoolean("firstRun", true);
|
||||
promise.resolve(firstRun);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void firstRunCompleted() {
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstRun", false);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
public class VersionInfoModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.facebook.react.uimanager.ViewManager;
|
|||
|
||||
import io.lbry.browser.reactmodules.DaemonServiceControlModule;
|
||||
import io.lbry.browser.reactmodules.DownloadManagerModule;
|
||||
import io.lbry.browser.reactmodules.FirstRunModule;
|
||||
import io.lbry.browser.reactmodules.MixpanelModule;
|
||||
import io.lbry.browser.reactmodules.ScreenOrientationModule;
|
||||
import io.lbry.browser.reactmodules.VersionInfoModule;
|
||||
|
@ -27,10 +28,11 @@ public class LbryReactPackage implements ReactPackage {
|
|||
|
||||
modules.add(new DaemonServiceControlModule(reactContext));
|
||||
modules.add(new DownloadManagerModule(reactContext));
|
||||
modules.add(new FirstRunModule(reactContext));
|
||||
modules.add(new MixpanelModule(reactContext));
|
||||
modules.add(new ScreenOrientationModule(reactContext));
|
||||
modules.add(new VersionInfoModule(reactContext));
|
||||
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue