UNPKG

7.02 kBJavaScriptView Raw
1import { Trace } from '../trace';
2import { getFileExtension } from './common';
3import { SDK_VERSION } from './constants';
4import { android as AndroidUtils } from './native-helper';
5import { topmost } from '../ui/frame/frame-stack';
6export { clearInterval, clearTimeout, setInterval, setTimeout } from '../timer';
7export * from './common';
8export * from './constants';
9export * from './debug';
10export * from './layout-helper';
11export * from './macrotask-scheduler';
12export * from './mainthread-helper';
13export * from './native-helper';
14export * from './types';
15const MIN_URI_SHARE_RESTRICTED_APK_VERSION = 24;
16export function GC() {
17 gc();
18}
19export function releaseNativeObject(object) {
20 __releaseNativeCounterpart(object);
21}
22export function openUrl(location) {
23 const context = AndroidUtils.getApplicationContext();
24 try {
25 const intent = new android.content.Intent(android.content.Intent.ACTION_VIEW, android.net.Uri.parse(location.trim()));
26 intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
27 context.startActivity(intent);
28 }
29 catch (e) {
30 // We don't do anything with an error. We just output it
31 Trace.write(`Failed to start activity for handling URL: ${location}`, Trace.categories.Error, Trace.messageType.error);
32 return false;
33 }
34 return true;
35}
36/**
37 * Check whether external storage is read only
38 *
39 * @returns {boolean} whether the external storage is read only
40 */
41function isExternalStorageReadOnly() {
42 const extStorageState = android.os.Environment.getExternalStorageState();
43 if (android.os.Environment.MEDIA_MOUNTED_READ_ONLY === extStorageState) {
44 return true;
45 }
46 return false;
47}
48/**
49 * Checks whether external storage is available
50 *
51 * @returns {boolean} whether external storage is available
52 */
53function isExternalStorageAvailable() {
54 const extStorageState = android.os.Environment.getExternalStorageState();
55 if (android.os.Environment.MEDIA_MOUNTED === extStorageState) {
56 return true;
57 }
58 return false;
59}
60/**
61 * Detect the mimetype of a file at a given path
62 *
63 * @param {string} filePath
64 * @returns {string} mimetype
65 */
66function getMimeTypeNameFromExtension(filePath) {
67 const mimeTypeMap = android.webkit.MimeTypeMap.getSingleton();
68 const extension = getFileExtension(filePath).replace('.', '').toLowerCase();
69 return mimeTypeMap.getMimeTypeFromExtension(extension);
70}
71/**
72 * Open a file
73 *
74 * @param {string} filePath
75 * @returns {boolean} whether opening the file succeeded or not
76 */
77export function openFile(filePath, title = 'Open File...') {
78 const context = AndroidUtils.getApplicationContext();
79 try {
80 // Ensure external storage is available
81 if (!isExternalStorageAvailable()) {
82 Trace.write(`
83External storage is unavailable (please check app permissions).
84Applications cannot access internal storage of other application on Android (see: https://developer.android.com/guide/topics/data/data-storage).
85`, Trace.categories.Error, Trace.messageType.error);
86 return false;
87 }
88 // Ensure external storage is available
89 if (isExternalStorageReadOnly()) {
90 Trace.write('External storage is read only', Trace.categories.Error, Trace.messageType.error);
91 return false;
92 }
93 // Determine file mimetype & start creating intent
94 const mimeType = getMimeTypeNameFromExtension(filePath);
95 const intent = new android.content.Intent(android.content.Intent.ACTION_VIEW);
96 const chooserIntent = android.content.Intent.createChooser(intent, title);
97 intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
98 chooserIntent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
99 // Android SDK <28 only requires starting the chooser Intent straight forwardly
100 if (SDK_VERSION < MIN_URI_SHARE_RESTRICTED_APK_VERSION) {
101 Trace.write(`detected sdk version ${SDK_VERSION} (< ${MIN_URI_SHARE_RESTRICTED_APK_VERSION}), using simple openFile`, Trace.categories.Debug);
102 intent.setDataAndType(android.net.Uri.fromFile(new java.io.File(filePath)), mimeType);
103 context.startActivity(chooserIntent);
104 return true;
105 }
106 Trace.write(`detected sdk version ${SDK_VERSION} (>= ${MIN_URI_SHARE_RESTRICTED_APK_VERSION}), using URI openFile`, Trace.categories.Debug);
107 // Android SDK 24+ introduced file system permissions changes that disallow
108 // exposing URIs between applications
109 //
110 // see: https://developer.android.com/reference/android/os/FileUriExposedException
111 // see: https://github.com/NativeScript/NativeScript/issues/5661#issuecomment-456405380
112 const providerName = `${context.getPackageName()}.provider`;
113 Trace.write(`fully-qualified provider name [${providerName}]`, Trace.categories.Debug);
114 const apkURI = androidx.core.content.FileProvider.getUriForFile(context, providerName, new java.io.File(filePath));
115 // Set flags & URI as data type on the view action
116 intent.addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION);
117 chooserIntent.addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION);
118 // Finish intent setup
119 intent.setDataAndType(apkURI, mimeType);
120 context.startActivity(chooserIntent);
121 return true;
122 }
123 catch (err) {
124 const msg = err.message ? `: ${err.message}` : '';
125 Trace.write(`Error in openFile${msg}`, Trace.categories.Error, Trace.messageType.error);
126 if (msg && msg.includes('Attempt to invoke virtual method') && msg.includes('android.content.pm.ProviderInfo.loadXmlMetaData') && msg.includes('on a null object reference')) {
127 // Alert user to possible fix
128 Trace.write(`
129Please ensure you have your manifest correctly configured with the FileProvider.
130(see: https://developer.android.com/reference/android/support/v4/content/FileProvider#ProviderDefinition)
131`, Trace.categories.Error);
132 }
133 return false;
134 }
135}
136export function isRealDevice() {
137 return AndroidUtils.isRealDevice();
138}
139export function dismissSoftInput(nativeView) {
140 AndroidUtils.dismissSoftInput(nativeView);
141}
142export function dismissKeyboard() {
143 dismissSoftInput();
144 const modalDialog = (topmost()?._modalParent ?? topmost()?.modal)?._dialogFragment?.getDialog();
145 const view = modalDialog ?? AndroidUtils.getCurrentActivity();
146 if (view) {
147 const focus = view.getCurrentFocus();
148 if (focus) {
149 focus.clearFocus();
150 }
151 }
152}
153export function copyToClipboard(value) {
154 try {
155 const clipboard = AndroidUtils.getApplicationContext().getSystemService(android.content.Context.CLIPBOARD_SERVICE);
156 const clip = android.content.ClipData.newPlainText('Clipboard value', value);
157 clipboard.setPrimaryClip(clip);
158 }
159 catch (err) {
160 console.log(err);
161 }
162}
163//# sourceMappingURL=index.android.js.map
\No newline at end of file