Skip to content

VISKA-IO/upsell-background-actions

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

27 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

upsell-background-actions

A powerful React Native library designed specifically to handle autoprint functionality with continuous fetching/long polling in the background and printing. This package is optimized for a single autoprint process - if you need multiple background processes, consider using a different package or updating this one to support dynamic background services and semaphores.

Built on Android Services API - Learn more

Forked from react-native-background-actions

npm version License: MIT

✨ Features

  • Background Task Execution - Run tasks in the background with notification support
  • Alarm Management - Schedule exact alarms with proper permissions
  • Battery Optimization - Handle battery optimization settings
  • Semaphore Management - Thread-safe operations with custom semaphores
  • Service Management - Monitor and control background services
  • Permission Handling - Request and check various system permissions

πŸ“¦ Installation

npm install upsell-background-actions

Android Setup

Add these permissions to your android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

πŸš€ Quick Start

Basic Import

import BackgroundService from 'upsell-background-actions';

Complete Example

import BackgroundService from 'upsell-background-actions';

const sleep = (time) =>
  new Promise((resolve) => setTimeout(() => resolve(), time));

const veryIntensiveTask = async (taskDataArguments) => {
  const { delay } = taskDataArguments;

  await new Promise(async (resolve) => {
    for (let i = 0; BackgroundService.isBackgroundServiceRunning(); i++) {
      console.log(i);
      await sleep(delay);
    }
  });
};

const options = {
  taskName: 'AutoPrint', // THE NAME HAS TO BE THIS OTHERWISE IT WONT WORK WILL NEED TO MAKE THIS MORE DYNAMIC IN THE FUTURE
  taskTitle: 'AutoPrint',
  taskDesc: 'AutoPrint',
  taskIcon: {
    name: 'ic_launcher',
    type: 'mipmap',
  },
  color: '#ff00ff',
  linkingURI: 'upsell://autoprint/off',
  parameters: {
    delay: 3000,
  },
};

// Start the service
await BackgroundService.setIsBackgroundServiceRunning(true);
await BackgroundService.start(veryIntensiveTask, options, 90000);

// Update notification
await BackgroundService.updateNotification({
  taskDesc: 'New ExampleTask description',
});

Stopping the Service

// Stop the background loop
await BackgroundService.setIsBackgroundServiceRunning(false);

// Stop the alarm restart mechanism
await BackgroundService.stopAlarm();

// Send stop broadcast to service
await BackgroundService.sendStopBroadcast();

// Interrupt any queued threads
await BackgroundService.interruptQueuedThread();

πŸ“‹ API Reference

Core Service Methods

Method Description Returns
start(task, options, triggerTime) Starts background task Promise<void>
isBackgroundServiceRunning() Checks if service is running Promise<Boolean>
setIsBackgroundServiceRunning(value) Sets service state in database Promise<void>
updateNotification(taskData) Updates notification (Android only) Promise<void>
sendStopBroadcast() Sends stop broadcast Promise<void>
listRunningServices() Lists all running services Promise<Array>

Alarm Management

Method Description Returns
setAlarm(milliseconds) Sets alarm trigger time Promise<void>
stopAlarm() Stops current alarm Promise<void>
checkScheduleExactAlarmPermission() Checks exact alarm permission Promise<Boolean>
requestExactAlarmPermission() Requests exact alarm permission Promise<void>

Battery Optimization

Method Description Returns
isIgnoreBatteryOptimization() Checks battery optimization status Promise<Boolean>
requestIgnoreBatteryOptmization() Requests battery optimization exemption Promise<void>
requestActionIgnoreBatteryOptimizationSettings() Opens battery settings Promise<void>

Synchronization & Threading

Method Description
lock() Acquires general-purpose lock
unlock() Releases general-purpose lock
lockAddPrinterSemaphore() Acquires printer-specific lock
unlockAddPrinterSemaphore() Releases printer-specific lock
interruptQueuedThread() Interrupts queued threads

βš™οΈ Configuration Options

const options = {
  taskName: 'UniqueTaskName', // Unique task identifier
  taskTitle: 'Task Title', // Notification title
  taskDesc: 'Task Description', // Notification description
  taskIcon: {
    name: 'icon_name', // Icon resource name
    type: 'mipmap', // Resource type (mipmap/drawable)
    package: 'com.yourapp', // Package name (optional)
  },
  color: '#FF0000', // Notification color (hex)
  linkingURI: 'yourapp://action', // Deep link URI (optional)
  progressBar: {
    // Progress bar (optional)
    indeterminate: false,
    max: 100,
    value: 0,
  },
};

πŸ› οΈ Error Handling

