native-android-sms
v0.0.2
Published
Manage Android phone SMS
Downloads
60
Maintainers
Readme
Native Android SMS
A high-performance React Native module to manage SMS functionality on Android devices. Supports background SMS dispatch, personalized and broadcast bulk SMS, multi-SIM / Dual-SIM management, SIM health checks, delivery status event tracking, GSM-7/Unicode segment analysis, text sanitization, and permission controls.
Table of Contents
Features
- 📱 Direct Background SMS: Send SMS silently in the background without UI interruption.
- 🚀 Personalized & Broadcast Bulk SMS: Send identical messages to many recipients or distinct personalized messages per recipient with native delay pacing.
- 📊 Progress & Result Reporting: Built-in helper
sendBulkSmsWithProgressAsyncfor live UI progress bars and individual failure logs. - 📶 Multi-SIM / Dual-SIM Support: Query SIM slots, carrier names, country ISOs, and specify active SIM cards.
- 🛡️ Active SIM Detection & Fallback: Automatically filter out ghost/inactive SIM slots ("Emergency calls only", "No service") with optional fallback to the default SIM.
- 🔤 GSM-7 Text Sanitization: Convert smart quotes, em-dashes, and special whitespace to prevent accidental multi-segment UCS-2/Unicode billing expansions.
- 📏 SMS Segment Counter: Accurately compute character counts, segment thresholds, and Unicode detection.
- 📡 Delivery & Sent Event Listeners: Track cellular carrier ACK (
sent) and handset delivery reports (delivered). - 💬 Composer UI Fallback: Launch the system SMS composer (
smsto:) across Android and iOS.
Installation
npm install native-android-smsSince this library uses native Android code, run expo prebuild to build your project using local builds or EAS Build.
Quick Start
import React, { useEffect, useState } from 'react';
import { View, Text, Button, Alert } from 'react-native';
import * as Sms from 'native-android-sms';
export default function SmsApp() {
const [sims, setSims] = useState<Sms.SimInfo[]>([]);
useEffect(() => {
// Listen for SMS status updates from the cell radio
const sentSub = Sms.addSmsSentListener((event) => {
console.log('SMS Sent Status:', event.uuid, event.status, event.error);
});
const deliveredSub = Sms.addSmsDeliveredListener((event) => {
console.log('SMS Delivered Status:', event.uuid, event.status);
});
return () => {
sentSub.remove();
deliveredSub.remove();
};
}, []);
const handleSend = async () => {
// 1. Request SMS permission (granularly without requiring READ_PHONE_STATE)
const perm = await Sms.requestPermissionsAsync({ sendSms: true });
if (!perm.sendSms) {
Alert.alert('Permission Denied', 'SEND_SMS permission is required.');
return;
}
// 2. Fetch active SIM cards (filters out empty/inactive slots by default)
const activeSims = await Sms.getAvailableSimsAsync();
setSims(activeSims);
const subscriptionId = activeSims[0]?.subscriptionId;
// 3. Send SMS with optional SIM fallback and text sanitization
try {
const uuid = await Sms.sendSmsAsync('+1234567890', '“Hello World”—Welcome!', {
subscriptionId,
fallbackToDefaultSim: true,
sanitize: true, // Replaces curly quotes & dashes with standard GSM-7
});
Alert.alert('Dispatched', `Message queued with UUID: ${uuid}`);
} catch (error: any) {
Alert.alert('Error', error.message);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Active SIMs: {sims.length}</Text>
<Button title="Send SMS" onPress={handleSend} />
</View>
);
}API Reference
SMS Dispatch
sendSmsAsync(phoneNumber, message, optionsOrSubscriptionId?)
Sends an SMS in the background. Returns a unique UUID tracking the message.
phoneNumber: Recipient phone number in standard or E.164 format.message: Text content.optionsOrSubscriptionId: A numericsubscriptionIdor an options object:subscriptionId?: number | null: Specific SIM slot to send from.fallbackToDefaultSim?: boolean: If sending via the specified SIM fails, automatically retry using the Android default SMS SIM.sanitize?: boolean: Automatically normalize curly quotes, unicode dashes, and non-breaking spaces into standard GSM-7 characters before dispatch.
sendSmsUIAsync(phoneNumber, message)
Launches the system SMS composer pre-filled with the provided recipient and message. Works on Android and iOS.
Bulk SMS Dispatch
sendBulkSmsAsync(recipients, options?)
Dispatches personalized messages to multiple recipients in a high-performance native Android background coroutine (Dispatchers.IO).
recipients: Array ofBulkSmsRecipient:interface BulkSmsRecipient { to: string; message: string; recipientName?: string; id?: string; }options: Optional configuration:delayMs?: number: Milliseconds delay between consecutive sends (default:1000).subscriptionId?: number | null: Target SIM card.fallbackToDefaultSim?: boolean: Retries on the default SIM if specific SIM fails.sanitize?: boolean: Sanitizes all messages into GSM-7 format.
sendBulkSmsAsync(phoneNumbers, message, options?)
Broadcasts an identical message to an array of phone numbers natively.
sendBulkSmsWithProgressAsync(recipients, options?)
Convenience method for user interfaces needing real-time dispatch progress and detailed success/failure breakdowns:
const result = await Sms.sendBulkSmsWithProgressAsync(recipients, {
delayMs: 2000,
fallbackToDefaultSim: true,
sanitize: true,
onProgress: ({ current, total, label, currentRecipient }) => {
console.log(`[${current}/${total}] ${label}`);
},
});
console.log(`Sent: ${result.successCount}, Failed: ${result.failedCount}`);SIM Card Management
getAvailableSimsAsync(options?)
Returns an array of available SIM cards.
options.includeInactive?: boolean: Defaults tofalse. Whenfalse, automatically filters out inactive slots ("No service","Emergency calls only","No SIM","Disabled").
Returns SimInfo[]:
interface SimInfo {
subscriptionId: number;
displayName: string;
carrierName: string;
slotIndex: number;
number?: string;
countryIso?: string; // e.g. "ke", "us"
isActive?: boolean;
}canSendSmsAsync(): Promise<boolean>
Checks if the current device has telephony hardware capable of sending SMS.
Permissions
requestPermissionsAsync(options?): Promise<PermissionResponse>
Requests permissions granularly:
// Request only SEND_SMS without READ_PHONE_STATE
const res = await Sms.requestPermissionsAsync({ sendSms: true, readPhoneState: false });
console.log(res.sendSms); // booleangetPermissionsAsync(options?): Promise<PermissionResponse>
Checks the current permission status.
Utilities
sanitizeSmsText(text: string): string
Normalizes smart quotes (‘’“”), en/em dashes (–—), ellipsis (…), and non-breaking spaces into GSM-7 equivalents, preventing unintended 70-character UCS-2 multi-part expansions.
getSmsSegmentInfo(text: string): SmsSegmentInfo
Analyzes string content to determine segment count, remaining characters in the current segment, and encoding mode:
interface SmsSegmentInfo {
chars: number;
segments: number;
isUnicode: boolean;
limit: number; // 160 for GSM-7, 70 for Unicode
remaining: number; // Characters left in current segment
}isSimActive(sim: { carrierName?: string; displayName?: string }): boolean
Helper to test if a carrier or display name indicates an active cellular carrier.
Events
addSmsSentListener(listener: (event: SmsStatusEvent) => void): EventSubscription
Fires when the device's radio hands over the SMS to the mobile carrier tower. Includes error reason if failed (e.g. GENERIC_FAILURE, NO_SERVICE, RADIO_OFF).
addSmsDeliveredListener(listener: (event: SmsStatusEvent) => void): EventSubscription
Fires when the recipient's phone sends back a delivery receipt (subject to carrier support).
Types
interface SmsStatusEvent {
uuid: string;
status: 'sent' | 'delivered' | 'failed';
error?: string;
}
interface BulkSmsResult {
total: number;
successCount: number;
failedCount: number;
uuids: string[];
successful: Array<{ to: string; message: string; uuid?: string }>;
failed: Array<{ to: string; message: string; error: string }>;
}Android Permissions & Policies
The module includes standard permissions in its Android Manifest:
android.permission.SEND_SMSandroid.permission.READ_PHONE_STATE
Note: Background SMS dispatch is restricted by Google Play Store policies. Ensure your app falls within Google's permitted use-cases (such as Enterprise / CRM / Default SMS Handler / Financial transaction alerts) before publishing to the Play Store.
