这个有一个或两个技巧,但这里有一个使用 Expo 的解决方案,适用于 iOS 和 Android。
在新建的Expo项目中,修改以下两个文件:
import React, { Component } from 'react';
import { View, ScrollView, StyleSheet, Button, Alert, Platform, Text, TouchableWithoutFeedback } from 'react-native';
import { FileSystem, Constants, Notifications, Permissions } from 'expo';
import Toast, {DURATION} from 'react-native-easy-toast';
async function getiOSNotificationPermission() {
const { status } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
if (status !== 'granted') {
await Permissions.askAsync(Permissions.NOTIFICATIONS);
}
}
export default class App extends Component {
constructor(props) {
super(props);
// this.toast = null;
this.listenForNotifications = this.listenForNotifications.bind(this);
// this.openFile = this.openFile.bind(this);
this.state = {
filePreviewText: ''
}
}
_handleButtonPress = () => {
let fileName = 'document.txt';
let fileUri = FileSystem.documentDirectory + fileName;
FileSystem.downloadAsync(
"https://raw.githubusercontent.com/expo/expo/master/README.md",
fileUri
).then(({ uri }) => {
console.log('Finished downloading to ', uri);
const localnotification = {
title: 'Download has finished',
body: fileName + " has been downloaded. Tap to open file.",
android: {
sound: true,
},
ios: {
sound: true,
},
data: {
fileUri: uri
},
};
localnotification.data.title = localnotification.title;
localnotification.data.body = localnotification.body;
let sendAfterFiveSeconds = Date.now();
sendAfterFiveSeconds += 3000;
const schedulingOptions = { time: sendAfterFiveSeconds };
Notifications.scheduleLocalNotificationAsync(
localnotification,
schedulingOptions
);
})
.catch(error => {
console.error(error);
Alert.alert(error);
});
};
listenForNotifications = () => {
const _this = this;
Notifications.addListener(notification => {
if (notification.origin === 'received') {
// We could also make our own design for the toast
// _this.refs.toast.show(<View><Text>hello world!</Text></View>);
const toastDOM =
<TouchableWithoutFeedback
onPress={() => {this.openFile(notification.data.fileUri)}}
style={{padding: '10', backgroundColor: 'green'}}>
<Text style={styles.toastText}>{notification.data.body}</Text>
</TouchableWithoutFeedback>;
_this.toast.show(toastDOM, DURATION.FOREVER);
} else if (notification.origin === 'selected') {
this.openFile(notification.data.fileUri);
}
// Expo.Notifications.setBadgeNumberAsync(number);
// Notifications.setBadgeNumberAsync(10);
// Notifications.presentLocalNotificationAsync(notification);
// Alert.alert(notification.title, notification.body);
});
};
componentWillMount() {
getiOSNotificationPermission();
this.listenForNotifications();
}
componentDidMount() {
// let asset = Asset.fromModule(md);
// Toast.show('Hello World');
}
openFile = (fileUri) => {
this.toast.close(40);
console.log('Opening file ' + fileUri);
FileSystem.readAsStringAsync(fileUri)
.then((fileContents) => {
// Get file contents in binary and convert to text
// let fileTextContent = parseInt(fileContents, 2);
this.setState({filePreviewText: fileContents});
});
}
render() {
return (
<View style={styles.container}>
<View style={styles.buttonsContainer}>
<Button style={styles.button}
title={"Download text file"}
onPress={this._handleButtonPress}
/>
<Button style={styles.button}
title={"Clear File Preview"}
onPress={() => {this.setState({filePreviewText: ""})}}
/>
</View>
<ScrollView style={styles.filePreview}>
<Text>{this.state.filePreviewText}</Text>
</ScrollView>
<Toast ref={ (ref) => this.toast=ref }/>
</View>
);
// <Toast
// ref={ (ref) => this.toast=ref }
// style={{backgroundColor:'green'}}
// textStyle={{color:'white'}}
// position={'bottom'}
// positionValue={100}
// opacity={0.8}
// />
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
buttonsContainer: {
flexDirection: 'row',
},
button: {
flex: 1
},
filePreview: {
flex: 1,
padding: 10,
},
toastText: {
color: 'white',
padding: 5,
justifyContent: 'flex-start',
},
});
- package.json:添加以下依赖项(fork of反应本机轻松烤面包 https://github.com/crazycodeboy/react-native-easy-toast/)
"react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git"
关于此解决方案有几个重要注意事项:
使用Expo API最多,用于外部本地通知和文件写入/读取,这限制了当前的解决方案到无法 https://docs.expo.io/versions/v32.0.0/sdk/filesystem/写入除 Expo 自己的目录之外的其他位置。
下载文件后,如果应用程序处于活动状态,则会向用户显示可自定义的 toast(Expo 目前不支持前台通知),或者发送本地推送通知以让用户知道下载已完成。单击这两个中的任何一个将在视图中显示文件的内容,使用<Text>
成分。
The crazycodeboy/react-native-easy-toast
由于 toast 的限制,当前忽略触摸事件,因此尚未直接使用 repo。分叉的存储库使此功能在原始存储库中实现合并请求之前可用。我建议在修补后切换回原来的版本,因为我可能不会维护我的版本。
虽然这个项目也可用 https://snack.expo.io/@siavas/stackoverflow-47979921在 Snack 中,由于需要,它不会运行使用 git 存储库 https://expo.canny.io/feature-requests/p/load-snack-dependencies-from-github in package.json
如上所述,以及变量范围的其他明显不一致。这可以通过合并请求或 Snack 中的新功能来解决。
可能支持其他文件类型由世博会本身 https://docs.expo.io/versions/v32.0.0/sdk/filesystem/或通过外部包,例如这个 PDF 查看器 https://github.com/xcarpentier/rn-pdf-reader-js。但是,该代码必须进一步调整。
Toast(内部通知)是用TouchableWithoutFeedback https://facebook.github.io/react-native/docs/touchablewithoutfeedback组件,虽然有其他类似的 https://facebook.github.io/react-native/docs/handling-touches在 React Native 中存在各种差异。该组件可以在代码中自定义(搜索toastDOM
), but 甚至可能是可更换的 https://expo.canny.io/feature-requests/p/implement-foreground-notifications-on-ios-using-new-native-willpresentnotificati未来可通过世博会的内部通知进行。
最后,文件下载后,有意将通知延迟三秒——这使我们能够在应用程序处于后台时测试通知。请随意消除延迟并立即触发通知 https://docs.expo.io/versions/latest/sdk/notifications/#exponotificationspresentlocalnotificationasynclocalnotification.
就是这样!我认为这为使用 Expo 下载和预览文件提供了一个良好的起点。
代码库也可以在 GitHub 上找到 https://github.com/SiavasFiroozbakht/StackOverflow-47979921.