try {
  await BackgroundService.start(myTask, options, 5000);
} catch (error) {
  console.error('Failed to start background task:', error);

  if (error.message.includes('Pending Intent not Found')) {
    // Handle pending intent error
  } else if (error.message.includes('Not Safe to stop Alarm')) {
    // Handle unsafe alarm stop
  }
}

πŸ“± Platform Support

Android Only - This library provides:

  • Background tasks with persistent notifications
  • Exact alarm scheduling
  • Battery optimization handling
  • Background service management

πŸ’‘ Best Practices

  1. Always check permissions before performing sensitive operations
  2. Handle errors gracefully with proper try-catch blocks
  3. Clean up resources by calling unlock() and stop methods
  4. Use exact alarms - Don't use WorkManager or JobScheduler as they adhere to Doze mode

πŸ”§ Technical Details

Why This Package Exists

This package bypasses Android Doze Mode (Learn about Doze mode) to enable continuous data fetching and printing.

How It Works

The package uses React Native's Headless JS API (Documentation) which utilizes the Android Service API (Learn about Services).

Important Notes

  • Database Storage: Service state is stored in database rather than memory because React Native may flush memory on weaker devices. Don't fall into the rabbit hole of trying to optimize this just for better app size - React Native/JavaScript is known to have memory flushed to create space as deemed necessary
  • Service Stopping: Stopping the service doesn't immediately stop your JS code - use isBackgroundServiceRunning() flags to control loops. Your JS code keeps running but will eventually be stopped
  • Headless JS: The service calls HeadlessService to handle JavaScript tasks with start/finish listeners
  • Single Process Design: This package expects only one autoprint process to be running. If you need multiple background processes, use a different package or update this one to support dynamic isBackgroundService function and semaphores
  • Exact Alarms: NEEDS TO BE DONE - DO NOT use anything other than EXACT ALARM Android API. WorkManager and JobScheduler don't work as they adhere to Doze mode
  • Sempahore Limitations: Note that you cannot implement Semaphore in JavaScript - it's a single-threaded language that does concurrency but cannot do locking mechanisms
  • Expo Compatibility: Previous errors when upgrading to EXPO 53 were caused by undocumented changes where Intent could be null in newer versions

Key Files to Understand in React Native Repo

  • HeadlessJSService.kt - Main service implementation
  • HeadlessJSServiceContext.kt - Service context management

Further Technical Explanation

Behind the scenes, this package uses React Native's Headless JS API and the Android Service API. A previous error when trying to upgrade to EXPO 53 was caused by an undocumented error when trying to start a Service where the Intent was able to be null but on older versions of EXPO and React Native it was not set to be null.

Headless JS works by calling HeadlessService in the background to handle the JavaScript task passed to it. It has listeners that indicate when the process is started or finished. However, when stopping the service it does not stop your JS code that you pass in React Native. Instead your JS code keeps running but will eventually be stopped, hence why we use isBackgroundServiceRunning and setIsBackgroundServiceRunning to stop the JS process.

πŸ“– Example App

Check the example/ directory for a complete implementation showing all features.

Further Improvement

The current autoprint process uses the long polling concept to fetch data and print, this introduces a problem where we don't exactly know and cannot notify users if this connection is severed or intterupted because sometimes an error does occur in API level such as error 500, or 501 however they usually only last for a second or 1 to two minutes.

Another problem is, this package doesn't fully solve the doze mode issue on different devices hence why we sill run into a problem where sometimes autoprint doesn't run properly. This is an android problem as I've said multiple times to Sindri that Android by design is not meant to have continous process running in the background. You can do it but you would need to root the device, but by then it would have taken too much effort and it wouldn't be a viable solution as it's not scalable

To fix this I recommend moving into a websocket procedure or SSE procedure on the javascript side. The good thing about using this is the server can tell when a connection is severed, and the device could also tell that. Combining it with package that restarts the process every n milliseconds, we will be able to send notification to remind users if the process is :

  1. Restarting
  2. Connection Established
  3. Connection Disconnected

PLEASE DO NOT FALL INTO THE TRAP OF TRYING TO BYPASS DOZE MODE TOO MUCH, THIS IS SIMPLY ANDROID's LIMITATION

Editing Android Function

To Edit the function in Android my ideal workflow is to open this in VSCODE and Android Studio. The difference is in Android Studio you open it from example/android folder. Do not open it from /android it must be from example/android folder, otherwise you'd run into compile issues or the IDE simply not recognizing the syntaxes. A comprehensive Readme is available in android/src/main/java/com/upsellbackgroundactions

πŸ“„ License

MIT Β© Dwikavindra


Made with create-react-native-library

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Kotlin 79.6%
  • TypeScript 13.2%
  • JavaScript 2.8%
  • Ruby 2.6%
  • Swift 1.3%
  • Objective-C++ 0.4%
  • Objective-C 0.1%