├── .gitattributes ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── RNFileViewer.podspec ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── vinzscam │ │ └── reactnativefileviewer │ │ ├── FileProvider.java │ │ ├── RNFileViewerModule.java │ │ └── RNFileViewerPackage.java │ └── res │ └── xml │ └── file_viewer_provider_paths.xml ├── docs ├── react-native-file-viewer-certificate.pdf └── react-native-file-viewer-usage-example.gif ├── index.d.ts ├── index.js ├── ios ├── RNFileViewer.xcodeproj │ └── project.pbxproj ├── RNFileViewer.xcworkspace │ └── contents.xcworkspacedata ├── RNFileViewerManager.h └── RNFileViewerManager.m ├── package.json └── windows ├── RNFileViewer.sln └── RNFileViewer ├── Properties ├── AssemblyInfo.cs └── RNFileViewer.rd.xml ├── RNFileViewer.csproj ├── RNFileViewerModule.cs └── RNFileViewerPackage.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | 33 | 34 | # Android/IntelliJ 35 | # 36 | build/ 37 | .idea 38 | .gradle 39 | local.properties 40 | *.iml 41 | 42 | # BUCK 43 | buck-out/ 44 | \.buckd/ 45 | *.keystore 46 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vincenzo Scamporlino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-file-viewer 2 | 3 | Native file viewer for react-native. Preview any type of file supported by the mobile device. 4 | 5 | **iOS**: it uses [QuickLook Framework](https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/DocumentInteraction_TopicsForIOS/Articles/UsingtheQuickLookFramework.html) 6 | 7 | **Android**: it uses `ACTION_VIEW` Intent to start the default app associated with the specified file. 8 | 9 | **Windows**: Start the default app associated with the specified file. 10 | 11 | ![Usage example](docs/react-native-file-viewer-usage-example.gif) 12 | 13 | ## Getting started 14 | 15 | `$ npm install react-native-file-viewer --save` 16 | 17 | or 18 | 19 | `$ yarn add react-native-file-viewer` 20 | 21 | ### Mostly automatic installation (recommended) 22 | 23 | ``` 24 | # RN >= 0.60 25 | cd ios && pod install 26 | 27 | # RN < 0.60 28 | react-native link react-native-file-viewer 29 | ``` 30 | 31 | #### Extra step (Android only) 32 | 33 | If your app is targeting **Android 11 (API level 30) or newer**, the following extra step is required, as described in [Declaring package visibility needs](https://developer.android.com/training/package-visibility/declaring) and [Package visibility in Android 11](https://medium.com/androiddevelopers/package-visibility-in-android-11-cc857f221cd9). 34 | 35 | Specifically: 36 | 37 | > If your app targets Android 11 or higher and needs to interact with apps other than the ones that are visible automatically, add the element in your app's manifest file. Within the element, specify the other apps by package name, by intent signature, or by provider authority, as described in the following sections. 38 | 39 | For example, if you know upfront that your app is supposed to open PDF files, the following lines should be added to your `AndroidManifest.xml`. 40 | 41 | ```diff 42 | ... 43 | 44 | + 45 | + 46 | + 47 | + 48 | + 49 | + 50 | + 51 | 52 | ``` 53 | 54 | **IMPORTANT**: Try to be as granular as possible when defining your own queries. This might affect your Play Store approval, as mentioned in [Package visibility filtering on Android](https://developer.android.com/training/package-visibility). 55 | 56 | > If you publish your app on Google Play, your app's use of this permission is subject to approval based on an upcoming policy. 57 | 58 | ### Expo 59 | 60 | If your project is based on [Expo](https://expo.io), you need to eject your project by switching to the [Bare workflow](https://docs.expo.dev/workflow/customizing), in order to use this library. 61 | 62 | ### Manual installation 63 | 64 | #### iOS (CocoaPods) 65 | 66 | Add the following to your Podfile: 67 | 68 | ``` 69 | pod 'RNFileViewer', :path => '../node_modules/react-native-file-viewer' 70 | ``` 71 | 72 | #### iOS 73 | 74 | 1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 75 | 2. Go to `node_modules` ➜ `react-native-file-viewer` and add `RNFileViewer.xcodeproj` 76 | 3. In XCode, in the project navigator, select your project. Add `libRNFileViewer.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` 77 | 4. Run your project (`Cmd+R`) 78 | 79 | #### Android 80 | 81 | 1. Open up `android/app/src/main/java/[...]/MainApplication.java` 82 | 83 | - Add `import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;` to the imports at the top of the file 84 | - Add `new RNFileViewerPackage()` to the list returned by the `getPackages()` method 85 | 86 | 2. Append the following lines to `android/settings.gradle`: 87 | 88 | ``` 89 | include ':react-native-file-viewer' 90 | project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android') 91 | ``` 92 | 93 | 3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: 94 | 95 | ``` 96 | compile project(':react-native-file-viewer') 97 | ``` 98 | 99 | 4. Locate `react-native-file-viewer` inside `node_modules` folder and copy `android/src/main/res/xml/file_viewer_provider_paths.xml` to your project `res/xml/` directory 100 | 5. Add the following lines to `AndroidManifest.xml` between the main `` tag: 101 | 102 | ``` 103 | ... 104 | 105 | ... 106 | 111 | 115 | 116 | 117 | .... 118 | ``` 119 | 120 | #### Windows 121 | 122 | Follow the instructions in the ['Linking Libraries'](https://github.com/Microsoft/react-native-windows/blob/master/docs/LinkingLibrariesWindows.md) documentation on the react-native-windows GitHub repo. For the first step of adding the project to the Visual Studio solution file, the path to the project should be `../node_modules/react-native-file-viewer/windows/RNFileViewer/RNFileViewer.csproj`. 123 | 124 | ## Usage 125 | 126 | ### Open a local file 127 | 128 | ```javascript 129 | import FileViewer from "react-native-file-viewer"; 130 | 131 | const path = FileViewer.open(path) // absolute-path-to-my-local-file. 132 | .then(() => { 133 | // success 134 | }) 135 | .catch((error) => { 136 | // error 137 | }); 138 | ``` 139 | 140 | ### Pick up and open a local file #1 (using [react-native-document-picker](https://github.com/Elyx0/react-native-document-picker)) 141 | 142 | ```javascript 143 | import FileViewer from "react-native-file-viewer"; 144 | import DocumentPicker from "react-native-document-picker"; 145 | 146 | try { 147 | const res = await DocumentPicker.pick({ 148 | type: [DocumentPicker.types.allFiles], 149 | }); 150 | await FileViewer.open(res.uri); 151 | } catch (e) { 152 | // error 153 | } 154 | ``` 155 | 156 | ### Pick up and open a local file #2 (using [react-native-image-crop-picker](https://github.com/ivpusic/react-native-image-crop-picker)) 157 | 158 | ```javascript 159 | import FileViewer from "react-native-file-viewer"; 160 | import ImagePicker from "react-native-image-crop-picker"; 161 | 162 | ImagePicker.openPicker({}) 163 | .then((image) => FileViewer.open(image.path)) 164 | .catch((error) => { 165 | // error 166 | }); 167 | ``` 168 | 169 | ### Prompt the user to choose an app to open the file with (if there are multiple installed apps that support the mimetype) 170 | 171 | ```javascript 172 | import FileViewer from "react-native-file-viewer"; 173 | 174 | const path = FileViewer.open(path, { showOpenWithDialog: true }) // absolute-path-to-my-local-file. 175 | .then(() => { 176 | // success 177 | }) 178 | .catch((error) => { 179 | // error 180 | }); 181 | ``` 182 | 183 | ### Open a file from Android assets folder 184 | 185 | Since the library works only with absolute paths and Android assets folder doesn't have any absolute path, the file needs to be copied first. Use [copyFileAssets](https://github.com/itinance/react-native-fs#copyfileassetsfilepath-string-destpath-string-promisevoid) of [react-native-fs](https://github.com/itinance/react-native-fs). 186 | 187 | Example (using react-native-fs): 188 | 189 | ```javascript 190 | import FileViewer from "react-native-file-viewer"; 191 | import RNFS from "react-native-fs"; 192 | 193 | const file = "file-to-open.doc"; // this is your file name 194 | 195 | // feel free to change main path according to your requirements 196 | const dest = `${RNFS.DocumentDirectoryPath}/${file}`; 197 | 198 | RNFS.copyFileAssets(file, dest) 199 | .then(() => FileViewer.open(dest)) 200 | .then(() => { 201 | // success 202 | }) 203 | .catch((error) => { 204 | /* */ 205 | }); 206 | ``` 207 | 208 | ### Download and open a file (using [react-native-fs](https://github.com/itinance/react-native-fs)) 209 | 210 | No function about file downloading has been implemented in this package. 211 | Use [react-native-fs](https://github.com/itinance/react-native-fs) or any similar library for this purpose. 212 | 213 | Example (using react-native-fs): 214 | 215 | ```javascript 216 | import RNFS from "react-native-fs"; 217 | import FileViewer from "react-native-file-viewer"; 218 | import { Platform } from "react-native"; 219 | 220 | const url = 221 | "https://github.com/vinzscam/react-native-file-viewer/raw/master/docs/react-native-file-viewer-certificate.pdf"; 222 | 223 | // *IMPORTANT*: The correct file extension is always required. 224 | // You might encounter issues if the file's extension isn't included 225 | // or if it doesn't match the mime type of the file. 226 | // https://stackoverflow.com/a/47767860 227 | function getUrlExtension(url) { 228 | return url.split(/[#?]/)[0].split(".").pop().trim(); 229 | } 230 | 231 | const extension = getUrlExtension(url); 232 | 233 | // Feel free to change main path according to your requirements. 234 | const localFile = `${RNFS.DocumentDirectoryPath}/temporaryfile.${extension}`; 235 | 236 | const options = { 237 | fromUrl: url, 238 | toFile: localFile, 239 | }; 240 | RNFS.downloadFile(options) 241 | .promise.then(() => FileViewer.open(localFile)) 242 | .then(() => { 243 | // success 244 | }) 245 | .catch((error) => { 246 | // error 247 | }); 248 | ``` 249 | 250 | ## API 251 | 252 | ### `open(filepath: string, options?: Object): Promise` 253 | 254 | | Parameter | Type | Description | 255 | | ---------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 256 | | **filepath** | string | The absolute path where the file is stored. The file needs to have a valid extension to be successfully detected. Use [react-native-fs constants](https://github.com/itinance/react-native-fs#constants) to determine the absolute path correctly. | 257 | | **options** (optional) | Object | Some options to customize the behaviour. See below. | 258 | 259 | #### Options 260 | 261 | | Parameter | Type | Description | 262 | | ---------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | 263 | | **displayName** (optional) | string | Customize the QuickLook title (iOS only). | 264 | | **onDismiss** (optional) | function | Callback invoked when the viewer is being dismissed (iOS and Android only). | 265 | | **showOpenWithDialog** (optional) | boolean | If there is more than one app that can open the file, show an _Open With_ dialogue box (Android only). | 266 | | **showAppsSuggestions** (optional) | boolean | If there is not an installed app that can open the file, open the Play Store with suggested apps (Android only). | 267 | 268 | ## Issues 269 | 270 | ### Android X Breaking changes 271 | 272 | The library supports [Android X](https://developer.android.com/jetpack/androidx/) and React Native 0.60+. 273 | 274 | If you're using **React Native < 0.60**, please append the following snippet to your `android/app/build.gradle` file: 275 | 276 | ``` 277 | preBuild.doFirst { 278 | ant.replaceregexp(match:'androidx.core.content.', replace:'android.support.v4.content.', flags:'g', byline:true) { 279 | fileset(dir: '../../node_modules/react-native-file-viewer/android/src/main/java/com/vinzscam/reactnativefileviewer', includes: '*.java') 280 | } 281 | } 282 | ``` 283 | 284 | If you prefer to not touch your gradle file, you can still use version `1.0.15` which is perfectly compatible. 285 | -------------------------------------------------------------------------------- /RNFileViewer.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 3 | 4 | Pod::Spec.new do |s| 5 | s.name = "RNFileViewer" 6 | s.version = package['version'] 7 | s.summary = package["description"] 8 | s.homepage = "https://github.com/vinzscam/react-native-file-viewer" 9 | s.license = package["license"] 10 | # s.license = { :type => "MIT", :file => "FILE_LICENSE" } 11 | s.author = { "Vincenzo Scamporlino" => "vinz.scamporlino@gmail.com" } 12 | s.platform = :ios, "9.0" 13 | s.source = { :git => "https://github.com/vinzscam/react-native-file-viewer.git", :tag => 'v#{s.version}' } 14 | s.source_files = "ios/*.{h,m}" 15 | s.requires_arc = true 16 | s.dependency "React-Core" 17 | end 18 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 9 | buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') 10 | 11 | defaultConfig { 12 | minSdkVersion safeExtGet('minSdkVersion', 16) 13 | targetSdkVersion safeExtGet('targetSdkVersion', 28) 14 | versionCode 1 15 | versionName "1.0" 16 | } 17 | lintOptions { 18 | abortOnError false 19 | } 20 | } 21 | 22 | repositories { 23 | mavenCentral() 24 | } 25 | 26 | dependencies { 27 | implementation 'com.facebook.react:react-native:+' 28 | } 29 | 30 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/src/main/java/com/vinzscam/reactnativefileviewer/FileProvider.java: -------------------------------------------------------------------------------- 1 | package com.vinzscam.reactnativefileviewer; 2 | 3 | public class FileProvider extends androidx.core.content.FileProvider { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /android/src/main/java/com/vinzscam/reactnativefileviewer/RNFileViewerModule.java: -------------------------------------------------------------------------------- 1 | 2 | package com.vinzscam.reactnativefileviewer; 3 | 4 | import android.app.Activity; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import androidx.core.content.FileProvider; 9 | import android.webkit.MimeTypeMap; 10 | 11 | import com.facebook.react.bridge.ActivityEventListener; 12 | import com.facebook.react.bridge.Arguments; 13 | import com.facebook.react.bridge.BaseActivityEventListener; 14 | import com.facebook.react.bridge.ReactApplicationContext; 15 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 16 | import com.facebook.react.bridge.ReactMethod; 17 | import com.facebook.react.bridge.ReadableMap; 18 | import com.facebook.react.bridge.WritableMap; 19 | 20 | import com.facebook.react.modules.core.DeviceEventManagerModule; 21 | import java.io.File; 22 | 23 | public class RNFileViewerModule extends ReactContextBaseJavaModule { 24 | private final ReactApplicationContext reactContext; 25 | private static final String SHOW_OPEN_WITH_DIALOG = "showOpenWithDialog" ; 26 | private static final String SHOW_STORE_SUGGESTIONS ="showAppsSuggestions"; 27 | private static final String OPEN_EVENT = "RNFileViewerDidOpen"; 28 | private static final String DISMISS_EVENT = "RNFileViewerDidDismiss"; 29 | private static final Integer RN_FILE_VIEWER_REQUEST = 33341; 30 | 31 | private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() { 32 | @Override 33 | public void onActivityResult(final Activity activity, final int requestCode, final int resultCode, final Intent intent) { 34 | sendEvent(DISMISS_EVENT, requestCode - RN_FILE_VIEWER_REQUEST, null); 35 | } 36 | }; 37 | 38 | public RNFileViewerModule(ReactApplicationContext reactContext) { 39 | super(reactContext); 40 | this.reactContext = reactContext; 41 | reactContext.addActivityEventListener(mActivityEventListener); 42 | } 43 | 44 | @ReactMethod 45 | public void open(String path, Integer currentId, ReadableMap options) { 46 | Uri contentUri = null; 47 | Boolean showOpenWithDialog = options.hasKey(SHOW_OPEN_WITH_DIALOG) ? options.getBoolean(SHOW_OPEN_WITH_DIALOG) : false; 48 | Boolean showStoreSuggestions = options.hasKey(SHOW_STORE_SUGGESTIONS) ? options.getBoolean(SHOW_STORE_SUGGESTIONS) : false; 49 | 50 | if(path.startsWith("content://")) { 51 | contentUri = Uri.parse(path); 52 | } else { 53 | File newFile = new File(path); 54 | 55 | Activity currentActivity = getCurrentActivity(); 56 | if(currentActivity == null) { 57 | sendEvent(OPEN_EVENT, currentId, "Activity doesn't exist"); 58 | return; 59 | } 60 | try { 61 | final String packageName = currentActivity.getPackageName(); 62 | final String authority = new StringBuilder(packageName).append(".provider").toString(); 63 | contentUri = FileProvider.getUriForFile(currentActivity, authority, newFile); 64 | } 65 | catch(IllegalArgumentException e) { 66 | sendEvent(OPEN_EVENT, currentId, e.getMessage()); 67 | return; 68 | } 69 | } 70 | 71 | if(contentUri == null) { 72 | sendEvent(OPEN_EVENT, currentId, "Invalid file"); 73 | return; 74 | } 75 | 76 | String extension = MimeTypeMap.getFileExtensionFromUrl(path).toLowerCase(); 77 | String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 78 | 79 | Intent shareIntent = new Intent(); 80 | 81 | shareIntent.setAction(Intent.ACTION_VIEW); 82 | shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 83 | shareIntent.setDataAndType(contentUri, mimeType); 84 | shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); 85 | Intent intentActivity; 86 | 87 | if (showOpenWithDialog) { 88 | intentActivity = Intent.createChooser(shareIntent, "Open with"); 89 | } else { 90 | intentActivity = shareIntent; 91 | } 92 | 93 | PackageManager pm = getCurrentActivity().getPackageManager(); 94 | 95 | if (shareIntent.resolveActivity(pm) != null) { 96 | try { 97 | getCurrentActivity().startActivityForResult(intentActivity, currentId + RN_FILE_VIEWER_REQUEST); 98 | sendEvent(OPEN_EVENT, currentId, null); 99 | } 100 | catch(Exception e) { 101 | sendEvent(OPEN_EVENT, currentId, e.getMessage()); 102 | } 103 | } else { 104 | try { 105 | if (showStoreSuggestions) { 106 | if(mimeType == null) { 107 | throw new Exception("It wasn't possible to detect the type of the file"); 108 | } 109 | Intent storeIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=" + mimeType + "&c=apps")); 110 | getCurrentActivity().startActivity(storeIntent); 111 | } 112 | throw new Exception("No app associated with this mime type"); 113 | } 114 | catch(Exception e) { 115 | sendEvent(OPEN_EVENT, currentId, e.getMessage()); 116 | } 117 | } 118 | } 119 | 120 | @Override 121 | public String getName() { 122 | return "RNFileViewer"; 123 | } 124 | 125 | private void sendEvent(String eventName, Integer currentId, String errorMessage) { 126 | WritableMap params = Arguments.createMap(); 127 | params.putInt("id", currentId); 128 | if(errorMessage != null) { 129 | params.putString("error", errorMessage); 130 | } 131 | reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 132 | .emit(eventName, params); 133 | } 134 | 135 | @ReactMethod 136 | public void addListener(String eventName) { } 137 | 138 | @ReactMethod 139 | public void removeListeners(Integer count) { } 140 | } 141 | -------------------------------------------------------------------------------- /android/src/main/java/com/vinzscam/reactnativefileviewer/RNFileViewerPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package com.vinzscam.reactnativefileviewer; 3 | 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.List; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | import com.facebook.react.bridge.JavaScriptModule; 13 | public class RNFileViewerPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList(new RNFileViewerModule(reactContext)); 17 | } 18 | 19 | // Deprecated from RN 0.47 20 | public List> createJSModules() { 21 | return Collections.emptyList(); 22 | } 23 | 24 | @Override 25 | public List createViewManagers(ReactApplicationContext reactContext) { 26 | return Collections.emptyList(); 27 | } 28 | } -------------------------------------------------------------------------------- /android/src/main/res/xml/file_viewer_provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/react-native-file-viewer-certificate.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinzscam/react-native-file-viewer/118d91a4006df4fd542adb6bbab581a2cd46c210/docs/react-native-file-viewer-certificate.pdf -------------------------------------------------------------------------------- /docs/react-native-file-viewer-usage-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vinzscam/react-native-file-viewer/118d91a4006df4fd542adb6bbab581a2cd46c210/docs/react-native-file-viewer-usage-example.gif -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | interface RNFileViewerOptions { 2 | displayName?: string; 3 | showAppsSuggestions?: boolean; 4 | showOpenWithDialog?: boolean; 5 | onDismiss?(): any; 6 | } 7 | 8 | declare function open( 9 | path: string, 10 | options?: RNFileViewerOptions | string 11 | ): Promise; 12 | 13 | declare namespace _default { 14 | export { open }; 15 | } 16 | 17 | export default _default; 18 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModules, Platform } from "react-native"; 2 | 3 | const { RNFileViewer } = NativeModules; 4 | const eventEmitter = new NativeEventEmitter(RNFileViewer); 5 | 6 | let lastId = 0; 7 | 8 | function open(path, options = {}) { 9 | const _options = 10 | typeof options === "string" ? { displayName: options } : options; 11 | const { onDismiss, ...nativeOptions } = _options; 12 | 13 | if (!["android", "ios"].includes(Platform.OS)) { 14 | return RNFileViewer.open(path, nativeOptions); 15 | } 16 | 17 | return new Promise((resolve, reject) => { 18 | const currentId = ++lastId; 19 | 20 | const openSubscription = eventEmitter.addListener( 21 | "RNFileViewerDidOpen", 22 | ({ id, error }) => { 23 | if (id === currentId) { 24 | openSubscription.remove(); 25 | return error ? reject(new Error(error)) : resolve(); 26 | } 27 | } 28 | ); 29 | const dismissSubscription = eventEmitter.addListener( 30 | "RNFileViewerDidDismiss", 31 | ({ id }) => { 32 | if (id === currentId) { 33 | dismissSubscription.remove(); 34 | onDismiss && onDismiss(); 35 | } 36 | } 37 | ); 38 | 39 | RNFileViewer.open(normalize(path), currentId, nativeOptions); 40 | }); 41 | } 42 | 43 | function normalize(path) { 44 | if (Platform.OS === "ios" || Platform.OS === "android") { 45 | const filePrefix = "file://"; 46 | if (path.startsWith(filePrefix)) { 47 | path = path.substring(filePrefix.length); 48 | try { 49 | path = decodeURI(path); 50 | } catch (e) {} 51 | } 52 | } 53 | return path; 54 | } 55 | 56 | export default { open }; 57 | export { open }; 58 | -------------------------------------------------------------------------------- /ios/RNFileViewer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | ACC1C9CC1F7D32530070ADA6 /* QuickLook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACC1C9CB1F7D32530070ADA6 /* QuickLook.framework */; }; 11 | B3E7B58A1CC2AC0600A0062D /* RNFileViewerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNFileViewerManager.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 58B511D91A9E6C8500147676 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 134814201AA4EA6300B7C361 /* libRNFileViewer.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFileViewer.a; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | ACC1C9CB1F7D32530070ADA6 /* QuickLook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLook.framework; path = System/Library/Frameworks/QuickLook.framework; sourceTree = SDKROOT; }; 29 | B3E7B5881CC2AC0600A0062D /* RNFileViewerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFileViewerManager.h; sourceTree = ""; }; 30 | B3E7B5891CC2AC0600A0062D /* RNFileViewerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFileViewerManager.m; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 58B511D81A9E6C8500147676 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ACC1C9CC1F7D32530070ADA6 /* QuickLook.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 134814211AA4EA7D00B7C361 /* Products */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | 134814201AA4EA6300B7C361 /* libRNFileViewer.a */, 49 | ); 50 | name = Products; 51 | sourceTree = ""; 52 | }; 53 | 58B511D21A9E6C8500147676 = { 54 | isa = PBXGroup; 55 | children = ( 56 | B3E7B5881CC2AC0600A0062D /* RNFileViewerManager.h */, 57 | B3E7B5891CC2AC0600A0062D /* RNFileViewerManager.m */, 58 | 134814211AA4EA7D00B7C361 /* Products */, 59 | ACC1C9CA1F7D32530070ADA6 /* Frameworks */, 60 | ); 61 | sourceTree = ""; 62 | }; 63 | ACC1C9CA1F7D32530070ADA6 /* Frameworks */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | ACC1C9CB1F7D32530070ADA6 /* QuickLook.framework */, 67 | ); 68 | name = Frameworks; 69 | sourceTree = ""; 70 | }; 71 | /* End PBXGroup section */ 72 | 73 | /* Begin PBXNativeTarget section */ 74 | 58B511DA1A9E6C8500147676 /* RNFileViewer */ = { 75 | isa = PBXNativeTarget; 76 | buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNFileViewer" */; 77 | buildPhases = ( 78 | 58B511D71A9E6C8500147676 /* Sources */, 79 | 58B511D81A9E6C8500147676 /* Frameworks */, 80 | 58B511D91A9E6C8500147676 /* CopyFiles */, 81 | ); 82 | buildRules = ( 83 | ); 84 | dependencies = ( 85 | ); 86 | name = RNFileViewer; 87 | productName = RCTDataManager; 88 | productReference = 134814201AA4EA6300B7C361 /* libRNFileViewer.a */; 89 | productType = "com.apple.product-type.library.static"; 90 | }; 91 | /* End PBXNativeTarget section */ 92 | 93 | /* Begin PBXProject section */ 94 | 58B511D31A9E6C8500147676 /* Project object */ = { 95 | isa = PBXProject; 96 | attributes = { 97 | LastUpgradeCheck = 0830; 98 | ORGANIZATIONNAME = Facebook; 99 | TargetAttributes = { 100 | 58B511DA1A9E6C8500147676 = { 101 | CreatedOnToolsVersion = 6.1.1; 102 | }; 103 | }; 104 | }; 105 | buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNFileViewer" */; 106 | compatibilityVersion = "Xcode 3.2"; 107 | developmentRegion = English; 108 | hasScannedForEncodings = 0; 109 | knownRegions = ( 110 | en, 111 | ); 112 | mainGroup = 58B511D21A9E6C8500147676; 113 | productRefGroup = 58B511D21A9E6C8500147676; 114 | projectDirPath = ""; 115 | projectRoot = ""; 116 | targets = ( 117 | 58B511DA1A9E6C8500147676 /* RNFileViewer */, 118 | ); 119 | }; 120 | /* End PBXProject section */ 121 | 122 | /* Begin PBXSourcesBuildPhase section */ 123 | 58B511D71A9E6C8500147676 /* Sources */ = { 124 | isa = PBXSourcesBuildPhase; 125 | buildActionMask = 2147483647; 126 | files = ( 127 | B3E7B58A1CC2AC0600A0062D /* RNFileViewerManager.m in Sources */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | /* End PBXSourcesBuildPhase section */ 132 | 133 | /* Begin XCBuildConfiguration section */ 134 | 58B511ED1A9E6C8500147676 /* Debug */ = { 135 | isa = XCBuildConfiguration; 136 | buildSettings = { 137 | ALWAYS_SEARCH_USER_PATHS = NO; 138 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 139 | CLANG_CXX_LIBRARY = "libc++"; 140 | CLANG_ENABLE_MODULES = YES; 141 | CLANG_ENABLE_OBJC_ARC = YES; 142 | CLANG_WARN_BOOL_CONVERSION = YES; 143 | CLANG_WARN_CONSTANT_CONVERSION = YES; 144 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 145 | CLANG_WARN_EMPTY_BODY = YES; 146 | CLANG_WARN_ENUM_CONVERSION = YES; 147 | CLANG_WARN_INFINITE_RECURSION = YES; 148 | CLANG_WARN_INT_CONVERSION = YES; 149 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 150 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 151 | CLANG_WARN_UNREACHABLE_CODE = YES; 152 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 153 | COPY_PHASE_STRIP = NO; 154 | ENABLE_STRICT_OBJC_MSGSEND = YES; 155 | ENABLE_TESTABILITY = YES; 156 | GCC_C_LANGUAGE_STANDARD = gnu99; 157 | GCC_DYNAMIC_NO_PIC = NO; 158 | GCC_NO_COMMON_BLOCKS = YES; 159 | GCC_OPTIMIZATION_LEVEL = 0; 160 | GCC_PREPROCESSOR_DEFINITIONS = ( 161 | "DEBUG=1", 162 | "$(inherited)", 163 | ); 164 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 165 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 166 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 167 | GCC_WARN_UNDECLARED_SELECTOR = YES; 168 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 169 | GCC_WARN_UNUSED_FUNCTION = YES; 170 | GCC_WARN_UNUSED_VARIABLE = YES; 171 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 172 | MTL_ENABLE_DEBUG_INFO = YES; 173 | ONLY_ACTIVE_ARCH = YES; 174 | SDKROOT = iphoneos; 175 | }; 176 | name = Debug; 177 | }; 178 | 58B511EE1A9E6C8500147676 /* Release */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 183 | CLANG_CXX_LIBRARY = "libc++"; 184 | CLANG_ENABLE_MODULES = YES; 185 | CLANG_ENABLE_OBJC_ARC = YES; 186 | CLANG_WARN_BOOL_CONVERSION = YES; 187 | CLANG_WARN_CONSTANT_CONVERSION = YES; 188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 189 | CLANG_WARN_EMPTY_BODY = YES; 190 | CLANG_WARN_ENUM_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 194 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 195 | CLANG_WARN_UNREACHABLE_CODE = YES; 196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 197 | COPY_PHASE_STRIP = YES; 198 | ENABLE_NS_ASSERTIONS = NO; 199 | ENABLE_STRICT_OBJC_MSGSEND = YES; 200 | GCC_C_LANGUAGE_STANDARD = gnu99; 201 | GCC_NO_COMMON_BLOCKS = YES; 202 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 203 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 204 | GCC_WARN_UNDECLARED_SELECTOR = YES; 205 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 206 | GCC_WARN_UNUSED_FUNCTION = YES; 207 | GCC_WARN_UNUSED_VARIABLE = YES; 208 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 209 | MTL_ENABLE_DEBUG_INFO = NO; 210 | SDKROOT = iphoneos; 211 | VALIDATE_PRODUCT = YES; 212 | }; 213 | name = Release; 214 | }; 215 | 58B511F01A9E6C8500147676 /* Debug */ = { 216 | isa = XCBuildConfiguration; 217 | buildSettings = { 218 | HEADER_SEARCH_PATHS = ( 219 | "$(inherited)", 220 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 221 | "$(SRCROOT)/../../../React/**", 222 | "$(SRCROOT)/../../react-native/React/**", 223 | ); 224 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 225 | OTHER_LDFLAGS = "-ObjC"; 226 | PRODUCT_NAME = RNFileViewer; 227 | SKIP_INSTALL = YES; 228 | }; 229 | name = Debug; 230 | }; 231 | 58B511F11A9E6C8500147676 /* Release */ = { 232 | isa = XCBuildConfiguration; 233 | buildSettings = { 234 | HEADER_SEARCH_PATHS = ( 235 | "$(inherited)", 236 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 237 | "$(SRCROOT)/../../../React/**", 238 | "$(SRCROOT)/../../react-native/React/**", 239 | ); 240 | LIBRARY_SEARCH_PATHS = "$(inherited)"; 241 | OTHER_LDFLAGS = "-ObjC"; 242 | PRODUCT_NAME = RNFileViewer; 243 | SKIP_INSTALL = YES; 244 | }; 245 | name = Release; 246 | }; 247 | /* End XCBuildConfiguration section */ 248 | 249 | /* Begin XCConfigurationList section */ 250 | 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNFileViewer" */ = { 251 | isa = XCConfigurationList; 252 | buildConfigurations = ( 253 | 58B511ED1A9E6C8500147676 /* Debug */, 254 | 58B511EE1A9E6C8500147676 /* Release */, 255 | ); 256 | defaultConfigurationIsVisible = 0; 257 | defaultConfigurationName = Release; 258 | }; 259 | 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNFileViewer" */ = { 260 | isa = XCConfigurationList; 261 | buildConfigurations = ( 262 | 58B511F01A9E6C8500147676 /* Debug */, 263 | 58B511F11A9E6C8500147676 /* Release */, 264 | ); 265 | defaultConfigurationIsVisible = 0; 266 | defaultConfigurationName = Release; 267 | }; 268 | /* End XCConfigurationList section */ 269 | }; 270 | rootObject = 58B511D31A9E6C8500147676 /* Project object */; 271 | } 272 | -------------------------------------------------------------------------------- /ios/RNFileViewer.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/RNFileViewerManager.h: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #import 4 | 5 | @class UIViewController; 6 | 7 | @interface RNFileViewer : RCTEventEmitter 8 | 9 | + (UIViewController*)topViewController; 10 | + (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController; 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /ios/RNFileViewerManager.m: -------------------------------------------------------------------------------- 1 | 2 | #import "RNFileViewerManager.h" 3 | #import 4 | #import 5 | 6 | #define OPEN_EVENT @"RNFileViewerDidOpen" 7 | #define DISMISS_EVENT @"RNFileViewerDidDismiss" 8 | 9 | @interface File: NSObject 10 | 11 | @property(readonly, nullable, nonatomic) NSURL *previewItemURL; 12 | @property(readonly, nullable, nonatomic) NSString *previewItemTitle; 13 | 14 | - (id)initWithPath:(NSString *)file title:(NSString *)title; 15 | 16 | @end 17 | 18 | @interface RNFileViewer () 19 | @end 20 | 21 | @implementation File 22 | 23 | - (id)initWithPath:(NSString *)file title:(NSString *)title { 24 | if(self = [super init]) { 25 | _previewItemURL = [NSURL fileURLWithPath:file]; 26 | _previewItemTitle = title; 27 | } 28 | return self; 29 | } 30 | 31 | @end 32 | 33 | @interface CustomQLViewController: QLPreviewController 34 | 35 | @property(nonatomic, strong) File *file; 36 | @property(nonatomic, strong) NSNumber *invocation; 37 | 38 | @end 39 | 40 | @implementation CustomQLViewController 41 | 42 | - (instancetype)initWithFile:(File *)file identifier:(NSNumber *)invocation { 43 | if(self = [super init]) { 44 | _file = file; 45 | _invocation = invocation; 46 | self.dataSource = self; 47 | } 48 | return self; 49 | } 50 | 51 | - (BOOL)prefersStatusBarHidden { 52 | return UIApplication.sharedApplication.isStatusBarHidden; 53 | } 54 | 55 | - (NSInteger)numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller{ 56 | return 1; 57 | } 58 | 59 | - (id )previewController:(QLPreviewController *)controller previewItemAtIndex:(NSInteger)index{ 60 | return self.file; 61 | } 62 | 63 | @end 64 | 65 | @implementation RNFileViewer 66 | 67 | - (dispatch_queue_t)methodQueue 68 | { 69 | return dispatch_get_main_queue(); 70 | } 71 | 72 | + (UIViewController*)topViewController { 73 | UIViewController *presenterViewController = [self topViewControllerWithRootViewController:UIApplication.sharedApplication.keyWindow.rootViewController]; 74 | return presenterViewController ? presenterViewController : UIApplication.sharedApplication.keyWindow.rootViewController; 75 | } 76 | 77 | + (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController { 78 | if ([viewController isKindOfClass:[UITabBarController class]]) { 79 | UITabBarController* tabBarController = (UITabBarController*)viewController; 80 | return [self topViewControllerWithRootViewController:tabBarController.selectedViewController]; 81 | } 82 | if ([viewController isKindOfClass:[UINavigationController class]]) { 83 | UINavigationController* navContObj = (UINavigationController*)viewController; 84 | return [self topViewControllerWithRootViewController:navContObj.visibleViewController]; 85 | } 86 | if (viewController.presentedViewController && !viewController.presentedViewController.isBeingDismissed) { 87 | UIViewController* presentedViewController = viewController.presentedViewController; 88 | return [self topViewControllerWithRootViewController:presentedViewController]; 89 | } 90 | for (UIView *view in [viewController.view subviews]) { 91 | id subViewController = [view nextResponder]; 92 | if ( subViewController && [subViewController isKindOfClass:[UIViewController class]]) { 93 | if ([(UIViewController *)subViewController presentedViewController] && ![subViewController presentedViewController].isBeingDismissed) { 94 | return [self topViewControllerWithRootViewController:[(UIViewController *)subViewController presentedViewController]]; 95 | } 96 | } 97 | } 98 | return viewController; 99 | } 100 | 101 | - (void)previewControllerDidDismiss:(CustomQLViewController *)controller { 102 | [self sendEventWithName:DISMISS_EVENT body: @{@"id": controller.invocation}]; 103 | } 104 | 105 | RCT_EXPORT_MODULE() 106 | 107 | - (NSArray *)supportedEvents { 108 | return @[OPEN_EVENT, DISMISS_EVENT]; 109 | } 110 | 111 | RCT_EXPORT_METHOD(open:(NSString *)path invocation:(nonnull NSNumber *)invocationId 112 | options:(NSDictionary *)options) 113 | { 114 | NSString *displayName = [RCTConvert NSString:options[@"displayName"]]; 115 | File *file = [[File alloc] initWithPath:path title:displayName]; 116 | 117 | QLPreviewController *controller = [[CustomQLViewController alloc] initWithFile:file identifier:invocationId]; 118 | controller.delegate = self; 119 | 120 | typeof(self) __weak weakSelf = self; 121 | [[RNFileViewer topViewController] presentViewController:controller animated:YES completion:^{ 122 | [weakSelf sendEventWithName:OPEN_EVENT body: @{@"id": invocationId}]; 123 | }]; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-file-viewer", 3 | "version": "2.1.5", 4 | "description": "Native file viewer for react-native", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "react-native", 12 | "mobile", 13 | "ios", 14 | "android", 15 | "windows", 16 | "file", 17 | "viewer", 18 | "preview", 19 | "quicklook", 20 | "pdf", 21 | "doc" 22 | ], 23 | "author": "Vincenzo Scamporlino", 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/vinzscam/react-native-file-viewer.git" 27 | }, 28 | "license": "MIT", 29 | "peerDependencies": { 30 | "react-native": ">=0.47" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /windows/RNFileViewer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2037 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RNFileViewer", "RNFileViewer\RNFileViewer.csproj", "{BE578CFA-16B7-4B29-978E-EF14C9E88020}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNative", "..\GitHub\DCTApp\node_modules\react-native-windows\ReactWindows\ReactNative\ReactNative.csproj", "{C7673AD5-E3AA-468C-A5FD-FA38154E205C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReactNativeWebViewBridge", "..\GitHub\DCTApp\node_modules\react-native-windows\ReactWindows\ReactNativeWebViewBridge\ReactNativeWebViewBridge.csproj", "{7596216B-669C-41F8-86DA-F3637F6545C0}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ChakraBridge", "..\GitHub\DCTApp\node_modules\react-native-windows\ReactWindows\ChakraBridge\ChakraBridge.vcxproj", "{4B72C796-16D5-4E3A-81C0-3E36F531E578}" 13 | EndProject 14 | Global 15 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 16 | ..\GitHub\DCTApp\node_modules\react-native-windows\ReactWindows\ReactNative.Shared\ReactNative.Shared.projitems*{c7673ad5-e3aa-468c-a5fd-fa38154e205c}*SharedItemsImports = 4 17 | EndGlobalSection 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|ARM = Debug|ARM 21 | Debug|x64 = Debug|x64 22 | Debug|x86 = Debug|x86 23 | Release|Any CPU = Release|Any CPU 24 | Release|ARM = Release|ARM 25 | Release|x64 = Release|x64 26 | Release|x86 = Release|x86 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|ARM.ActiveCfg = Debug|ARM 32 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|ARM.Build.0 = Debug|ARM 33 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|x64.ActiveCfg = Debug|x64 34 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|x64.Build.0 = Debug|x64 35 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|x86.ActiveCfg = Debug|x86 36 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Debug|x86.Build.0 = Debug|x86 37 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|ARM.ActiveCfg = Release|ARM 40 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|ARM.Build.0 = Release|ARM 41 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|x64.ActiveCfg = Release|x64 42 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|x64.Build.0 = Release|x64 43 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|x86.ActiveCfg = Release|x86 44 | {BE578CFA-16B7-4B29-978E-EF14C9E88020}.Release|x86.Build.0 = Release|x86 45 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|Any CPU.ActiveCfg = Debug|x86 46 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.ActiveCfg = Debug|ARM 47 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|ARM.Build.0 = Debug|ARM 48 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.ActiveCfg = Debug|x64 49 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x64.Build.0 = Debug|x64 50 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.ActiveCfg = Debug|x86 51 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Debug|x86.Build.0 = Debug|x86 52 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|Any CPU.ActiveCfg = Release|x86 53 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.ActiveCfg = Release|ARM 54 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|ARM.Build.0 = Release|ARM 55 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.ActiveCfg = Release|x64 56 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x64.Build.0 = Release|x64 57 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.ActiveCfg = Release|x86 58 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C}.Release|x86.Build.0 = Release|x86 59 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|Any CPU.ActiveCfg = Debug|x86 60 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|ARM.ActiveCfg = Debug|ARM 61 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|ARM.Build.0 = Debug|ARM 62 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x64.ActiveCfg = Debug|x64 63 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x64.Build.0 = Debug|x64 64 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x86.ActiveCfg = Debug|x86 65 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Debug|x86.Build.0 = Debug|x86 66 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|Any CPU.ActiveCfg = Release|x86 67 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|ARM.ActiveCfg = Release|ARM 68 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|ARM.Build.0 = Release|ARM 69 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x64.ActiveCfg = Release|x64 70 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x64.Build.0 = Release|x64 71 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x86.ActiveCfg = Release|x86 72 | {7596216B-669C-41F8-86DA-F3637F6545C0}.Release|x86.Build.0 = Release|x86 73 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|Any CPU.ActiveCfg = Debug|Win32 74 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.ActiveCfg = Debug|ARM 75 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|ARM.Build.0 = Debug|ARM 76 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.ActiveCfg = Debug|x64 77 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x64.Build.0 = Debug|x64 78 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.ActiveCfg = Debug|Win32 79 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Debug|x86.Build.0 = Debug|Win32 80 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|Any CPU.ActiveCfg = Release|Win32 81 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.ActiveCfg = Release|ARM 82 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|ARM.Build.0 = Release|ARM 83 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.ActiveCfg = Release|x64 84 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x64.Build.0 = Release|x64 85 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.ActiveCfg = Release|Win32 86 | {4B72C796-16D5-4E3A-81C0-3E36F531E578}.Release|x86.Build.0 = Release|Win32 87 | EndGlobalSection 88 | GlobalSection(SolutionProperties) = preSolution 89 | HideSolutionNode = FALSE 90 | EndGlobalSection 91 | GlobalSection(ExtensibilityGlobals) = postSolution 92 | SolutionGuid = {E013D5A4-4715-4421-84A1-FC6AB9301C22} 93 | EndGlobalSection 94 | EndGlobal 95 | -------------------------------------------------------------------------------- /windows/RNFileViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RNFileViewer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RNFileViewer")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /windows/RNFileViewer/Properties/RNFileViewer.rd.xml: -------------------------------------------------------------------------------- 1 | 2 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /windows/RNFileViewer/RNFileViewer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BE578CFA-16B7-4B29-978E-EF14C9E88020} 8 | Library 9 | Properties 10 | RNFileViewer 11 | RNFileViewer 12 | en-US 13 | UAP 14 | 10.0.16299.0 15 | 10.0.10240.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | 20 | 21 | AnyCPU 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 27 | prompt 28 | 4 29 | 30 | 31 | AnyCPU 32 | pdbonly 33 | true 34 | bin\Release\ 35 | TRACE;NETFX_CORE;WINDOWS_UWP 36 | prompt 37 | 4 38 | 39 | 40 | x86 41 | true 42 | bin\x86\Debug\ 43 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 44 | ;2008 45 | full 46 | x86 47 | false 48 | prompt 49 | 50 | 51 | x86 52 | bin\x86\Release\ 53 | TRACE;NETFX_CORE;WINDOWS_UWP 54 | true 55 | ;2008 56 | pdbonly 57 | x86 58 | false 59 | prompt 60 | 61 | 62 | ARM 63 | true 64 | bin\ARM\Debug\ 65 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 66 | ;2008 67 | full 68 | ARM 69 | false 70 | prompt 71 | 72 | 73 | ARM 74 | bin\ARM\Release\ 75 | TRACE;NETFX_CORE;WINDOWS_UWP 76 | true 77 | ;2008 78 | pdbonly 79 | ARM 80 | false 81 | prompt 82 | 83 | 84 | x64 85 | true 86 | bin\x64\Debug\ 87 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 88 | ;2008 89 | full 90 | x64 91 | false 92 | prompt 93 | 94 | 95 | x64 96 | bin\x64\Release\ 97 | TRACE;NETFX_CORE;WINDOWS_UWP 98 | true 99 | ;2008 100 | pdbonly 101 | x64 102 | false 103 | prompt 104 | 105 | 106 | PackageReference 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 6.0.8 117 | 118 | 119 | 120 | 121 | {C7673AD5-E3AA-468C-A5FD-FA38154E205C} 122 | ReactNative 123 | 124 | 125 | 126 | 14.0 127 | 128 | 129 | 136 | -------------------------------------------------------------------------------- /windows/RNFileViewer/RNFileViewerModule.cs: -------------------------------------------------------------------------------- 1 | using ReactNative.Bridge; 2 | using Windows.Storage; 3 | using Windows.System; 4 | using System; 5 | using Newtonsoft.Json.Linq; 6 | using Windows.UI.Core; 7 | 8 | namespace RNFileViewer 9 | { 10 | /// 11 | /// A module that allows JS to share data. 12 | /// 13 | class RNFileViewerModule : NativeModuleBase 14 | { 15 | /// 16 | /// The name of the native module. 17 | /// 18 | public override string Name 19 | { 20 | get 21 | { 22 | return "RNFileViewer"; 23 | } 24 | } 25 | 26 | [ReactMethod] 27 | public async void open(string filepath, JObject _, IPromise promise) 28 | { 29 | await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, 30 | async () => 31 | { 32 | 33 | try 34 | { 35 | var file = await StorageFile.GetFileFromPathAsync(filepath); 36 | 37 | if (file != null) 38 | { 39 | var success = await Launcher.LaunchFileAsync(file); 40 | 41 | if (success) 42 | { 43 | promise.Resolve(null); 44 | } 45 | else 46 | { 47 | promise.Reject(null, "File open failed."); 48 | } 49 | } 50 | else 51 | { 52 | promise.Reject(null, "File not found."); 53 | } 54 | } 55 | catch (Exception e) 56 | { 57 | promise.Reject(null, filepath, e); 58 | } 59 | } 60 | ); 61 | 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /windows/RNFileViewer/RNFileViewerPackage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ReactNative.Bridge; 3 | using ReactNative.Modules.Core; 4 | using ReactNative.UIManager; 5 | 6 | namespace RNFileViewer 7 | { 8 | /// 9 | /// Package defining core framework modules (e.g., ). 10 | /// It should be used for modules that require special integration with 11 | /// other framework parts (e.g., with the list of packages to load view 12 | /// managers from). 13 | /// 14 | public class RNFileViewerPackage : IReactPackage 15 | { 16 | public IReadOnlyList CreateNativeModules(ReactContext reactContext) 17 | { 18 | return new List 19 | { 20 | new RNFileViewerModule() 21 | }; 22 | } 23 | 24 | public IReadOnlyList CreateViewManagers(ReactContext reactContext) 25 | { 26 | return new List(0); 27 | } 28 | } 29 | } 30 | --------------------------------------------------------------------------------