Addresses the review feedback on #826.
Compression searches for a size that fits under the carrier limit, so it encodes
the same image many times: a binary search over dimensions, then a quality ladder
from 90 down to 40 if nothing fit. Every one of those attempts went through
ImageUtils.getScaledImage, which re-opened the source URI and re-decoded it from
scratch. Decoding is the expensive part and it was the only part being repeated
unnecessarily; a large photo could be decoded a dozen or more times to send once.
ScaledImageEncoder decodes the source a single time, sampled down to no smaller
than the largest width the search will ask for, and re-encodes that bitmap from
memory for each attempt. The search itself is unchanged.
The decode is guarded: with no carrier cap the target size is the original image,
which on a modern camera sensor is large enough to matter, so an OutOfMemoryError
returns null and the caller falls back to Glide, which streams instead of holding
the whole bitmap.
Also fixes three edge cases raised in review:
- A bounds decode that fails leaves outWidth/outHeight at zero, making the aspect
ratio 0/0 and every derived dimension garbage. Detect it and fall back rather
than divide by it.
- searchHi was floored at 100, which raised it above the original width, and above
the carrier maximum when that was under 100. Both upscale the image and inflate
the payload. The floor is now 1 and the search starts at 1 rather than 100.
- The last-resort fallback hard-coded a width of 100, upscaling anything narrower.
It is now bounded by searchHi, so it can only shrink.
Scale factor algorithm became <= 0 after 4-5 attempts regardless of
image size, silently bailing and leaving images potentially over the
carrier size limit. Replace with binary search on dimensions (100px to
carrier max) plus a quality-reduction fallback (90→40) for images that
still don't fit. Adds compressAttachment dispatch for extensibility
(audio/* logged, image/* compressed, else raw). Fixes InputStream leak
in BitmapFactory dimension probe. Queries MMS_CONFIG_ALLOW_ATTACH_AUDIO
for future audio support groundwork.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Android's normalizing function stripped out characters too aggressively leading to false matches between different numbers. This commit introduces a faster normalizing function that includes characters.
- Adds a new class of features for message management.
- Adds a manual option to find and delete duplicate messages
- Adds a setting to automatically delete duplicates during sync
- Adds a sha256 hashing function
Co-authored-by: octoshrimpy <octoshrimpy@users.noreply.github.com>
In some cases, (mainly on HyperOS and variants) content provider will return null if an empty conversation is created. This was breaking the compose activity as it expects some object other than null.
This commit changes the behavior to provide a new conversation object if createConversationFromCp ever returns null.
Some emoji reactions will appear as "Liked 'message...'" with the message cut off. This change uses a regex to match all ellipsis at the end of messages to any other message with the same content.