React Native splash and discover interface with daemon service initialisation

This commit is contained in:
Akinwale Ariwodola 2018-03-11 10:39:29 +01:00
parent 4967c8ec94
commit c5f68f5b29
37 changed files with 81924 additions and 778 deletions

View file

@ -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/

View file

@ -1,25 +0,0 @@
import React from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
class InfoComponent extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.title}>LBRY UI</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
title: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
AppRegistry.registerComponent('LBRYApp', () => InfoComponent);

6311
app/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,15 @@
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"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-thunk": "^2.2.0"
}
}

View file

@ -0,0 +1,65 @@
import React from 'react';
import DiscoverPage from '../page/discover';
import SplashScreen from '../page/splash';
import { addNavigationHelpers, DrawerNavigator, StackNavigator } from 'react-navigation';
import { connect } from 'react-redux';
import { addListener } from '../utils/redux';
import { StyleSheet } from 'react-native';
import Feather from 'react-native-vector-icons/Feather';
const styles = StyleSheet.create({
drawerHamburger: {
marginLeft: 8
}
});
const discoverStack = StackNavigator({
Discover: {
screen: DiscoverPage,
}
}, {
headerMode: 'screen'
});
const drawer = DrawerNavigator({
Discover: { screen: DiscoverPage },
}, {
drawerWidth: 300,
headerMode: 'screen'
});
export const AppNavigator = new StackNavigator({
Splash: {
screen: SplashScreen,
navigationOptions: { header: null }
},
Main: {
screen: drawer
}
}, {
headerMode: 'screen',
navigationOptions: ({ navigation }) => ({
headerLeft: () => <Feather name="menu" size={24} style={styles.drawerHamburger} onPress={() => navigation.navigate('DrawerOpen')} />
})
});
class AppWithNavigationState extends React.Component {
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);

View 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);

View file

@ -0,0 +1,23 @@
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 } = this.props;
return (
<View>
<Text style={discoverStyle.categoryName}>{category}</Text>
{names &&
names.map(name => (
<FileItem style={discoverStyle.fileItem} key={name} uri={normalizeURI(name)} />
))}
</View>
);
}
}
export default FeaturedCategory;

View 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);

View file

@ -0,0 +1,67 @@
import React from 'react';
import { normalizeURI } from 'lbry-redux';
import { Text, View } 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 (
<View style={style}>
<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>}
</View>
);
}
}
export default FileItem;

View 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);

View file

@ -0,0 +1,50 @@
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() {
const { title, thumbnail } = this.props;
const atStyle = this.state.autoThumbStyle;
if (thumbnail && ((typeof thumbnail) === 'string')) {
return (
<Image source={{uri: thumbnail }} resizeMode="cover" style={fileItemMediaStyle.thumbnail} />
)
}
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;

View 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);

View 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;

98
app/src/index.js Normal file
View file

@ -0,0 +1,98 @@
import React from 'react';
import { Provider, connect } from 'react-redux';
import DiscoverPage from './page/discover';
import { AppRegistry, StyleSheet, Text, View } 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 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({
log: app.env === 'development',
}),*/
applyMiddleware(...middleware)
)
);
class LBRYApp extends React.Component {
render() {
return (
<Provider store={store}>
<AppWithNavigationState />
</Provider>
);
}
}
AppRegistry.registerComponent('LBRYApp', () => LBRYApp);
export default LBRYApp;

View 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);

View file

@ -0,0 +1,45 @@
import React from 'react';
import FeaturedCategory from '../../component/featuredCategory';
import { Text, View, ScrollView } from 'react-native';
import discoverStyle from '../../styles/discover';
class DiscoverPage extends React.PureComponent {
static navigationOptions = {
title: 'Discover'
};
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]}
/>
) : (
''
)
)}
</ScrollView>
}
</View>
);
}
}
export default DiscoverPage;

View file

View file

View 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);

View 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;

View file

@ -0,0 +1,53 @@
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: '#888888'
},
filePriceContainer: {
backgroundColor: '#60f9d6',
justifyContent: 'center',
position: 'absolute',
right: 16,
top: 16,
width: 64,
height: 24
},
filePriceText: {
fontSize: 11,
textAlign: 'center'
}
});
export default discoverStyle;

View file

@ -0,0 +1,57 @@
import { StyleSheet, Dimensions } from 'react-native';
const windowDimension = Dimensions.get('window');
const width = windowDimension.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;

34
app/src/styles/splash.js Normal file
View 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
View 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,
};

View file

@ -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
View 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

View file

@ -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

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
fzvόκ9©φ¥yFΓ“Ο}‘ά³θ
J@В░╠5╩⌠╔√╬ы3Yи]Vm

View file

@ -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,16 @@ 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