Splash screen, discover and file Pages #30
39 changed files with 100386 additions and 820 deletions
|
@ -12,7 +12,7 @@ install:
|
|||
- sudo pip install --upgrade cython==0.25.2 pip setuptools
|
||||
- git clone https://github.com/akinwale/buildozer.git
|
||||
- cd app
|
||||
- npm install --silent --save react@16.0.0 react-native
|
||||
- npm install --silent --save react@16.2.0 react-native@0.52.0
|
||||
- cd ..
|
||||
- cd buildozer
|
||||
- sudo python setup.py install
|
||||
|
@ -34,5 +34,5 @@ install:
|
|||
- mkdir -p ~/.buildozer/android/platform/android-sdk-23/licenses
|
||||
- echo $'\nd56f5187479451eabf01fb78af6dfcb131a6481e' > ~/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license
|
||||
script:
|
||||
- buildozer android debug | grep -Fv -e 'working:' -e 'copy' --line-buffered
|
||||
- ./build.sh | grep -Fv -e 'working:' -e 'copy' --line-buffered
|
||||
- cp bin/*.apk /dev/null
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/sh
|
||||
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
react-native bundle --platform android --dev false --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
|
|
7101
app/package-lock.json
generated
7101
app/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -3,10 +3,21 @@
|
|||
"version": "0.0.1",
|
||||
"private": "true",
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start"
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"postinstall": "cp ./patch/ReactNativeRenderer-dev.js.patch ./node_modules/react-native/Libraries/Renderer/ReactNativeRenderer-dev.js; rm ./node_modules/react-native/local-cli/core/__fixtures__/files/package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^16.0.0",
|
||||
"react-native": "^0.51.0"
|
||||
"lbry-redux": "lbryio/lbry-redux#common-components-refactor",
|
||||
"react": "16.2.0",
|
||||
"react-native": "0.52.0",
|
||||
"react-native-vector-icons": "^4.5.0",
|
||||
"react-navigation": "^1.0.3",
|
||||
"react-navigation-redux-helpers": "^1.0.1",
|
||||
"react-redux": "^5.0.3",
|
||||
"redux": "^3.6.0",
|
||||
"redux-persist": "^4.8.0",
|
||||
"redux-persist-transform-compress": "^4.2.0",
|
||||
"redux-persist-transform-filter": "0.0.10",
|
||||
"redux-thunk": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
|
13009
app/patch/ReactNativeRenderer-dev.js.patch
Normal file
13009
app/patch/ReactNativeRenderer-dev.js.patch
Normal file
File diff suppressed because it is too large
Load diff
84
app/src/component/AppNavigator.js
Normal file
84
app/src/component/AppNavigator.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
import React from 'react';
|
||||
import DiscoverPage from '../page/discover';
|
||||
import FilePage from '../page/file';
|
||||
import SplashScreen from '../page/splash';
|
||||
import { addNavigationHelpers, DrawerNavigator, StackNavigator } from 'react-navigation';
|
||||
import { connect } from 'react-redux';
|
||||
import { addListener } from '../utils/redux';
|
||||
import { BackHandler } from 'react-native';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
import discoverStyle from '../styles/discover';
|
||||
|
||||
const discoverStack = StackNavigator({
|
||||
Discover: {
|
||||
screen: DiscoverPage,
|
||||
navigationOptions: ({ navigation }) => ({
|
||||
title: 'Discover',
|
||||
headerLeft: <Feather name="menu" size={24} style={discoverStyle.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />
|
||||
})
|
||||
},
|
||||
File: {
|
||||
screen: FilePage,
|
||||
navigationOptions: {
|
||||
header: null
|
||||
}
|
||||
}
|
||||
}, {
|
||||
headerMode: 'screen',
|
||||
});
|
||||
|
||||
const drawer = DrawerNavigator({
|
||||
Discover: { screen: discoverStack },
|
||||
}, {
|
||||
drawerWidth: 300,
|
||||
headerMode: 'none'
|
||||
});
|
||||
|
||||
export const AppNavigator = new StackNavigator({
|
||||
Splash: {
|
||||
screen: SplashScreen
|
||||
},
|
||||
Main: {
|
||||
screen: drawer
|
||||
}
|
||||
}, {
|
||||
headerMode: 'none'
|
||||
});
|
||||
|
||||
class AppWithNavigationState extends React.Component {
|
||||
componentWillMount() {
|
||||
BackHandler.addEventListener('hardwareBackPress', function() {
|
||||
const { dispatch, navigation, nav } = this.props;
|
||||
if (nav.routes.length === 2 && nav.routes[1].routeName === 'Main') {
|
||||
if (nav.routes[1].routes[0].routes[0].index > 0) {
|
||||
dispatch({ type: 'Navigation/BACK' });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
BackHandler.removeEventListener('hardwareBackPress');
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, nav } = this.props;
|
||||
return (
|
||||
<AppNavigator
|
||||
navigation={addNavigationHelpers({
|
||||
dispatch,
|
||||
state: nav,
|
||||
addListener,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
nav: state.nav,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(AppWithNavigationState);
|
7
app/src/component/featuredCategory/index.js
Normal file
7
app/src/component/featuredCategory/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import FeaturedCategory from './view';
|
||||
|
||||
const select = state => ({});
|
||||
const perform = dispatch => ({});
|
||||
|
||||
export default connect(select, perform)(FeaturedCategory);
|
27
app/src/component/featuredCategory/view.js
Normal file
27
app/src/component/featuredCategory/view.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import FileItem from '../fileItem';
|
||||
import discoverStyle from '../../styles/discover';
|
||||
|
||||
class FeaturedCategory extends React.PureComponent {
|
||||
render() {
|
||||
const { category, names, categoryLink, navigation } = this.props;
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text style={discoverStyle.categoryName}>{category}</Text>
|
||||
{names &&
|
||||
names.map(name => (
|
||||
<FileItem
|
||||
style={discoverStyle.fileItem}
|
||||
key={name}
|
||||
uri={normalizeURI(name)}
|
||||
navigation={navigation} />
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FeaturedCategory;
|
25
app/src/component/fileItem/index.js
Normal file
25
app/src/component/fileItem/index.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doResolveUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectMetadataForUri,
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectIsUriResolving,
|
||||
selectRewardContentClaimIds
|
||||
} from 'lbry-redux';
|
||||
/*import { selectShowNsfw } from 'redux/selectors/settings';*/
|
||||
import FileItem from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
|
||||
isResolvingUri: makeSelectIsUriResolving(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FileItem);
|
71
app/src/component/fileItem/view.js
Normal file
71
app/src/component/fileItem/view.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
import React from 'react';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import { NavigationActions } from 'react-navigation';
|
||||
import { Text, View, TouchableOpacity } from 'react-native';
|
||||
import FileItemMedia from '../fileItemMedia';
|
||||
import FilePrice from '../filePrice';
|
||||
import discoverStyle from '../../styles/discover';
|
||||
|
||||
class FileItem extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.resolve(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.resolve(nextProps);
|
||||
}
|
||||
|
||||
resolve(props) {
|
||||
const { isResolvingUri, resolveUri, claim, uri } = props;
|
||||
|
||||
if (!isResolvingUri && claim === undefined && uri) {
|
||||
resolveUri(uri);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
claim,
|
||||
fileInfo,
|
||||
metadata,
|
||||
isResolvingUri,
|
||||
rewardedContentClaimIds,
|
||||
style
|
||||
} = this.props;
|
||||
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
const title = metadata && metadata.title ? metadata.title : uri;
|
||||
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
|
||||
const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
|
||||
const channelName = claim ? claim.channel_name : null;
|
||||
|
||||
let description = '';
|
||||
if (isResolvingUri && !claim) {
|
||||
description = 'Loading...';
|
||||
} else if (metadata && metadata.description) {
|
||||
description = metadata.description;
|
||||
} else if (claim === null) {
|
||||
description = 'This address contains no content.';
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableOpacity style={style} onPress={() => {
|
||||
this.props.navigation.navigate('File', { uri: uri });
|
||||
}
|
||||
}>
|
||||
<FileItemMedia title={title} thumbnail={thumbnail} />
|
||||
<FilePrice uri={uri} style={discoverStyle.filePriceContainer} textStyle={discoverStyle.filePriceText} />
|
||||
<Text style={discoverStyle.fileItemName}>{title}</Text>
|
||||
{channelName &&
|
||||
<Text style={discoverStyle.channelName}>{channelName}</Text>}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FileItem;
|
7
app/src/component/fileItemMedia/index.js
Normal file
7
app/src/component/fileItemMedia/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import FileItemMedia from './view';
|
||||
|
||||
const select = state => ({});
|
||||
const perform = dispatch => ({});
|
||||
|
||||
export default connect(select, perform)(FileItemMedia);
|
56
app/src/component/fileItemMedia/view.js
Normal file
56
app/src/component/fileItemMedia/view.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
import React from 'react';
|
||||
import { Text, Image, View } from 'react-native';
|
||||
import fileItemMediaStyle from '../../styles/fileItemMedia';
|
||||
|
||||
class FileItemMedia extends React.PureComponent {
|
||||
static AUTO_THUMB_STYLES = [
|
||||
fileItemMediaStyle.autothumbPurple,
|
||||
fileItemMediaStyle.autothumbRed,
|
||||
fileItemMediaStyle.autothumbPink,
|
||||
fileItemMediaStyle.autothumbIndigo,
|
||||
fileItemMediaStyle.autothumbBlue,
|
||||
fileItemMediaStyle.autothumbLightBlue,
|
||||
fileItemMediaStyle.autothumbCyan,
|
||||
fileItemMediaStyle.autothumbTeal,
|
||||
fileItemMediaStyle.autothumbGreen,
|
||||
fileItemMediaStyle.autothumbYellow,
|
||||
fileItemMediaStyle.autothumbOrange,
|
||||
];
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
autoThumbStyle:
|
||||
FileItemMedia.AUTO_THUMB_STYLES[
|
||||
Math.floor(Math.random() * FileItemMedia.AUTO_THUMB_STYLES.length)
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let style = this.props.style;
|
||||
const { title, thumbnail } = this.props;
|
||||
const atStyle = this.state.autoThumbStyle;
|
||||
|
||||
if (thumbnail && ((typeof thumbnail) === 'string')) {
|
||||
if (style == null) {
|
||||
style = fileItemMediaStyle.thumbnail;
|
||||
}
|
||||
|
||||
return (
|
||||
<Image source={{uri: thumbnail}} resizeMode="cover" style={style} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[fileItemMediaStyle.autothumb, atStyle]}>
|
||||
<Text style={fileItemMediaStyle.autothumbText}>{title &&
|
||||
title
|
||||
.replace(/\s+/g, '')
|
||||
.substring(0, Math.min(title.replace(' ', '').length, 5))
|
||||
.toUpperCase()}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FileItemMedia;
|
20
app/src/component/filePrice/index.js
Normal file
20
app/src/component/filePrice/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doFetchCostInfoForUri,
|
||||
makeSelectCostInfoForUri,
|
||||
makeSelectFetchingCostInfoForUri,
|
||||
makeSelectClaimForUri
|
||||
} from 'lbry-redux';
|
||||
import FilePrice from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||
fetching: makeSelectFetchingCostInfoForUri(props.uri)(state),
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FilePrice);
|
120
app/src/component/filePrice/view.js
Normal file
120
app/src/component/filePrice/view.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text, View } from 'react-native';
|
||||
import { formatCredits, formatFullPrice } from 'lbry-redux';
|
||||
|
||||
class CreditAmount extends React.PureComponent {
|
||||
static propTypes = {
|
||||
amount: PropTypes.number.isRequired,
|
||||
precision: PropTypes.number,
|
||||
isEstimate: PropTypes.bool,
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
showFree: PropTypes.bool,
|
||||
showFullPrice: PropTypes.bool,
|
||||
showPlus: PropTypes.bool,
|
||||
look: PropTypes.oneOf(['indicator', 'plain', 'fee']),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
precision: 2,
|
||||
label: true,
|
||||
showFree: false,
|
||||
look: 'indicator',
|
||||
showFullPrice: false,
|
||||
showPlus: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const minimumRenderableAmount = Math.pow(10, -1 * this.props.precision);
|
||||
const { amount, precision, showFullPrice, style } = this.props;
|
||||
|
||||
let formattedAmount;
|
||||
const fullPrice = formatFullPrice(amount, 2);
|
||||
|
||||
if (showFullPrice) {
|
||||
formattedAmount = fullPrice;
|
||||
} else {
|
||||
formattedAmount =
|
||||
amount > 0 && amount < minimumRenderableAmount
|
||||
? `<${minimumRenderableAmount}`
|
||||
: formatCredits(amount, precision);
|
||||
}
|
||||
|
||||
let amountText;
|
||||
if (this.props.showFree && parseFloat(this.props.amount) === 0) {
|
||||
amountText = 'FREE';
|
||||
} else {
|
||||
if (this.props.label) {
|
||||
const label =
|
||||
typeof this.props.label === 'string'
|
||||
? this.props.label
|
||||
: parseFloat(amount) == 1 ? 'credit' : 'credits';
|
||||
|
||||
amountText = `${formattedAmount} ${label}`;
|
||||
} else {
|
||||
amountText = formattedAmount;
|
||||
}
|
||||
if (this.props.showPlus && amount > 0) {
|
||||
amountText = `+${amountText}`;
|
||||
}
|
||||
}
|
||||
|
||||
/*{this.props.isEstimate ? (
|
||||
<span
|
||||
className="credit-amount__estimate"
|
||||
title={__('This is an estimate and does not include data fees')}
|
||||
>
|
||||
*
|
||||
</span>
|
||||
) : null}*/
|
||||
return (
|
||||
<Text style={style}>{amountText}</Text>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FilePrice extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
this.fetchCost(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.fetchCost(nextProps);
|
||||
}
|
||||
|
||||
fetchCost(props) {
|
||||
const { costInfo, fetchCostInfo, uri, fetching, claim } = props;
|
||||
|
||||
if (costInfo === undefined && !fetching && claim) {
|
||||
fetchCostInfo(uri);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { costInfo, look = 'indicator', showFullPrice = false, style, textStyle } = this.props;
|
||||
|
||||
const isEstimate = costInfo ? !costInfo.includesData : null;
|
||||
|
||||
if (!costInfo) {
|
||||
return (
|
||||
<View style={style}>
|
||||
<Text style={textStyle}>???</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style}>
|
||||
<CreditAmount
|
||||
style={textStyle}
|
||||
label={false}
|
||||
amount={costInfo.cost}
|
||||
isEstimate={isEstimate}
|
||||
showFree
|
||||
showFullPrice={showFullPrice}>???</CreditAmount>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePrice;
|
116
app/src/index.js
Normal file
116
app/src/index.js
Normal file
|
@ -0,0 +1,116 @@
|
|||
import React from 'react';
|
||||
import { Provider, connect } from 'react-redux';
|
||||
import DiscoverPage from './page/discover';
|
||||
import { AppRegistry, StyleSheet, Text, View, AsyncStorage } from 'react-native';
|
||||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
||||
import {
|
||||
StackNavigator, addNavigationHelpers
|
||||
} from 'react-navigation';
|
||||
import { AppNavigator } from './component/AppNavigator';
|
||||
import AppWithNavigationState from './component/AppNavigator';
|
||||
import { persistStore, autoRehydrate } from 'redux-persist';
|
||||
import createCompressor from 'redux-persist-transform-compress';
|
||||
import createFilter from 'redux-persist-transform-filter';
|
||||
import thunk from 'redux-thunk';
|
||||
import {
|
||||
Lbry,
|
||||
claimsReducer,
|
||||
costInfoReducer,
|
||||
fileInfoReducer,
|
||||
searchReducer,
|
||||
walletReducer
|
||||
} from 'lbry-redux';
|
||||
import { reactNavigationMiddleware } from './utils/redux';
|
||||
|
||||
function isFunction(object) {
|
||||
return typeof object === 'function';
|
||||
}
|
||||
|
||||
function isNotFunction(object) {
|
||||
return !isFunction(object);
|
||||
}
|
||||
|
||||
function createBulkThunkMiddleware() {
|
||||
return ({ dispatch, getState }) => next => action => {
|
||||
if (action.type === 'BATCH_ACTIONS') {
|
||||
action.actions.filter(isFunction).map(actionFn => actionFn(dispatch, getState));
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
||||
function enableBatching(reducer) {
|
||||
return function batchingReducer(state, action) {
|
||||
switch (action.type) {
|
||||
case 'BATCH_ACTIONS':
|
||||
return action.actions.filter(isNotFunction).reduce(batchingReducer, state);
|
||||
default:
|
||||
return reducer(state, action);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const router = AppNavigator.router;
|
||||
const navAction = router.getActionForPathAndParams('Splash');
|
||||
const initialNavState = router.getStateForAction(navAction);
|
||||
const navigatorReducer = (state = initialNavState, action) => {
|
||||
const nextState = AppNavigator.router.getStateForAction(action, state);
|
||||
return nextState || state;
|
||||
};
|
||||
|
||||
const reducers = combineReducers({
|
||||
claims: claimsReducer,
|
||||
costInfo: costInfoReducer,
|
||||
fileInfo: fileInfoReducer,
|
||||
search: searchReducer,
|
||||
wallet: walletReducer,
|
||||
nav: navigatorReducer
|
||||
});
|
||||
|
||||
const bulkThunk = createBulkThunkMiddleware();
|
||||
const middleware = [thunk, bulkThunk, reactNavigationMiddleware];
|
||||
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const composeEnhancers = compose;
|
||||
|
||||
const store = createStore(
|
||||
enableBatching(reducers),
|
||||
{}, // initial state,
|
||||
composeEnhancers(
|
||||
autoRehydrate(),
|
||||
applyMiddleware(...middleware)
|
||||
)
|
||||
);
|
||||
|
||||
const compressor = createCompressor();
|
||||
const saveClaimsFilter = createFilter('claims', ['byId', 'claimsByUri']);
|
||||
const subscriptionsFilter = createFilter('subscriptions', ['subscriptions']);
|
||||
|
||||
const persistOptions = {
|
||||
whitelist: ['claims', 'subscriptions'],
|
||||
// Order is important. Needs to be compressed last or other transforms can't
|
||||
// read the data
|
||||
transforms: [saveClaimsFilter, subscriptionsFilter, compressor],
|
||||
debounce: 10000,
|
||||
storage: AsyncStorage
|
||||
};
|
||||
|
||||
persistStore(store, persistOptions, err => {
|
||||
if (err) {
|
||||
console.log('Unable to load saved SETTINGS');
|
||||
}
|
||||
});
|
||||
|
||||
class LBRYApp extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<AppWithNavigationState />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AppRegistry.registerComponent('LBRYApp', () => LBRYApp);
|
||||
|
||||
export default LBRYApp;
|
14
app/src/page/discover/index.js
Normal file
14
app/src/page/discover/index.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doFetchFeaturedUris, selectFeaturedUris, selectFetchingFeaturedUris } from 'lbry-redux';
|
||||
import DiscoverPage from './view';
|
||||
|
||||
const select = state => ({
|
||||
featuredUris: selectFeaturedUris(state),
|
||||
fetchingFeaturedUris: selectFetchingFeaturedUris(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(DiscoverPage);
|
44
app/src/page/discover/view.js
Normal file
44
app/src/page/discover/view.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import FeaturedCategory from '../../component/featuredCategory';
|
||||
import NavigationActions from 'react-navigation';
|
||||
import { Text, View, ScrollView } from 'react-native';
|
||||
import discoverStyle from '../../styles/discover';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
|
||||
class DiscoverPage extends React.PureComponent {
|
||||
componentWillMount() {
|
||||
this.props.fetchFeaturedUris();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { featuredUris, fetchingFeaturedUris } = this.props;
|
||||
const hasContent = typeof featuredUris === 'object' && Object.keys(featuredUris).length,
|
||||
failedToLoad = !fetchingFeaturedUris && !hasContent;
|
||||
|
||||
return (
|
||||
<View style={discoverStyle.container}>
|
||||
{!hasContent && fetchingFeaturedUris && <Text style={discoverStyle.title}>Fetching content...</Text>}
|
||||
{hasContent &&
|
||||
<ScrollView style={discoverStyle.scrollContainer}>
|
||||
{hasContent &&
|
||||
Object.keys(featuredUris).map(
|
||||
category =>
|
||||
featuredUris[category].length ? (
|
||||
<FeaturedCategory
|
||||
key={category}
|
||||
category={category}
|
||||
names={featuredUris[category]}
|
||||
navigation={this.props.navigation}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
)}
|
||||
</ScrollView>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DiscoverPage;
|
34
app/src/page/file/index.js
Normal file
34
app/src/page/file/index.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doFetchFileInfo,
|
||||
makeSelectFileInfoForUri,
|
||||
doFetchCostInfoForUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectContentTypeForUri,
|
||||
makeSelectMetadataForUri,
|
||||
selectRewardContentClaimIds,
|
||||
makeSelectCostInfoForUri
|
||||
} from 'lbry-redux';
|
||||
//import { selectShowNsfw } from 'redux/selectors/settings';
|
||||
import FilePage from './view';
|
||||
|
||||
const select = (state, props) => {
|
||||
const selectProps = { uri: props.navigation.state.params.uri };
|
||||
return {
|
||||
claim: makeSelectClaimForUri(selectProps.uri)(state),
|
||||
contentType: makeSelectContentTypeForUri(selectProps.uri)(state),
|
||||
costInfo: makeSelectCostInfoForUri(selectProps.uri)(state),
|
||||
metadata: makeSelectMetadataForUri(selectProps.uri)(state),
|
||||
//obscureNsfw: !selectShowNsfw(state),
|
||||
//tab: makeSelectCurrentParam('tab')(state),
|
||||
fileInfo: makeSelectFileInfoForUri(selectProps.uri)(state),
|
||||
rewardedContentClaimIds: selectRewardContentClaimIds(state, selectProps),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
|
||||
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FilePage);
|
79
app/src/page/file/view.js
Normal file
79
app/src/page/file/view.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
import React from 'react';
|
||||
import { Text, View, ScrollView } from 'react-native';
|
||||
import filePageStyle from '../../styles/filePage';
|
||||
import FileItemMedia from '../../component/fileItemMedia';
|
||||
|
||||
class FilePage extends React.PureComponent {
|
||||
static navigationOptions = {
|
||||
title: ''
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchFileInfo(this.props);
|
||||
this.fetchCostInfo(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.fetchFileInfo(nextProps);
|
||||
}
|
||||
|
||||
fetchFileInfo(props) {
|
||||
if (props.fileInfo === undefined) {
|
||||
props.fetchFileInfo(props.navigation.state.params.uri);
|
||||
}
|
||||
}
|
||||
|
||||
fetchCostInfo(props) {
|
||||
if (props.costInfo === undefined) {
|
||||
props.fetchCostInfo(props.navigation.state.params.uri);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
claim,
|
||||
fileInfo,
|
||||
metadata,
|
||||
contentType,
|
||||
tab,
|
||||
uri,
|
||||
rewardedContentClaimIds,
|
||||
} = this.props;
|
||||
|
||||
if (!claim || !metadata) {
|
||||
return (
|
||||
<View style={filePageStyle.container}>
|
||||
<Text style={filePageStyle.emptyClaimText}>Empty claim or metadata info.</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const title = metadata.title;
|
||||
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
|
||||
const description = metadata.description ? metadata.description : null;
|
||||
//const mediaType = lbry.getMediaType(contentType);
|
||||
//const player = require('render-media');
|
||||
//const obscureNsfw = this.props.obscureNsfw && metadata && metadata.nsfw;
|
||||
/*const isPlayable =
|
||||
Object.values(player.mime).indexOf(contentType) !== -1 || mediaType === 'audio';*/
|
||||
const { height, channel_name: channelName, value } = claim;
|
||||
const channelClaimId =
|
||||
value && value.publisherSignature && value.publisherSignature.certificateId;
|
||||
|
||||
|
||||
return (
|
||||
<View style={filePageStyle.pageContainer}>
|
||||
<View style={filePageStyle.mediaContainer}>
|
||||
<FileItemMedia style={filePageStyle.thumbnail} title={title} thumbnail={metadata.thumbnail} />
|
||||
</View>
|
||||
<ScrollView style={filePageStyle.scrollContainer}>
|
||||
<Text style={filePageStyle.title}>{title}</Text>
|
||||
{channelName && <Text style={filePageStyle.channelName}>{channelName}</Text>}
|
||||
{description && <Text style={filePageStyle.description}>{description}</Text>}
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FilePage;
|
7
app/src/page/splash/index.js
Normal file
7
app/src/page/splash/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import SplashScreen from './view';
|
||||
|
||||
const select = state => ({});
|
||||
const perform = dispatch => ({});
|
||||
|
||||
export default connect(select, perform)(SplashScreen);
|
97
app/src/page/splash/view.js
Normal file
97
app/src/page/splash/view.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import React from 'react';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import { View, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import splashStyle from '../../styles/splash';
|
||||
|
||||
class SplashScreen extends React.PureComponent {
|
||||
static navigationOptions = {
|
||||
title: 'Splash'
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
details: 'Starting daemon',
|
||||
message: 'Connecting',
|
||||
isRunning: false,
|
||||
isLagging: false,
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
Lbry.status().then(status => {
|
||||
this._updateStatusCallback(status);
|
||||
});
|
||||
}
|
||||
|
||||
_updateStatusCallback(status) {
|
||||
const startupStatus = status.startup_status;
|
||||
if (startupStatus.code == 'started') {
|
||||
// Wait until we are able to resolve a name before declaring
|
||||
// that we are done.
|
||||
// TODO: This is a hack, and the logic should live in the daemon
|
||||
// to give us a better sense of when we are actually started
|
||||
this.setState({
|
||||
message: 'Testing Network',
|
||||
details: 'Waiting for name resolution',
|
||||
isLagging: false,
|
||||
isRunning: true,
|
||||
});
|
||||
|
||||
Lbry.resolve({ uri: 'lbry://one' }).then(() => {
|
||||
// Leave the splash screen
|
||||
const { navigation } = this.props;
|
||||
navigation.navigate('Main');
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (status.blockchain_status && status.blockchain_status.blocks_behind > 0) {
|
||||
const behind = status.blockchain_status.blocks_behind;
|
||||
const behindText = behind + ' block' + (behind == 1 ? '' : 's') + ' behind';
|
||||
this.setState({
|
||||
message: 'Blockchain Sync',
|
||||
details: behindText,
|
||||
isLagging: startupStatus.is_lagging,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
message: 'Network Loading',
|
||||
details: startupStatus.message + (startupStatus.is_lagging ? '' : '...'),
|
||||
isLagging: startupStatus.is_lagging,
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.updateStatus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Lbry
|
||||
.connect()
|
||||
.then(() => {
|
||||
this.updateStatus();
|
||||
})
|
||||
.catch((e) => {
|
||||
this.setState({
|
||||
isLagging: true,
|
||||
message: 'Connection Failure',
|
||||
details:
|
||||
'We could not establish a connection to the daemon. Your data connection may be preventing LBRY from connecting. Contact hello@lbry.io if you think this is a software bug.'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { message, details, isLagging, isRunning } = this.state;
|
||||
|
||||
return (
|
||||
<View style={splashStyle.container}>
|
||||
<Text style={splashStyle.title}>Lbry.</Text>
|
||||
<Text style={splashStyle.message}>{message}</Text>
|
||||
<Text style={splashStyle.details}>{details}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SplashScreen;
|
60
app/src/styles/discover.js
Normal file
60
app/src/styles/discover.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const discoverStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
scrollContainer: {
|
||||
flex: 1
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
textAlign: 'center',
|
||||
margin: 10,
|
||||
},
|
||||
categoryName: {
|
||||
fontSize: 20,
|
||||
marginLeft: 24,
|
||||
marginTop: 16,
|
||||
marginBottom: 16,
|
||||
color: '#40b89a'
|
||||
},
|
||||
fileItem: {
|
||||
marginLeft: 24,
|
||||
marginRight: 24,
|
||||
marginBottom: 48
|
||||
},
|
||||
fileItemName: {
|
||||
marginTop: 8,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
channelName: {
|
||||
fontSize: 14,
|
||||
marginTop: 4,
|
||||
color: '#c0c0c0',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
filePriceContainer: {
|
||||
backgroundColor: '#61fcd8',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
right: 16,
|
||||
top: 16,
|
||||
width: 56,
|
||||
height: 24,
|
||||
borderRadius: 4
|
||||
},
|
||||
filePriceText: {
|
||||
fontSize: 12,
|
||||
textAlign: 'center',
|
||||
color: '#0c604b',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
drawerHamburger: {
|
||||
marginLeft: 8
|
||||
}
|
||||
});
|
||||
|
||||
export default discoverStyle;
|
57
app/src/styles/fileItemMedia.js
Normal file
57
app/src/styles/fileItemMedia.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { StyleSheet, Dimensions } from 'react-native';
|
||||
|
||||
const screenDimension = Dimensions.get('window');
|
||||
const width = screenDimension.width - 48; // screen width minus combined left and right margins
|
||||
|
||||
const fileItemMediaStyle = StyleSheet.create({
|
||||
autothumb: {
|
||||
width: width,
|
||||
height: 180,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
autothumbText: {
|
||||
textAlign: 'center',
|
||||
color: '#ffffff',
|
||||
fontSize: 40
|
||||
},
|
||||
autothumbPurple: {
|
||||
backgroundColor: '#9c27b0'
|
||||
},
|
||||
autothumbRed: {
|
||||
backgroundColor: '#e53935'
|
||||
},
|
||||
autothumbPink: {
|
||||
backgroundColor: '#e91e63'
|
||||
},
|
||||
autothumbIndigo: {
|
||||
backgroundColor: '#3f51b5'
|
||||
},
|
||||
autothumbBlue: {
|
||||
backgroundColor: '#2196f3'
|
||||
},
|
||||
autothumbLightBlue: {
|
||||
backgroundColor: '#039be5'
|
||||
},
|
||||
autothumbCyan: {
|
||||
backgroundColor: '#00acc1'
|
||||
},
|
||||
autothumbTeal: {
|
||||
backgroundColor: '#009688'
|
||||
},
|
||||
autothumbGreen: {
|
||||
backgroundColor: '#43a047'
|
||||
},
|
||||
autothumbYellow: {
|
||||
backgroundColor: '#ffeb3b'
|
||||
},
|
||||
autothumbOrange: {
|
||||
backgroundColor: '#ffa726'
|
||||
},
|
||||
thumbnail: {
|
||||
width: width,
|
||||
height: 180,
|
||||
shadowColor: 'transparent'
|
||||
}
|
||||
});
|
||||
|
||||
export default fileItemMediaStyle;
|
55
app/src/styles/filePage.js
Normal file
55
app/src/styles/filePage.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { StyleSheet, Dimensions } from 'react-native';
|
||||
|
||||
const screenDimension = Dimensions.get('window');
|
||||
const screenWidth = screenDimension.width;
|
||||
|
||||
const filePageStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
pageContainer: {
|
||||
flex: 1
|
||||
},
|
||||
mediaContainer: {
|
||||
backgroundColor: '#000000'
|
||||
},
|
||||
emptyClaimText: {
|
||||
textAlign: 'center',
|
||||
fontSize: 20,
|
||||
marginLeft: 16,
|
||||
marginRight: 16
|
||||
},
|
||||
scrollContainer: {
|
||||
flex: 1
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
marginTop: 20,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
marginBottom: 12
|
||||
},
|
||||
channelName: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
marginBottom: 20,
|
||||
color: '#9b9b9b'
|
||||
},
|
||||
description: {
|
||||
fontSize: 16,
|
||||
marginLeft: 20,
|
||||
marginRight: 20,
|
||||
marginBottom: 20,
|
||||
color: '#999999'
|
||||
},
|
||||
thumbnail: {
|
||||
width: screenWidth,
|
||||
height: 200
|
||||
}
|
||||
});
|
||||
|
||||
export default filePageStyle;
|
34
app/src/styles/splash.js
Normal file
34
app/src/styles/splash.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const splashStyle = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: '#40b89a'
|
||||
},
|
||||
title: {
|
||||
fontSize: 64,
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
marginBottom: 48,
|
||||
color: '#ffffff'
|
||||
},
|
||||
details: {
|
||||
fontSize: 14,
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
color: '#ffffff',
|
||||
textAlign: 'center'
|
||||
},
|
||||
message: {
|
||||
fontWeight: 'bold',
|
||||
fontSize: 18,
|
||||
color: '#ffffff',
|
||||
marginLeft: 16,
|
||||
marginRight: 16,
|
||||
marginBottom: 4,
|
||||
textAlign: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
export default splashStyle;
|
15
app/src/utils/redux.js
Normal file
15
app/src/utils/redux.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import {
|
||||
createReactNavigationReduxMiddleware,
|
||||
createReduxBoundAddListener,
|
||||
} from 'react-navigation-redux-helpers';
|
||||
|
||||
const reactNavigationMiddleware = createReactNavigationReduxMiddleware(
|
||||
"root",
|
||||
state => state.nav,
|
||||
);
|
||||
const addListener = createReduxBoundAddListener("root");
|
||||
|
||||
export {
|
||||
reactNavigationMiddleware,
|
||||
addListener,
|
||||
};
|
2
build.sh
2
build.sh
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
cd app
|
||||
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
react-native bundle --platform android --dev false --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
cd ..
|
||||
buildozer android debug
|
||||
|
|
6
deploy.sh
Executable file
6
deploy.sh
Executable file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
cd app
|
||||
react-native bundle --platform android --dev true --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
cd ..
|
||||
buildozer android debug deploy
|
||||
|
|
@ -67,6 +67,8 @@
|
|||
android:screenOrientation="{{ args.orientation }}"
|
||||
-->
|
||||
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
<activity android:name="io.lbry.lbrynet.MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
|
|
BIN
src/main/assets/fonts/Feather.ttf
Executable file
BIN
src/main/assets/fonts/Feather.ttf
Executable file
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
fzv‚όκ9©φ¥yFΓ“Ο}‘ά³θ
|
||||
JR['jÎÉŕ™©˛uaZ
|
|
@ -1,7 +1,13 @@
|
|||
package io.lbry.lbrynet;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
|
@ -10,13 +16,35 @@ import com.facebook.react.ReactInstanceManager;
|
|||
import com.facebook.react.shell.MainReactPackage;
|
||||
|
||||
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||
private static final int OVERLAY_PERMISSION_REQ_CODE = 101;
|
||||
|
||||
private ReactRootView mReactRootView;
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
/**
|
||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||
* onResume method.
|
||||
*/
|
||||
private boolean serviceRunning;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
|
@ -30,6 +58,17 @@ 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) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
// SYSTEM_ALERT_WINDOW permission not granted...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
|
@ -49,6 +88,11 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostResume(this, this);
|
||||
}
|
||||
|
@ -71,5 +115,15 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
|
|||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(serviceInfo.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 134 B |
Binary file not shown.
After Width: | Height: | Size: 100 B |
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 134 B |
Binary file not shown.
After Width: | Height: | Size: 167 B |
Binary file not shown.
After Width: | Height: | Size: 207 B |
Loading…
Reference in a new issue