import { Application, AndroidApplication, Utils, Device, Color, path, knownFolders, } from '@nativescript/core' import { localize } from '@nativescript/localize' let timerOne declare const global, android, androidx, com, java, Array: any const PowerManager = android.os.PowerManager const pm = Utils.android .getApplicationContext() .getSystemService(android.content.Context.POWER_SERVICE) const wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 'Timers') export const restartApp = () => { const ctx = Utils.ad.getApplicationContext() let mStartActivity = new android.content.Intent( ctx, Application.android.startActivity.getClass() ) let mPendingIntentId = Math.random() * 100000 let mPendingIntent = android.app.PendingIntent.getActivity( ctx, mPendingIntentId, mStartActivity, android.app.PendingIntent.FLAG_CANCEL_CURRENT ) let mgr = ctx.getSystemService(android.content.Context.ALARM_SERVICE) mgr.set( android.app.AlarmManager.RTC, java.lang.System.currentTimeMillis() + 100, mPendingIntent ) android.os.Process.killProcess(android.os.Process.myPid()) } export const openAppSettingsPage = () => { let intent = new android.content.Intent( android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS ) intent.addCategory(android.content.Intent.CATEGORY_DEFAULT) intent.setData( android.net.Uri.parse( 'package:' + Application.android.context.getPackageName() ) ) Application.android.foregroundActivity.startActivity(intent) } export const hasAccelerometer = () => { let ctx = Utils.ad.getApplicationContext() let sensorManager = ctx.getSystemService( android.content.Context.SENSOR_SERVICE ) return sensorManager.getDefaultSensor( android.hardware.Sensor.TYPE_ACCELEROMETER ) } export const vibrate = (duration) => { let vibratorService = Application.android.context.getSystemService( android.content.Context.VIBRATOR_SERVICE ) if (vibratorService.hasVibrator()) vibratorService.vibrate(duration) } export const timer = (dur, callback) => { callback(true) clearInterval(timerOne) timerOne = setInterval(() => { dur-- callback(true) if (dur == 0) { clearInterval(timerOne) callback(false) } }, 1000) } function callIntent(ctx, intent, msg, pickerType) { return new Promise((resolve) => { const onEvent = function(e) { if (e.requestCode === pickerType) { resolve(e) Application.android.off(AndroidApplication.activityResultEvent, onEvent) } } Application.android.once(AndroidApplication.activityResultEvent, onEvent) ctx.startActivityForResult( android.content.Intent.createChooser(intent, msg), pickerType ) }) } // IMAGE PICKER export const getRecipePhoto = () => { const ctx = Application.android.foregroundActivity || Application.android.startActivity const DIR_CODE = Math.round(Math.random() * 10000) const intent = new android.content.Intent( android.content.Intent.ACTION_GET_CONTENT ) intent.setType('image/*') return callIntent(ctx, intent, 'Select photo', DIR_CODE).then( ({ resultCode, intent }: any) => { if (resultCode === android.app.Activity.RESULT_OK) if (intent != null && intent.getData()) return intent.getData() } ) } export const copyPhotoToCache = (uri, filepath) => { const ContentResolver = Application.android.nativeApp.getContentResolver() return new Promise((resolve) => { const inputStream = ContentResolver.openInputStream(uri) const input = new java.io.BufferedInputStream(inputStream) let size = input.available() let buffer = Array.create('byte', size) const output = new java.io.BufferedOutputStream( new java.io.FileOutputStream(filepath) ) input.read(buffer) do { output.write(buffer) } while (input.read(buffer) != -1) input.close() output.close() resolve(filepath) }) } // COPY DB FILE export const copyDBToExport = () => { const src = path.join(knownFolders.documents().path, 'EnRecipes.db') const dst = path.join( knownFolders.documents().getFolder('EnRecipes').path, 'EnRecipes.db' ) const input = new java.io.FileInputStream(src) try { const output = new java.io.FileOutputStream(dst) let len: number let buffer = Array.create('byte', 1024) while ((len = input.read(buffer)) > 0) output.write(buffer, 0, len) } catch (error) { console.log(error) } finally { input.close() } } // BACKUP FOLDER PICKER export const getBackupFolder = () => { const ctx = Application.android.foregroundActivity || Application.android.startActivity const DIR_CODE = Math.round(Math.random() * 10000) const intent = new android.content.Intent( android.content.Intent.ACTION_OPEN_DOCUMENT_TREE ) return callIntent(ctx, intent, 'Select folder', DIR_CODE).then( ({ resultCode, intent }: any) => { if (resultCode === android.app.Activity.RESULT_OK) if (intent != null && intent.getData()) return intent.getData() } ) } // BACKUP FILE PICKER export const getBackupFile = () => { const ctx = Application.android.foregroundActivity || Application.android.startActivity const DIR_CODE = Math.round(Math.random() * 10000) const intent = new android.content.Intent( android.content.Intent.ACTION_GET_CONTENT ) intent.addCategory(android.content.Intent.CATEGORY_OPENABLE) intent.setType('application/zip') return callIntent(ctx, intent, 'Select file to import', DIR_CODE).then( ({ resultCode, intent }: any) => { if (resultCode === android.app.Activity.RESULT_OK) { if (intent != null && intent.getData()) return intent.getData() } } ) } // ZIP OPERATIONS export class Zip { static getSubFiles(src: string, isRootFolder?: boolean) { const fileList = new java.util.ArrayList() const sourceFile = new java.io.File(src) let tempList = sourceFile.listFiles() for (let i = 0; i < tempList.length; i++) { if (tempList[i].isFile()) { fileList.add(tempList[i]) } if (tempList[i].isDirectory()) { if (isRootFolder) { fileList.add(tempList[i]) } fileList.addAll(Zip.getSubFiles(tempList[i].getAbsolutePath())) } } return fileList } static zip(src, destUri, filename) { const ContentResolver = Application.android.nativeApp.getContentResolver() const parsedUri = new android.net.Uri.parse(destUri) const uri = new androidx.documentfile.provider.DocumentFile.fromTreeUri( Application.android.context, parsedUri ) return new Promise((resolve, reject) => { if (uri.exists()) { let destFile = uri.createFile('application/zip', filename).getUri() const outputStream = ContentResolver.openOutputStream(destFile) const zipOutputStream = new java.util.zip.ZipOutputStream(outputStream) const sourceFiles = Zip.getSubFiles(src, true) for (let i = 0; i < sourceFiles.size(); i++) { let len let buffer = Array.create('byte', 4096) let file = sourceFiles.get(i) let entry = new java.util.zip.ZipEntry( 'EnRecipes/' + new java.io.File(src) .toURI() .relativize(file.toURI()) .getPath() ) zipOutputStream.putNextEntry(entry) if (!file.isDirectory()) { let inputStream = new java.io.FileInputStream(file) while ((len = inputStream.read(buffer)) != -1) { zipOutputStream.write(buffer, 0, len) } inputStream.close() } zipOutputStream.closeEntry() } zipOutputStream.close() resolve(destFile) } else { reject('Destination file cannot be created: Path does not exist') } }) } static unzip(uri, dest) { const ContentResolver = Application.android.nativeApp.getContentResolver() return new Promise((resolve) => { const inputStream = ContentResolver.openInputStream(uri) const zipInputStream = new java.util.zip.ZipInputStream(inputStream) let len, filepath, entry while ((entry = zipInputStream.getNextEntry()) != null) { filepath = dest + '/' + entry.getName() let output = new java.io.File(filepath) if (entry.isDirectory()) { output.mkdirs() continue } if (!output.getParentFile().exists()) output.getParentFile().mkdirs() const outputStream = new java.io.BufferedOutputStream( new java.io.FileOutputStream(output) ) let buffer = Array.create('byte', 4096) while ((len = zipInputStream.read(buffer)) != -1) outputStream.write(buffer, 0, len) outputStream.close() zipInputStream.closeEntry() } zipInputStream.close() resolve(dest) }) } } // SHARE OPERATIONS function share(intent, subject) { const ctx = Application.android.context const shareIntent = android.content.Intent.createChooser(intent, subject) shareIntent.setFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK) ctx.startActivity(shareIntent) } function getSendIntent(type) { const intent = new android.content.Intent(android.content.Intent.ACTION_SEND) intent.setType(type) return intent } export const shareText = (text, subject) => { const intent = getSendIntent('text/plain') intent.putExtra(android.content.Intent.EXTRA_TEXT, text) share(intent, subject) } export const shareImage = (image, subject, title) => { let ctx = Application.android.context const intent = getSendIntent('image/jpeg') const baos = new java.io.ByteArrayOutputStream() image.android.compress(android.graphics.Bitmap.CompressFormat.JPEG, 100, baos) const tmpFile = new java.io.File(ctx.getCacheDir(), `${title}.jpg`) const fos = new java.io.FileOutputStream(tmpFile) fos.write(baos.toByteArray()) fos.flush() fos.close() let shareUri = global.androidx.core.content.FileProvider.getUriForFile( ctx, Application.android.nativeApp.getPackageName() + '.provider', tmpFile ) intent.putExtra(android.content.Intent.EXTRA_STREAM, shareUri) share(intent, subject) } export const keepScreenOn = (bool) => { let ctx = Application.android.foregroundActivity || Application.android.startActivity let window = ctx.getWindow() let flag = android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON bool ? window.addFlags(flag) : window.clearFlags(flag) } // TIMER NOTIFICATION export class TimerNotif { static getIcon(ctx, icon) { const packageName = ctx.getApplicationInfo().packageName let resources = ctx.getResources() return ( resources.getIdentifier(icon, 'drawable', packageName) || ctx.getApplicationInfo().icon ) } static clear(nID) { let ctx = Utils.ad.getApplicationContext() const NotifySrv = ctx.getSystemService( android.content.Context.NOTIFICATION_SERVICE ) // nID ? NotifySrv.cancel(nID) : NotifySrv.cancelAll() NotifySrv.cancel(nID) } static getNotification( { multi, actions, bID, cID, cName, description, priority, sound, title, vibrate, }: { multi?: boolean actions?: boolean bID: string cID: string cName: string description: string priority: number sound: string title: string vibrate?: number }, ctx ) { let sdkv: number = parseInt(Device.sdkVersion) let soundUri: any if (sound) soundUri = new android.net.Uri.parse(sound) const NotifyMgr = android.app.NotificationManager const NotifySrv = ctx.getSystemService( android.content.Context.NOTIFICATION_SERVICE ) const NotificationCompat = androidx.core.app.NotificationCompat const AudioManager = android.media.AudioManager if (sdkv >= 26) { const importance = priority > 0 ? NotifyMgr.IMPORTANCE_HIGH : NotifyMgr.IMPORTANCE_MIN const AudioAttributes = android.media.AudioAttributes const audioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_ALARM) .build() const Channel = new android.app.NotificationChannel( cID, cName, importance ) if (description) Channel.setDescription(description) Channel.enableVibration(vibrate) Channel.enableLights(false) if (sound) Channel.setSound(soundUri, audioAttributes) else Channel.setSound(null, null) Channel.setShowBadge(true) Channel.setLockscreenVisibility(NotificationCompat.VISIBILITY_PUBLIC) NotifySrv.createNotificationChannel(Channel) } const Intent = android.content.Intent const PendingIntent = android.app.PendingIntent const mainInt = new Intent(ctx, com.tns.NativeScriptActivity.class) mainInt.putExtra('action', 'timer') const mainPInt = PendingIntent.getActivity( ctx, 1, mainInt, PendingIntent.FLAG_UPDATE_CURRENT ) // Action intent let actionInt1, actionInt2, actionInt3, actionPInt1, actionPInt2, actionPInt3 if (actions) { actionInt1 = new Intent(bID) actionInt1.putExtra('action', 'delay') actionPInt1 = PendingIntent.getBroadcast( ctx, 2, actionInt1, PendingIntent.FLAG_UPDATE_CURRENT ) actionInt2 = new Intent(bID) actionInt2.putExtra('action', 'dismiss') actionPInt2 = PendingIntent.getBroadcast( ctx, 3, actionInt2, PendingIntent.FLAG_UPDATE_CURRENT ) actionInt3 = new Intent(bID) actionInt3.putExtra('action', 'dismissAll') actionPInt3 = PendingIntent.getBroadcast( ctx, 4, actionInt3, PendingIntent.FLAG_UPDATE_CURRENT ) } // CREATE NOTIFICATION let icon = this.getIcon(ctx, 'notify_icon_sil') let builder = new NotificationCompat.Builder(ctx, cID) .setColor(new Color('#ff5200').android) .setContentIntent(mainPInt) .setContentTitle(title) .setOngoing(true) .setPriority(priority) .setShowWhen(actions) .setSmallIcon(icon) .setTicker(title) .setAutoCancel(false) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) if (sound) builder.setSound(soundUri, AudioManager.STREAM_ALARM) else builder.setSound(null) if (description) builder.setContentText(description) if (vibrate) builder.setVibrate([500, 1000]) if (actions) { builder.setFullScreenIntent(mainPInt, true) if (multi) builder.addAction(null, localize('dismissAll'), actionPInt3) else { builder.addAction(null, localize('delay'), actionPInt1) builder.addAction(null, localize('dismiss'), actionPInt2) } } let notification = builder.build() notification.flags = NotificationCompat.FLAG_INSISTENT | NotificationCompat.FLAG_ONGOING_EVENT return notification } static show(data) { const ctx = Utils.ad.getApplicationContext() const NotifySrv = ctx.getSystemService( android.content.Context.NOTIFICATION_SERVICE ) NotifySrv.notify(data.nID, this.getNotification(data, ctx)) } } export class Printer { static PrintPackage = global.androidx.print static isSupported() { return this.PrintPackage.PrintHelper.systemSupportsPrint() } static print(view) { return new Promise((resolve, reject) => { try { let img: any img = android.graphics.Bitmap.createBitmap( view.getMeasuredWidth(), view.getMeasuredHeight(), android.graphics.Bitmap.Config.ARGB_8888 ) view.android.draw(new android.graphics.Canvas(img)) this.printImage(img).then(resolve, reject) } catch (e) { reject(e) } }) } static printImage(img) { return new Promise((resolve, reject) => { try { let callback = (success) => resolve(success) let printHelper = new this.PrintPackage.PrintHelper( Application.android.foregroundActivity ) printHelper.setScaleMode(this.PrintPackage.PrintHelper.SCALE_MODE_FIT) let jobName = 'MyPrintJob' printHelper.printBitmap(jobName, img) callback(true) } catch (e) { reject(e) } }) } } // GET RINGTONES LIST export const getTones = () => { const RingtoneManager = android.media.RingtoneManager let ctx = Utils.ad.getApplicationContext() const ringtonesMgr = new RingtoneManager(ctx) ringtonesMgr.setType(RingtoneManager.TYPE_ALARM) const cursor = ringtonesMgr.getCursor() let tones = [] while (cursor.moveToNext()) { tones.push({ title: cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX), uri: cursor.getString(RingtoneManager.URI_COLUMN_INDEX) + '/' + cursor.getString(RingtoneManager.ID_COLUMN_INDEX), }) } let defaultToneUri = RingtoneManager.getActualDefaultRingtoneUri( ctx, RingtoneManager.TYPE_ALARM ) let defaultTone if (defaultToneUri) { let uriString = defaultToneUri.toString() let tonesAvailable = tones.filter((e) => e.uri == uriString) let toneExist = tonesAvailable.length defaultTone = { title: toneExist ? tonesAvailable[0].title : tones[0].title, uri: toneExist ? uriString : tones[0].uri, } } return { tones, defaultTone: defaultToneUri ? defaultTone : tones[0] } } //DETECT RTL LANGUAGE export const RTL = (): boolean => { const ctx = Utils.android.getApplicationContext() const config = ctx.getResources().getConfiguration() return config.getLayoutDirection() == android.view.View.LAYOUT_DIRECTION_RTL } //WAKE LOCK export const wakeLock = (bool) => { bool ? !wl.isHeld() && wl.acquire() : wl.isHeld() && wl.release() }