Support 7z extraction, and fix .xz extraction which would have crashed at runtime
Commons Compress declares its LZMA/LZMA2 support against org.tukaani:xz as an *optional* dependency, so nothing pulled it in transitively and nothing failed at build time. The app nevertheless advertised .xz as extractable, so the first user to open one would have got NoClassDefFoundError: org/tukaani/xz/FilterOptions. XZCompressorInputStream references org/tukaani/xz/XZInputStream directly. The library is now declared, and a round-trip test covers the path that was silently missing. That same library is what 7z needs, and Commons Compress already ships a pure-Java 7z reader, so 7z extraction costs no native code and no further dependency. The docs had assumed the format was off limits and said so in three places, including the customer-facing store copy. 7z is the only format here that cannot arrive through a stream: its header sits at the end of the file, so the reader has to seek. That is now a capability the backend advertises, StorageSource.openSeekable: the all-files backend answers with a real FileChannel, the SAF fallback with null, so folder mode reports needs-a-seekable-file instead of failing obscurely. Entry names go through the same ArchivePath.safeRelativePath zip-slip guard. 7z creation stays deferred.
This commit is contained in:
@@ -12,6 +12,13 @@ upload needs a **higher `versionCode`** than every previous upload.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`.xz` extraction would have crashed on a real device.** Commons Compress declares its
|
||||
LZMA/LZMA2 support — the `.xz` compressor and the 7z format — against `org.tukaani:xz` as
|
||||
an **optional** dependency. Optional means it is not pulled in transitively, so nothing
|
||||
warned at build time, and the app went on advertising `.xz` as extractable. The first user
|
||||
to open one would have got `NoClassDefFoundError: org/tukaani/xz/FilterOptions`. The
|
||||
library is now declared, and a round-trip test covers the path that was silently missing.
|
||||
|
||||
- **The storage-usage screen stayed blank for ~45–60 s** on a real 55 GB volume, which
|
||||
reads as a hang even though the work was off the main thread. `UsageAnalysis` now passes
|
||||
the `FileEntry` down the recursion instead of re-`stat`ing every node — the parent's
|
||||
@@ -31,6 +38,19 @@ upload needs a **higher `versionCode`** than every previous upload.
|
||||
|
||||
### Added
|
||||
|
||||
- **7z extraction.** Commons Compress already contained a pure-Java 7z reader
|
||||
(`SevenZFile`), so this needed no new native code and no library beyond the XZ one the
|
||||
`.xz` fix above required — the docs had simply assumed the format was off-limits and said
|
||||
so in two places. The one real constraint is that 7z is the only format here that cannot
|
||||
arrive through a stream: its header sits at the end of the file, so the reader has to
|
||||
seek. That is now a capability the backend can advertise, `StorageSource.openSeekable`:
|
||||
the all-files backend answers with a real `FileChannel`, and the SAF fallback answers
|
||||
`null`, so a 7z in folder mode is refused with "needs a seekable file, which this storage
|
||||
mode cannot provide" rather than failing obscurely. Entry names go through the same
|
||||
`ArchivePath.safeRelativePath` zip-slip guard as every other format, with a test that
|
||||
plants `../evil.txt` in a real 7z and asserts it lands nowhere. 7z *creation* stays
|
||||
deferred, so ZIP is still the only writable format.
|
||||
|
||||
- **A two-pane layout for large screens.** A window at or above Material's "expanded"
|
||||
breakpoint — 840 dp, which a 10-inch tablet in landscape at 1280 dp clears — now splits
|
||||
into a master pane and a detail pane. Tapping a row highlights it, and the second pane
|
||||
|
||||
Reference in New Issue
Block a user