Class Texts

java.lang.Object
com.aurelienpierre.sioul.Texts

public final class Texts extends Object
Texts read on the phone for your computers (docs/texts.md; Rust's side: crates/sioul-app/src/texts.rs), without Sioul being the phone's SMS app: the SMS app stays the record. With READ_SMS, Android shows another app its inbox and its sent box only (the provider's restricted views): never the drafts, the failed or queued texts, nor a multimedia message still to download. Java reads, and hands Rust plain JSON; Rust keys the numbers, seals the lines, and decides what travels. Java never writes a shared file. Asked through StepService.call(android.content.Context, java.lang.String, java.lang.String) ("texts-…", from either process): the permissions and the SIMs, Android's question for the permissions (TextsAsk), the rows Rust's reader asks for by their ids (the whole history, every column, multimedia messages and their parts), a part's content, every row there is with its date, the texts Sioul sent lately (to find one after a crash), and a text to send (TextSend), whether the phone's texts changed since the last look and whether it charges (`changes`). Heard: a new text, which brings a step soon. The background service watches Android's provider of texts (`watch`): the import asks Android only when they changed, and a message received (a multimedia one too, once downloaded) brings a step soon, as a new text does.
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static final class 
    A new text, after the phone's SMS app had it (SMS_RECEIVED, sent by the system alone: its permission): a step soon, in which Rust reads it and shares it.
    (package private) static interface 
    An inbox's newest id asked in an order (`NEWEST`); a refusal throws.
    (package private) static interface 
    A row of a cursor, or of a check's stand-in: its values by column.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    (package private) static final String
    Java's own file, in the private texts folder: how Android answered the last question (TextsAsk).
    (package private) static final long
    How quickly Android must answer the question, giving nothing, for no question to have been shown: a person reads one longer (ms).
    private static final AtomicBoolean
    The phone's texts changed since Rust last asked with "clear" (`changes`): set at first, so that the first step looks.
    (package private) static final String
    Sioul's private texts folder in its state (Rust's `texts::private_folder`): Java's own files there.
    (package private) static final String
    Columns never kept: where the provider keeps a part's file on this phone.
    private static Runnable
     
    private static final long
    How long after the provider's last word the watch looks for a message received: a burst of changes, one look.
    private static Handler
    Where that look runs (the process's main thread, which the service leaves free), and the look itself; null: not watched.
    (package private) static final String[]
    How the newest row is asked for: the limit in the sort, since Android's own SMS and MMS providers pass no ContentResolver.QUERY_ARG_LIMIT on and would give every id each time; then, from a provider that refuses a limit there, the plain sort, its ids in that order and only the first read.
    private static long
     
    private static long
    The newest message received, by the provider's id, of each table (SMS, MMS), as the watch last saw it; -1: not read yet.
    (package private) static final int
    Android's provider: a text sent; received.
    (package private) static final String
     
    private static ContentObserver
    Android's provider of texts watched in this process (the background service's, or the window's); null: not watched here.
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    private
     
  • Method Summary

    Modifier and Type
    Method
    Description
    (package private) static boolean
    allowed(Context context, String permission)
     
    (package private) static boolean
    arrived(Context context)
    Whether a message was received since the watch last looked: the newest id of either inbox grew (`grew`); the newest ones kept.
    (package private) static void
    ask(Context context)
    Android's question for the texts' permissions: an activity never seen (TextsAsk), from Sioul's window.
    (package private) static JSONObject
    asked(Context context)
    How Android answered the texts' last question (`remember`); null when none was asked here.
    (package private) static boolean
    Whether the last question was answered at once, nothing given (`atOnce`); false when none was asked.
    (package private) static boolean
    atOnce(int given, long took)
    Whether Android answered the texts' question at once, giving none of the three: it showed none (Android 15's restricted settings after the first time, or "Don't allow" chosen twice before).
    (package private) static String
    call(Context context, String verb, JSONObject asked)
    (package private) static boolean
    changedSince(boolean watched, AtomicBoolean mark, boolean clear)
    Whether the texts changed since the last look: always when not watched here (READ_SMS not given yet); else as marked, the mark cleared when `clear`.
    (package private) static JSONObject
    changes(Context context, boolean clear)
    Rust's "texts-changes": {changed (`changedSince`), plugged (the phone on its charger)}.
    private static List<JSONObject>
    copies(ContentResolver resolver, Uri uri, String selection, String[] arguments)
    Every row of a query, every column copied (the cursor closed after).
    (package private) static JSONObject
    copy(Cursor cursor)
    A row's every column, as the provider gives it (a part's file's place on the phone aside).
    (package private) static JSONObject
    find(ContentResolver resolver, String own, long since)
    The texts Sioul sent since `since` (ms), as Android filed them in the sent box with Sioul as their creator: after a crash between a claim and Android's word, Rust looks for the one it claimed.
    (package private) static JSONObject
    findMms(ContentResolver resolver, String own, String key)
    Sioul's multimedia message by its key: the rows Android filed with that transaction id (the message Rust built carries it) and Sioul as their creator, each its id, its date (seconds) and its box (2: sent).
    (package private) static boolean
    grew(long before, long now)
    Whether an inbox's newest id grew: read before (not -1), larger now (a box that could not be read now says -1: no).
    (package private) static JSONObject
    ids(ContentResolver resolver, boolean measure)
    Every row there is now, each id with its own date ("sms" with "sms_at", "mms" with "mms_at": the plain texts' table gives a deleted newest text's id to the next one, and the date tells the two apart; Rust's `texts::Row`) and, when `measure`, how much media the multimedia messages hold: the rows to read, the texts deleted on the phone, and the sizes the settings say.
    (package private) static boolean
    info(Context context)
    Android's App info for Sioul, where ⋮ ▸ Allow restricted settings and the permissions are.
    (package private) static JSONObject
    limits(Context context, Integer only)
    What each SIM's carrier lets Sioul send (Android's carrier configuration, SmsManager.getCarrierConfigValues, which asks no permission): that SIM's alone (only; -1 the phone's default), else each active SIM's (READ_PHONE_STATE lists them), else the default's.
    (package private) static JSONObject
    limitsLine(int sub, int max, int width, int height, int recipients, boolean group, boolean mms)
    One SIM's carrier values as Rust reads them (texts::Limits): none below zero (a carrier's "no limit" said as the largest count).
    private static void
    listed(ContentResolver resolver, Uri table, String name, JSONObject out)
    A table's ids and dates (`ids`), put in `out` under `name` and `name_at` only when listed whole.
    (package private) static boolean
    media(String type)
    Whether a part's content travels as a file: anything but words and the SMIL that lays them out.
    (package private) static JSONObject
    mms(JSONObject row, List<JSONObject> addresses, List<JSONObject> parts)
    A multimedia message, as Rust reads it: every column of its row, of its addresses and of its parts (each part's size from Android, its file's place on the phone left out), so that a later restore loses nothing.
    private static long
    newest(Context context, Uri box)
    The newest id in an inbox, 0 when it is empty, -1 when it cannot be read (`newestBy`).
    (package private) static long
    The newest id as `look` gives it, asked in each order of NEWEST until one is taken; -1 when none is (the inbox cannot be read).
    private static Texts.Row
    of(Cursor cursor)
    A cursor's current row.
    (package private) static JSONObject
    part(ContentResolver resolver, long part, String target)
    A part's content copied to `target` (Sioul's own folder), for Rust to seal and share: {copied: bytes}, or -1.
    (package private) static boolean
    plugged(Context context)
    The phone on its charger, by Android's last word on the battery (a sticky broadcast: no receiver kept).
    (package private) static JSONObject
    read(ContentResolver resolver, long smsMark, long mmsMark, int limit, JSONArray smsIds, JSONArray mmsIds)
    The rows `smsIds` and `mmsIds` name (else those after the marks `sms`, `mms`: `rows`); `limit` at most, the plain texts first, by id, every column, the date among them (Rust tells a row by its id and its date); each conversation's people; and the new marks.
    (package private) static void
    remember(Context context, int given, int of, long took)
    How Android answered the question (TextsAsk), kept in Java's own file for `state`: how many of how many, how fast.
    (package private) static String
    Which rows a read takes: given `ids` (the rows Rust's reader asks for: those its log lacks, by their ids), those rows only; else those after the mark (no reader asks so since its version 4, 16 October 2026).
    (package private) static long
    size(ContentResolver resolver, long part)
    A part's size in bytes, as Android opens it; -1 when it does not open (not downloaded).
    (package private) static JSONObject
    A text of the SMS provider, as Rust reads it (crates/sioul-app/src/texts.rs, `Raw`): every column.
    (package private) static JSONObject
    state(Context context)
    What Android allows Sioul: each permission, the phone's SMS app, its SIMs (with READ_PHONE_STATE: their names).
    (package private) static JSONObject
    Each conversation's people, by its thread: Android's own list of each thread's recipients (the person themselves never among them), so that a group reads as a group.
    (package private) static void
    A new text heard (Heard): changed, whatever the provider said yet.
    (package private) static void
    unwatch(Context context)
    No longer watched (the service ending).
    (package private) static void
    watch(Context context)
    Android's provider of texts watched in this process (the background service's), once READ_SMS is given: a text received, sent, deleted or read marks them changed, read at the next step; a message received (its newest id grown, a multimedia one once downloaded) brings a step soon, as SMS_RECEIVED does (`Heard`), so that it reaches your computers within the minute.
    (package private) static void
    watch(Context context, boolean steps)
    The same, `steps` saying whether a message received brings a step (the background service's process); in the window's, which exchanges each minute by itself, the watch only marks the texts changed, so that its exchanges ask Android for every row only after a change (the review of 16 October 2026, LOW 3: unwatched, it listed every row once a minute while on screen).

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • TAG

      static final String TAG
      See Also:
    • FOLDER

      static final String FOLDER
      Sioul's private texts folder in its state (Rust's `texts::private_folder`): Java's own files there.
      See Also:
    • SENT_BOX

      static final int SENT_BOX
      Android's provider: a text sent; received.
      See Also:
    • changed

      private static final AtomicBoolean changed
      The phone's texts changed since Rust last asked with "clear" (`changes`): set at first, so that the first step looks.
    • watching

      private static ContentObserver watching
      Android's provider of texts watched in this process (the background service's, or the window's); null: not watched here.
    • newestSms

      private static long newestSms
      The newest message received, by the provider's id, of each table (SMS, MMS), as the watch last saw it; -1: not read yet.
    • newestMms

      private static long newestMms
    • LOOK_MS

      private static final long LOOK_MS
      How long after the provider's last word the watch looks for a message received: a burst of changes, one look.
      See Also:
    • looks

      private static Handler looks
      Where that look runs (the process's main thread, which the service leaves free), and the look itself; null: not watched.
    • look

      private static Runnable look
    • NEWEST

      static final String[] NEWEST
      How the newest row is asked for: the limit in the sort, since Android's own SMS and MMS providers pass no ContentResolver.QUERY_ARG_LIMIT on and would give every id each time; then, from a provider that refuses a limit there, the plain sort, its ids in that order and only the first read.
    • AT_ONCE_MS

      static final long AT_ONCE_MS
      How quickly Android must answer the question, giving nothing, for no question to have been shown: a person reads one longer (ms).
      See Also:
    • ASKED

      static final String ASKED
      Java's own file, in the private texts folder: how Android answered the last question (TextsAsk).
      See Also:
    • LOCAL_PATH

      static final String LOCAL_PATH
      Columns never kept: where the provider keeps a part's file on this phone.
      See Also:
  • Constructor Details

  • Method Details

    • watch

      static void watch(Context context)
      Android's provider of texts watched in this process (the background service's), once READ_SMS is given: a text received, sent, deleted or read marks them changed, read at the next step; a message received (its newest id grown, a multimedia one once downloaded) brings a step soon, as SMS_RECEIVED does (`Heard`), so that it reaches your computers within the minute. The look for it comes a second after the provider's last word: one message tells it several times (its text, its row, its conversation), one look. Asked again at each step until it holds.
    • watch

      static void watch(Context context, boolean steps)
      The same, `steps` saying whether a message received brings a step (the background service's process); in the window's, which exchanges each minute by itself, the watch only marks the texts changed, so that its exchanges ask Android for every row only after a change (the review of 16 October 2026, LOW 3: unwatched, it listed every row once a minute while on screen).
    • arrived

      static boolean arrived(Context context)
      Whether a message was received since the watch last looked: the newest id of either inbox grew (`grew`); the newest ones kept. Nothing of them is read but their ids.
    • grew

      static boolean grew(long before, long now)
      Whether an inbox's newest id grew: read before (not -1), larger now (a box that could not be read now says -1: no). Pure: android/jvm-checks/StepsCheck.java.
    • newest

      private static long newest(Context context, Uri box)
      The newest id in an inbox, 0 when it is empty, -1 when it cannot be read (`newestBy`).
    • newestBy

      static long newestBy(Texts.Look look)
      The newest id as `look` gives it, asked in each order of NEWEST until one is taken; -1 when none is (the inbox cannot be read). Pure: android/jvm-checks/StepsCheck.java.
    • unwatch

      static void unwatch(Context context)
      No longer watched (the service ending).
    • touched

      static void touched()
      A new text heard (Heard): changed, whatever the provider said yet.
    • changedSince

      static boolean changedSince(boolean watched, AtomicBoolean mark, boolean clear)
      Whether the texts changed since the last look: always when not watched here (READ_SMS not given yet); else as marked, the mark cleared when `clear`. Pure: android/jvm-checks/StepsCheck.java.
    • changes

      static JSONObject changes(Context context, boolean clear) throws JSONException
      Rust's "texts-changes": {changed (`changedSince`), plugged (the phone on its charger)}.
      Throws:
      JSONException
    • plugged

      static boolean plugged(Context context)
      The phone on its charger, by Android's last word on the battery (a sticky broadcast: no receiver kept).
    • of

      private static Texts.Row of(Cursor cursor)
      A cursor's current row.
    • call

      static String call(Context context, String verb, JSONObject asked) throws JSONException
      Throws:
      JSONException
    • allowed

      static boolean allowed(Context context, String permission)
    • state

      static JSONObject state(Context context) throws JSONException
      What Android allows Sioul: each permission, the phone's SMS app, its SIMs (with READ_PHONE_STATE: their names).
      Throws:
      JSONException
    • atOnce

      static boolean atOnce(int given, long took)
      Whether Android answered the texts' question at once, giving none of the three: it showed none (Android 15's restricted settings after the first time, or "Don't allow" chosen twice before). `took` is -1 when not measured. Pure: android/jvm-checks/TextsCheck.java.
    • remember

      static void remember(Context context, int given, int of, long took)
      How Android answered the question (TextsAsk), kept in Java's own file for `state`: how many of how many, how fast.
    • asked

      static JSONObject asked(Context context)
      How Android answered the texts' last question (`remember`); null when none was asked here.
    • askedAtOnce

      static boolean askedAtOnce(Context context)
      Whether the last question was answered at once, nothing given (`atOnce`); false when none was asked.
    • info

      static boolean info(Context context)
      Android's App info for Sioul, where ⋮ ▸ Allow restricted settings and the permissions are.
    • ask

      static void ask(Context context)
      Android's question for the texts' permissions: an activity never seen (TextsAsk), from Sioul's window.
    • copy

      static JSONObject copy(Cursor cursor) throws JSONException
      A row's every column, as the provider gives it (a part's file's place on the phone aside).
      Throws:
      JSONException
    • sms

      static JSONObject sms(JSONObject row) throws JSONException
      A text of the SMS provider, as Rust reads it (crates/sioul-app/src/texts.rs, `Raw`): every column.
      Throws:
      JSONException
    • mms

      static JSONObject mms(JSONObject row, List<JSONObject> addresses, List<JSONObject> parts) throws JSONException
      A multimedia message, as Rust reads it: every column of its row, of its addresses and of its parts (each part's size from Android, its file's place on the phone left out), so that a later restore loses nothing.
      Throws:
      JSONException
    • media

      static boolean media(String type)
      Whether a part's content travels as a file: anything but words and the SMIL that lays them out.
    • size

      static long size(ContentResolver resolver, long part)
      A part's size in bytes, as Android opens it; -1 when it does not open (not downloaded).
    • threads

      static JSONObject threads(ContentResolver resolver) throws JSONException
      Each conversation's people, by its thread: Android's own list of each thread's recipients (the person themselves never among them), so that a group reads as a group.
      Throws:
      JSONException
    • rows

      static String rows(JSONArray ids)
      Which rows a read takes: given `ids` (the rows Rust's reader asks for: those its log lacks, by their ids), those rows only; else those after the mark (no reader asks so since its version 4, 16 October 2026).
    • read

      static JSONObject read(ContentResolver resolver, long smsMark, long mmsMark, int limit, JSONArray smsIds, JSONArray mmsIds) throws JSONException
      The rows `smsIds` and `mmsIds` name (else those after the marks `sms`, `mms`: `rows`); `limit` at most, the plain texts first, by id, every column, the date among them (Rust tells a row by its id and its date); each conversation's people; and the new marks.
      Throws:
      JSONException
    • copies

      private static List<JSONObject> copies(ContentResolver resolver, Uri uri, String selection, String[] arguments)
      Every row of a query, every column copied (the cursor closed after).
    • ids

      static JSONObject ids(ContentResolver resolver, boolean measure) throws JSONException
      Every row there is now, each id with its own date ("sms" with "sms_at", "mms" with "mms_at": the plain texts' table gives a deleted newest text's id to the next one, and the date tells the two apart; Rust's `texts::Row`) and, when `measure`, how much media the multimedia messages hold: the rows to read, the texts deleted on the phone, and the sizes the settings say. A table that could not be listed whole is left out: Rust then reads nothing and marks nothing deleted, never taking "not known" for "none there".
      Throws:
      JSONException
    • listed

      private static void listed(ContentResolver resolver, Uri table, String name, JSONObject out) throws JSONException
      A table's ids and dates (`ids`), put in `out` under `name` and `name_at` only when listed whole.
      Throws:
      JSONException
    • part

      static JSONObject part(ContentResolver resolver, long part, String target) throws JSONException
      A part's content copied to `target` (Sioul's own folder), for Rust to seal and share: {copied: bytes}, or -1.
      Throws:
      JSONException
    • find

      static JSONObject find(ContentResolver resolver, String own, long since) throws JSONException
      The texts Sioul sent since `since` (ms), as Android filed them in the sent box with Sioul as their creator: after a crash between a claim and Android's word, Rust looks for the one it claimed.
      Throws:
      JSONException
    • limits

      static JSONObject limits(Context context, Integer only) throws JSONException
      What each SIM's carrier lets Sioul send (Android's carrier configuration, SmsManager.getCarrierConfigValues, which asks no permission): that SIM's alone (only; -1 the phone's default), else each active SIM's (READ_PHONE_STATE lists them), else the default's. Numbers only, never the carrier's name.
      Throws:
      JSONException
    • limitsLine

      static JSONObject limitsLine(int sub, int max, int width, int height, int recipients, boolean group, boolean mms) throws JSONException
      One SIM's carrier values as Rust reads them (texts::Limits): none below zero (a carrier's "no limit" said as the largest count). Pure: android/jvm-checks/TextsCheck.java.
      Throws:
      JSONException
    • findMms

      static JSONObject findMms(ContentResolver resolver, String own, String key) throws JSONException
      Sioul's multimedia message by its key: the rows Android filed with that transaction id (the message Rust built carries it) and Sioul as their creator, each its id, its date (seconds) and its box (2: sent).
      Throws:
      JSONException