diff --git a/.gitignore b/.gitignore index 256df604e..689b6a794 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.idea/ /build/ /captures/ +/composeApp/build/ /local.properties /signing.jks /signing.properties diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 8ac97eaf7..000000000 --- a/app/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.cxx/ -/.externalNativeBuild/ -/build/ -/out/ diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt deleted file mode 100644 index 608a96d11..000000000 --- a/app/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.6) - -project(MaterialFiles C) - -add_library(hiddenapi SHARED - src/main/jni/hiddenapi.c) -target_compile_options(hiddenapi - PRIVATE - -Wall - -Werror) - -find_library(LOG_LIBRARY log) -add_library(syscall SHARED - src/main/jni/syscall.c) -target_compile_options(syscall - PRIVATE - -Wall - -Werror) -target_link_libraries(syscall - PRIVATE - ${LOG_LIBRARY}) diff --git a/app/build.gradle b/app/build.gradle deleted file mode 100644 index 2d18f05cf..000000000 --- a/app/build.gradle +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-parcelize' - -apply from: '../signing.gradle' - -//#ifdef NONFREE -buildscript { - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.google.gms:google-services:4.4.1' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' - } -} -apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.google.firebase.crashlytics' -//#endif - -android { - namespace 'me.zhanghai.android.files' - buildToolsVersion = '34.0.0' - compileSdk 34 - ndkVersion '26.3.11579264' - defaultConfig { - applicationId 'me.zhanghai.android.files' - minSdk 21 - // Not supporting notification runtime permission yet. - //noinspection OldTargetApi - targetSdk 34 - versionCode 37 - versionName '1.7.2' - resValue 'string', 'app_version', versionName + ' (' + versionCode + ')' - buildConfigField 'String', 'FILE_PROVIDIER_AUTHORITY', 'APPLICATION_ID + ".file_provider"' - resValue 'string', 'app_provider_authority', applicationId + '.app_provider' - resValue 'string', 'file_provider_authority', applicationId + '.file_provider' - externalNativeBuild { - cmake { - arguments '-DANDROID_STL=none' - } - } - } - buildFeatures { - aidl true - buildConfig true - viewBinding true - } - androidResources { - generateLocaleConfig true - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() - } - externalNativeBuild { - cmake { - path 'CMakeLists.txt' - } - } - lint { - // For "Invalid package reference in library; not included in Android: javax.security.sasl. - // Referenced from org.apache.mina.proxy.ProxyAuthException." - warning 'InvalidPackage', 'MissingTranslation' - } - buildTypes { - release { - minifyEnabled true - shrinkResources true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.release -//#ifdef NONFREE - firebaseCrashlytics { - nativeSymbolUploadEnabled true - } -//#endif - } - } - packagingOptions { - jniLibs { - useLegacyPackaging true - } - resources { - excludes += [ - 'META-INF/DEPENDENCIES', - 'org/bouncycastle/pqc/crypto/picnic/*' - ] - } - } - bundle { - language { - enableSplit = false - } - } -} - -repositories { - maven { - url 'https://jitpack.io' - } -} -dependencies { - implementation('com.github.zhanghai:dav4jvm:c317607') { - exclude group: 'org.ogce', module: 'xpp3' - } - implementation 'com.github.chrisbanes:PhotoView:2.3.0' - implementation 'com.github.topjohnwu.libsu:service:5.2.2' -} - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - - // kotlinx-coroutines-android depends on kotlin-stdlib-jdk8 - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - def kotlinx_coroutines_version = '1.8.0' - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version" - - implementation 'androidx.activity:activity-ktx:1.9.0' - // Appcompat 1.7.0-alpha01 is required for properly changing locale below API 24 (b/243119645). - implementation 'androidx.appcompat:appcompat:1.7.0-beta01' - implementation 'androidx.core:core-ktx:1.13.1' - implementation 'androidx.drawerlayout:drawerlayout:1.2.0' - implementation 'androidx.exifinterface:exifinterface:1.3.7' - implementation 'androidx.fragment:fragment-ktx:1.7.0' - def androidx_lifecycle_version = '2.7.0' - implementation "androidx.lifecycle:lifecycle-common-java8:$androidx_lifecycle_version" - implementation "androidx.lifecycle:lifecycle-livedata-ktx:$androidx_lifecycle_version" - implementation "androidx.lifecycle:lifecycle-process:$androidx_lifecycle_version" - implementation "androidx.lifecycle:lifecycle-runtime-ktx:$androidx_lifecycle_version" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$androidx_lifecycle_version" - implementation 'androidx.preference:preference-ktx:1.2.1' - implementation 'androidx.recyclerview:recyclerview:1.3.2' - implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' - implementation 'com.google.android.material:material:1.12.0' - - implementation 'com.caverock:androidsvg-aar:1.4' - implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' - implementation 'com.drakeet.drawer:drawer:1.0.3' - implementation 'com.h6ah4i.android.materialshadowninepatch:materialshadowninepatch:1.0.0' - implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' - // SMBJ 0.12.0 breaks anonymous authentication: https://github.com/hierynomus/smbj/issues/792 - //noinspection GradleDependency - implementation ('com.hierynomus:smbj:0.11.5') { - // org.bouncycastle:bcprov-jdk15on uses bytecode version unsupported by Jetifier, so use - // org.bouncycastle:bcprov-jdk15to18 instead. - exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on' - } - // SSHJ 0.36.0 requires Java 8. - //noinspection GradleDependency - implementation ('com.hierynomus:sshj:0.35.0') { - exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on' - } - implementation 'com.jakewharton.threetenabp:threetenabp:1.4.6' - implementation 'com.leinardi.android:speed-dial:3.3.0' - implementation ('com.rapid7.client:dcerpc:0.12.1') { - // SMBJ-RPC depends on the JRE flavor of Guava which targets Java 8. - exclude group: 'com.google.guava', module: 'guava' - exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on' - } - implementation 'com.google.guava:guava:33.0.0-android' - // Guava conflicts with com.google.guava:listenablefuture:1.0 pulled in by AndroidX Core - implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' - implementation 'com.takisoft.preferencex:preferencex:1.1.0' - // Commons Net 3.9.0 started using java.time.Duration in FTPClient. - //noinspection GradleDependency - implementation 'commons-net:commons-net:3.8.0' - // LicensesDialog 2.2.0 pulls in androidx.webkit and uses setForceDark() instead of correctly - // setting colors. - //noinspection GradleDependency - implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0' - // dev.chrisbanesinsetter:insetter:0.6.0 makes inset unstable when entering immersive. - implementation 'dev.chrisbanes:insetter-ktx:0.3.1' - implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3' - implementation 'dev.rikka.shizuku:api:13.1.5' - implementation ('eu.agno3.jcifs:jcifs-ng:2.1.10') { - exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on' - } - implementation 'org.bouncycastle:bcprov-jdk15to18:1.77' - implementation platform('io.coil-kt:coil-bom:2.6.0') - implementation 'io.coil-kt:coil' - implementation 'io.coil-kt:coil-gif' - implementation 'io.coil-kt:coil-svg' - implementation 'io.coil-kt:coil-video' - implementation 'me.zhanghai.android.appiconloader:appiconloader:1.5.0' - implementation 'me.zhanghai.android.fastscroll:library:1.3.0' - implementation 'me.zhanghai.android.foregroundcompat:library:1.0.2' - implementation 'me.zhanghai.android.libarchive:library:1.0.3' - implementation 'me.zhanghai.android.libselinux:library:2.1.0' - implementation 'me.zhanghai.android.retrofile:library:1.1.1' - implementation 'me.zhanghai.android.systemuihelper:library:1.0.0' - implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.4' - implementation 'org.apache.ftpserver:ftpserver-core:1.2.0' - // This is a dependency of org.apache.ftpserver:ftpserver-core but org.apache.mina:mina-core - // 2.1.3+ became incompatible before API 24 due to dependency on StandardSocketOptions - // (DIRMINA-1123) and NetworkChannel.supportedOptions() (DIRMINA-1130). - implementation ('org.apache.mina:mina-core') { - version { - strictly '2.1.3' - } - } - // Also a dependency of jCIFS-NG. - implementation 'org.slf4j:slf4j-android:1.7.36' - -//#ifdef NONFREE - implementation platform('com.google.firebase:firebase-bom:32.7.4') - implementation 'com.google.firebase:firebase-analytics' - implementation 'com.google.firebase:firebase-crashlytics-ndk' -//#endif -} diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 6e41000c5..000000000 --- a/app/google-services.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "project_info": { - "project_number": "728501351509", - "firebase_url": "https://zhanghai-materialfiles.firebaseio.com", - "project_id": "zhanghai-materialfiles", - "storage_bucket": "zhanghai-materialfiles.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:728501351509:android:007cb5eb181702a9", - "android_client_info": { - "package_name": "me.zhanghai.android.files" - } - }, - "oauth_client": [ - { - "client_id": "728501351509-3ul5c5u8jpd9q9gi4e7537bd31r5nkr5.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyDUcQAOKRcCjsA-e5-cZHbt4HQQFDP4wA4" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "728501351509-3ul5c5u8jpd9q9gi4e7537bd31r5nkr5.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro deleted file mode 100644 index 6c70e0117..000000000 --- a/app/proguard-rules.pro +++ /dev/null @@ -1,57 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile - -# Native methods -# https://www.guardsquare.com/en/products/proguard/manual/examples#native --keepclasseswithmembernames,includedescriptorclasses class * { - native ; -} - -# App --keep class me.zhanghai.android.files.** implements androidx.appcompat.view.CollapsibleActionView { *; } --keep class me.zhanghai.android.files.provider.common.ByteString { *; } --keep class me.zhanghai.android.files.provider.linux.syscall.** { *; } --keepnames class * extends java.lang.Exception -# For Class.getEnumConstants() --keepclassmembers enum * { - public static **[] values(); -} --keepnames class me.zhanghai.android.files.** implements android.os.Parcelable - -# Apache FtpServer --keepclassmembers class * implements org.apache.mina.core.service.IoProcessor { - public (java.util.concurrent.ExecutorService); - public (java.util.concurrent.Executor); - public (); -} - -# Bouncy Castle --keep class org.bouncycastle.jcajce.provider.** { *; } --keep class org.bouncycastle.jce.provider.** { *; } - -# SMBJ --dontwarn javax.el.** --dontwarn org.ietf.jgss.** --dontwarn sun.security.x509.X509Key - -# SMBJ-RPC --dontwarn java.rmi.UnmarshalException diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml deleted file mode 100644 index 4479e3e09..000000000 --- a/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,399 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelableFileTime.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelableFileTime.aidl deleted file mode 100644 index 1a521a7ba..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelableFileTime.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.common; - -parcelable ParcelableFileTime; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelablePosixFileMode.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelablePosixFileMode.aidl deleted file mode 100644 index 6dc79157f..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelablePosixFileMode.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.common; - -parcelable ParcelablePosixFileMode; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixGroup.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixGroup.aidl deleted file mode 100644 index 0599e79a9..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixGroup.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.common; - -parcelable PosixGroup; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixUser.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixUser.aidl deleted file mode 100644 index 83760ca39..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixUser.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.common; - -parcelable PosixUser; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileService.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileService.aidl deleted file mode 100644 index 5d9dfba72..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileService.aidl +++ /dev/null @@ -1,23 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.IRemoteFileSystem; -import me.zhanghai.android.files.provider.remote.IRemoteFileSystemProvider; -import me.zhanghai.android.files.provider.remote.IRemotePosixFileAttributeView; -import me.zhanghai.android.files.provider.remote.IRemotePosixFileStore; -import me.zhanghai.android.files.provider.remote.ParcelableObject; - -interface IRemoteFileService { - IRemoteFileSystemProvider getRemoteFileSystemProviderInterface(String scheme); - - IRemoteFileSystem getRemoteFileSystemInterface(in ParcelableObject fileSystem); - - IRemotePosixFileStore getRemotePosixFileStoreInterface(in ParcelableObject fileStore); - - IRemotePosixFileAttributeView getRemotePosixFileAttributeViewInterface( - in ParcelableObject attributeView - ); - - void setArchivePasswords(in ParcelableObject fileSystem, in List passwords); - - void refreshArchiveFileSystem(in ParcelableObject fileSystem); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystem.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystem.aidl deleted file mode 100644 index 3cfd57bd6..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystem.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableException; - -interface IRemoteFileSystem { - void close(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystemProvider.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystemProvider.aidl deleted file mode 100644 index 8aa36cb04..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystemProvider.aidl +++ /dev/null @@ -1,108 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableCopyOptions; -import me.zhanghai.android.files.provider.remote.ParcelableDirectoryStream; -import me.zhanghai.android.files.provider.remote.ParcelableException; -import me.zhanghai.android.files.provider.remote.ParcelableFileAttributes; -import me.zhanghai.android.files.provider.remote.ParcelableObject; -import me.zhanghai.android.files.provider.remote.ParcelablePathListConsumer; -import me.zhanghai.android.files.provider.remote.ParcelableSerializable; -import me.zhanghai.android.files.provider.remote.RemotePathObservable; -import me.zhanghai.android.files.provider.remote.RemoteInputStream; -import me.zhanghai.android.files.provider.remote.RemoteSeekableByteChannel; -import me.zhanghai.android.files.util.RemoteCallback; - -interface IRemoteFileSystemProvider { - RemoteInputStream newInputStream( - in ParcelableObject file, - in ParcelableSerializable options, - out ParcelableException exception - ); - - RemoteSeekableByteChannel newByteChannel( - in ParcelableObject file, - in ParcelableSerializable options, - in ParcelableFileAttributes attributes, - out ParcelableException exception - ); - - ParcelableDirectoryStream newDirectoryStream( - in ParcelableObject directory, - in ParcelableObject filter, - out ParcelableException exception - ); - - void createDirectory( - in ParcelableObject directory, - in ParcelableFileAttributes attributes, - out ParcelableException exception - ); - - void createSymbolicLink( - in ParcelableObject link, - in ParcelableObject target, - in ParcelableFileAttributes attributes, - out ParcelableException exception - ); - - void createLink( - in ParcelableObject link, - in ParcelableObject existing, - out ParcelableException exception - ); - - void delete(in ParcelableObject path, out ParcelableException exception); - - ParcelableObject readSymbolicLink(in ParcelableObject link, out ParcelableException exception); - - RemoteCallback copy( - in ParcelableObject source, - in ParcelableObject target, - in ParcelableCopyOptions options, - in RemoteCallback callback - ); - - RemoteCallback move( - in ParcelableObject source, - in ParcelableObject target, - in ParcelableCopyOptions options, - in RemoteCallback callback - ); - - boolean isSameFile( - in ParcelableObject path, - in ParcelableObject path2, - out ParcelableException exception - ); - - boolean isHidden(in ParcelableObject path, out ParcelableException exception); - - ParcelableObject getFileStore(in ParcelableObject path, out ParcelableException exception); - - void checkAccess( - in ParcelableObject path, - in ParcelableSerializable modes, - out ParcelableException exception - ); - - ParcelableObject readAttributes( - in ParcelableObject path, - in ParcelableSerializable type, - in ParcelableSerializable options, - out ParcelableException exception - ); - - RemotePathObservable observe( - in ParcelableObject path, - long intervalMillis, - out ParcelableException exception - ); - - RemoteCallback search( - in ParcelableObject directory, - in String query, - long intervalMillis, - in ParcelablePathListConsumer listener, - in RemoteCallback callback - ); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteInputStream.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteInputStream.aidl deleted file mode 100644 index d5f8d02fd..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteInputStream.aidl +++ /dev/null @@ -1,15 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableException; - -interface IRemoteInputStream { - int read(out ParcelableException exception); - - int read2(out byte[] buffer, out ParcelableException exception); - - long skip(long size, out ParcelableException exception); - - int available(out ParcelableException exception); - - void close(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePathObservable.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePathObservable.aidl deleted file mode 100644 index 80b374a75..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePathObservable.aidl +++ /dev/null @@ -1,10 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableException; -import me.zhanghai.android.files.util.RemoteCallback; - -interface IRemotePathObservable { - void addObserver(in RemoteCallback observer); - - void close(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileAttributeView.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileAttributeView.aidl deleted file mode 100644 index 391983553..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileAttributeView.aidl +++ /dev/null @@ -1,29 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.common.ParcelableFileTime; -import me.zhanghai.android.files.provider.common.ParcelablePosixFileMode; -import me.zhanghai.android.files.provider.common.PosixGroup; -import me.zhanghai.android.files.provider.common.PosixUser; -import me.zhanghai.android.files.provider.remote.ParcelableException; -import me.zhanghai.android.files.provider.remote.ParcelableObject; - -interface IRemotePosixFileAttributeView { - ParcelableObject readAttributes(out ParcelableException exception); - - void setTimes( - in ParcelableFileTime lastModifiedTime, - in ParcelableFileTime lastAccessTime, - in ParcelableFileTime createTime, - out ParcelableException exception - ); - - void setOwner(in PosixUser owner, out ParcelableException exception); - - void setGroup(in PosixGroup group, out ParcelableException exception); - - void setMode(in ParcelablePosixFileMode mode, out ParcelableException exception); - - void setSeLinuxContext(in ParcelableObject context, out ParcelableException exception); - - void restoreSeLinuxContext(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileStore.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileStore.aidl deleted file mode 100644 index 239f18715..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileStore.aidl +++ /dev/null @@ -1,13 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableException; - -interface IRemotePosixFileStore { - void setReadOnly(boolean readOnly, out ParcelableException exception); - - long getTotalSpace(out ParcelableException exception); - - long getUsableSpace(out ParcelableException exception); - - long getUnallocatedSpace(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteSeekableByteChannel.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteSeekableByteChannel.aidl deleted file mode 100644 index e24521b45..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteSeekableByteChannel.aidl +++ /dev/null @@ -1,21 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -import me.zhanghai.android.files.provider.remote.ParcelableException; - -interface IRemoteSeekableByteChannel { - int read(out byte[] destination, out ParcelableException exception); - - int write(in byte[] source, out ParcelableException exception); - - long position(out ParcelableException exception); - - void position2(long newPosition, out ParcelableException exception); - - long size(out ParcelableException exception); - - void truncate(long size, out ParcelableException exception); - - void force(boolean metaData, out ParcelableException exception); - - void close(out ParcelableException exception); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableCopyOptions.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableCopyOptions.aidl deleted file mode 100644 index 200d19ba4..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableCopyOptions.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableCopyOptions; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableDirectoryStream.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableDirectoryStream.aidl deleted file mode 100644 index 49f7198f8..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableDirectoryStream.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableDirectoryStream; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableException.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableException.aidl deleted file mode 100644 index 6557a5d19..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableException.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableException; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableFileAttributes.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableFileAttributes.aidl deleted file mode 100644 index f28745488..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableFileAttributes.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableFileAttributes; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableObject.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableObject.aidl deleted file mode 100644 index 374afb9f9..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableObject.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableObject; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelablePathListConsumer.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelablePathListConsumer.aidl deleted file mode 100644 index c3f0451fc..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelablePathListConsumer.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelablePathListConsumer; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableSerializable.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableSerializable.aidl deleted file mode 100644 index 218d0980d..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableSerializable.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable ParcelableSerializable; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteInputStream.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteInputStream.aidl deleted file mode 100644 index 17e7157c1..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteInputStream.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable RemoteInputStream; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemotePathObservable.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemotePathObservable.aidl deleted file mode 100644 index 6f7f2fbfe..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemotePathObservable.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable RemotePathObservable; diff --git a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteSeekableByteChannel.aidl b/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteSeekableByteChannel.aidl deleted file mode 100644 index ff61db32c..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteSeekableByteChannel.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.provider.remote; - -parcelable RemoteSeekableByteChannel; diff --git a/app/src/main/aidl/me/zhanghai/android/files/util/IRemoteCallback.aidl b/app/src/main/aidl/me/zhanghai/android/files/util/IRemoteCallback.aidl deleted file mode 100644 index 2f2495b37..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/util/IRemoteCallback.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package me.zhanghai.android.files.util; - -import android.os.Bundle; - -interface IRemoteCallback { - void sendResult(in Bundle result); -} diff --git a/app/src/main/aidl/me/zhanghai/android/files/util/ParcelSlicedList.aidl b/app/src/main/aidl/me/zhanghai/android/files/util/ParcelSlicedList.aidl deleted file mode 100644 index bc21a9e24..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/util/ParcelSlicedList.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.util; - -parcelable ParcelSlicedList; diff --git a/app/src/main/aidl/me/zhanghai/android/files/util/RemoteCallback.aidl b/app/src/main/aidl/me/zhanghai/android/files/util/RemoteCallback.aidl deleted file mode 100644 index 1ab6add09..000000000 --- a/app/src/main/aidl/me/zhanghai/android/files/util/RemoteCallback.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package me.zhanghai.android.files.util; - -parcelable RemoteCallback; diff --git a/app/src/main/java/androidx/appcompat/app/AppCompatDelegateCompat.java b/app/src/main/java/androidx/appcompat/app/AppCompatDelegateCompat.java deleted file mode 100644 index 4cf01deb4..000000000 --- a/app/src/main/java/androidx/appcompat/app/AppCompatDelegateCompat.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package androidx.appcompat.app; - -import android.annotation.SuppressLint; -import android.content.Context; - -import androidx.annotation.NonNull; - -public class AppCompatDelegateCompat { - private AppCompatDelegateCompat() {} - - @SuppressLint("RestrictedApi") - public static int mapNightMode(@NonNull AppCompatDelegate delegate, @NonNull Context context, - int mode) { - return ((AppCompatDelegateImpl) delegate).mapNightMode(context, mode); - } -} diff --git a/app/src/main/java/androidx/appcompat/widget/FixPaddingListPopupWindow.java b/app/src/main/java/androidx/appcompat/widget/FixPaddingListPopupWindow.java deleted file mode 100644 index 2b7c0cd69..000000000 --- a/app/src/main/java/androidx/appcompat/widget/FixPaddingListPopupWindow.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package androidx.appcompat.widget; - -import android.content.Context; -import android.util.AttributeSet; - -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StyleRes; - -public class FixPaddingListPopupWindow extends ListPopupWindow { - public FixPaddingListPopupWindow(@NonNull Context context) { - super(context); - } - - public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @NonNull - @Override - DropDownListView createDropDownListView(@NonNull Context context, boolean hijackFocus) { - return new FixPaddingDropDownListView(context, hijackFocus); - } - - private static class FixPaddingDropDownListView extends DropDownListView { - public FixPaddingDropDownListView(Context context, boolean hijackFocus) { - super(context, hijackFocus); - } - - // DropDownListView.measureHeightOfChildrenCompat() uses list padding instead of regular - // padding, which isn't initialized before onMeasure() so returns no padding for the first - // time. And ListPopupWindow.buildDropDown() adds the regular padding back every time, which - // will double the padding after the first show. - @Override - public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition, - int endPosition, int maxHeight, - int disallowPartialChildPosition) { - int height = super.measureHeightOfChildrenCompat(widthMeasureSpec, startPosition, - endPosition, maxHeight, disallowPartialChildPosition); - height -= getListPaddingTop() + getListPaddingBottom(); - return height; - } - } -} diff --git a/app/src/main/java/androidx/swiperefreshlayout/widget/ThemedSwipeRefreshLayout.java b/app/src/main/java/androidx/swiperefreshlayout/widget/ThemedSwipeRefreshLayout.java deleted file mode 100644 index 49ebf9466..000000000 --- a/app/src/main/java/androidx/swiperefreshlayout/widget/ThemedSwipeRefreshLayout.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package androidx.swiperefreshlayout.widget; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.drawable.ShapeDrawable; -import android.util.AttributeSet; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.graphics.ColorUtils; -import me.zhanghai.android.files.R; -import me.zhanghai.android.files.compat.ContextCompatKt; -import me.zhanghai.android.files.util.ContextExtensionsKt; - -public class ThemedSwipeRefreshLayout extends SwipeRefreshLayout { - public ThemedSwipeRefreshLayout(@NonNull Context context) { - super(context); - - init(); - } - - public ThemedSwipeRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - - init(); - } - - private void init() { - Context context = getContext(); - boolean isMaterial3Theme = ContextExtensionsKt.isMaterial3Theme(context); - int backgroundColor; - if (isMaterial3Theme) { - int surfaceColor = ContextExtensionsKt.getColorByAttr(context, - com.google.android.material.R.attr.colorSurface); - @SuppressLint("PrivateResource") - int overlayColor = ContextCompatKt.getColorCompat(context, - com.google.android.material.R.color.m3_popupmenu_overlay_color); - backgroundColor = ColorUtils.compositeColors(overlayColor, surfaceColor); - } else { - backgroundColor = ContextExtensionsKt.getColorByAttr(context, - androidx.appcompat.R.attr.colorBackgroundFloating); - } - ((ShapeDrawable) mCircleView.getBackground()).getPaint().setColor(backgroundColor); - setColorSchemeColors(ContextExtensionsKt.getColorByAttr(context, - androidx.appcompat.R.attr.colorAccent)); - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - - View child = getChildView(); - if (child != null) { - measureChild(child, widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(child.getMeasuredWidth() + getPaddingLeft() + getPaddingRight(), - child.getMeasuredHeight() + getPaddingTop() + getPaddingBottom()); - } - } - - @Nullable - private View getChildView() { - for (int i = 0; i < getChildCount(); ++i) { - View child = getChildAt(i); - if (!child.equals(mCircleView)) { - return child; - } - } - return null; - } -} diff --git a/app/src/main/java/at/bitfire/dav4jvm/DavResourceAccessor.java b/app/src/main/java/at/bitfire/dav4jvm/DavResourceAccessor.java deleted file mode 100644 index 0aa812dba..000000000 --- a/app/src/main/java/at/bitfire/dav4jvm/DavResourceAccessor.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024 Hai Zhang - * All Rights Reserved. - */ - -package at.bitfire.dav4jvm; - -import java.io.IOException; - -import androidx.annotation.NonNull; -import at.bitfire.dav4jvm.exception.DavException; -import at.bitfire.dav4jvm.exception.HttpException; -import kotlin.jvm.functions.Function0; -import okhttp3.Response; - -public class DavResourceAccessor { - private DavResourceAccessor() {} - - public static void checkStatus(@NonNull DavResource davResource, @NonNull Response response) - throws HttpException { - davResource.checkStatus(response); - } - - public static Response followRedirects(@NonNull DavResource davResource, - @NonNull Function0 sendRequest) throws DavException, IOException { - return davResource.followRedirects$build(sendRequest); - } -} diff --git a/app/src/main/java/com/google/android/material/appbar/OnWindowInsetChangedAppBarLayout.java b/app/src/main/java/com/google/android/material/appbar/OnWindowInsetChangedAppBarLayout.java deleted file mode 100644 index df8ca8397..000000000 --- a/app/src/main/java/com/google/android/material/appbar/OnWindowInsetChangedAppBarLayout.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2022 Hai Zhang - * All Rights Reserved. - */ - -package com.google.android.material.appbar; - -import android.content.Context; -import android.util.AttributeSet; - -import androidx.annotation.AttrRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.WindowInsetsCompat; - -public class OnWindowInsetChangedAppBarLayout extends AppBarLayout { - public OnWindowInsetChangedAppBarLayout(@NonNull Context context) { - super(context); - } - - public OnWindowInsetChangedAppBarLayout(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public OnWindowInsetChangedAppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs, - @AttrRes int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public WindowInsetsCompat onWindowInsetChanged(@NonNull WindowInsetsCompat insets) { - return super.onWindowInsetChanged(insets); - } -} diff --git a/app/src/main/java/com/google/android/material/shape/MaterialShapeDrawableAccessor.java b/app/src/main/java/com/google/android/material/shape/MaterialShapeDrawableAccessor.java deleted file mode 100644 index ed44d116f..000000000 --- a/app/src/main/java/com/google/android/material/shape/MaterialShapeDrawableAccessor.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package com.google.android.material.shape; - -import android.annotation.SuppressLint; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.android.material.elevation.ElevationOverlayProvider; - -public class MaterialShapeDrawableAccessor { - private MaterialShapeDrawableAccessor() {} - - @SuppressLint("RestrictedApi") - public static ElevationOverlayProvider getElevationOverlayProvider( - @NonNull MaterialShapeDrawable drawable) { - MaterialShapeDrawable.MaterialShapeDrawableState drawableState = - (MaterialShapeDrawable.MaterialShapeDrawableState) drawable.getConstantState(); - return drawableState.elevationOverlayProvider; - } - - @SuppressLint("RestrictedApi") - public static void setElevationOverlayProvider( - @NonNull MaterialShapeDrawable drawable, - @Nullable ElevationOverlayProvider elevationOverlayProvider) { - MaterialShapeDrawable.MaterialShapeDrawableState drawableState = - (MaterialShapeDrawable.MaterialShapeDrawableState) drawable.getConstantState(); - drawableState.elevationOverlayProvider = elevationOverlayProvider; - } - - public static void updateZ(@NonNull MaterialShapeDrawable drawable) { - final float parentAbsoluteElevation = drawable.getParentAbsoluteElevation(); - drawable.setParentAbsoluteElevation(parentAbsoluteElevation + 1); - drawable.setParentAbsoluteElevation(parentAbsoluteElevation); - } -} diff --git a/app/src/main/java/com/hierynomus/smbj/share/FileAccessor.java b/app/src/main/java/com/hierynomus/smbj/share/FileAccessor.java deleted file mode 100644 index 9b82fe24a..000000000 --- a/app/src/main/java/com/hierynomus/smbj/share/FileAccessor.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package com.hierynomus.smbj.share; - -import com.hierynomus.mssmb2.messages.SMB2ReadResponse; -import com.hierynomus.smbj.common.SMBRuntimeException; - -import java.util.concurrent.Future; - -import androidx.annotation.NonNull; - -public class FileAccessor { - private FileAccessor() {} - - /** - * @see File#readAsync(long, int) - */ - @NonNull - public static Future readAsync(@NonNull File file, long offset, int length) - throws SMBRuntimeException { - return file.readAsync(offset, length); - } -} diff --git a/app/src/main/java/com/hierynomus/smbj/share/ShareAccessor.java b/app/src/main/java/com/hierynomus/smbj/share/ShareAccessor.java deleted file mode 100644 index 9e7216c9d..000000000 --- a/app/src/main/java/com/hierynomus/smbj/share/ShareAccessor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package com.hierynomus.smbj.share; - -import androidx.annotation.NonNull; - -import com.hierynomus.mssmb2.SMB2FileId; -import com.hierynomus.mssmb2.messages.SMB2IoctlResponse; -import com.hierynomus.smbj.io.ArrayByteChunkProvider; -import com.hierynomus.smbj.io.ByteChunkProvider; - -import java.util.concurrent.Future; - -public class ShareAccessor { - private ShareAccessor() {} - - /** - * This ioctl() variant allows passing in the {@param statusHandler}. - * - * @see Share#ioctl(com.hierynomus.mssmb2.SMB2FileId, long, boolean, ByteChunkProvider, int) - */ - @NonNull - public static SMB2IoctlResponse ioctl(@NonNull Share share, @NonNull SMB2FileId fileId, - long ctlCode, boolean isFsCtl, @NonNull byte[] inData, - int inOffset, int inLength, - @NonNull StatusHandler statusHandler, long timeout) { - final ByteChunkProvider inputData = new ArrayByteChunkProvider(inData, inOffset, inLength, - 0); - final Future future = share.ioctlAsync(fileId, ctlCode, isFsCtl, - inputData, -1); - return share.receive(future, "IOCTL", fileId, statusHandler, timeout); - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/about/AboutActivity.kt b/app/src/main/java/me/zhanghai/android/files/about/AboutActivity.kt deleted file mode 100644 index 2b73b19d9..000000000 --- a/app/src/main/java/me/zhanghai/android/files/about/AboutActivity.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.about - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.add -import androidx.fragment.app.commit -import me.zhanghai.android.files.app.AppActivity - -class AboutActivity : AppActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // Calls ensureSubDecor(). - findViewById(android.R.id.content) - if (savedInstanceState == null) { - supportFragmentManager.commit { add(android.R.id.content) } - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/about/AboutFragment.kt b/app/src/main/java/me/zhanghai/android/files/about/AboutFragment.kt deleted file mode 100644 index e93a4c988..000000000 --- a/app/src/main/java/me/zhanghai/android/files/about/AboutFragment.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.about - -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.isVisible -import androidx.fragment.app.Fragment -import me.zhanghai.android.files.databinding.AboutFragmentBinding -import me.zhanghai.android.files.ui.LicensesDialogFragment -import me.zhanghai.android.files.util.createViewIntent -import me.zhanghai.android.files.util.startActivitySafe - -class AboutFragment : Fragment() { - private lateinit var binding: AboutFragmentBinding - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View = - AboutFragmentBinding.inflate(inflater, container, false) - .also { binding = it } - .root - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - - val activity = requireActivity() as AppCompatActivity - activity.setSupportActionBar(binding.toolbar) - activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true) - binding.gitHubLayout.setOnClickListener { startActivitySafe(GITHUB_URI.createViewIntent()) } - binding.licensesLayout.setOnClickListener { LicensesDialogFragment.show(this) } -//#ifdef NONFREE - binding.privacyPolicyLayout.isVisible = true - binding.privacyPolicyLayout.setOnClickListener { - startActivitySafe(PRIVACY_POLICY_URI.createViewIntent()) - } -//#endif - binding.authorNameLayout.setOnClickListener { - startActivitySafe(AUTHOR_RESUME_URI.createViewIntent()) - } - binding.authorGitHubLayout.setOnClickListener { - startActivitySafe(AUTHOR_GITHUB_URI.createViewIntent()) - } - binding.authorTwitterLayout.setOnClickListener { - startActivitySafe(AUTHOR_TWITTER_URI.createViewIntent()) - } - } - - companion object { - private val GITHUB_URI = Uri.parse("https://github.com/zhanghai/MaterialFiles") - private val PRIVACY_POLICY_URI = - Uri.parse("https://github.com/zhanghai/MaterialFiles/blob/master/PRIVACY.md") - private val AUTHOR_RESUME_URI = Uri.parse("https://resume.zhanghai.me/") - private val AUTHOR_GITHUB_URI = Uri.parse("https://github.com/zhanghai") - private val AUTHOR_TWITTER_URI = Uri.parse("https://twitter.com/zhanghai95") - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/AppActivity.kt b/app/src/main/java/me/zhanghai/android/files/app/AppActivity.kt deleted file mode 100644 index ef43a14df..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/AppActivity.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.app.AppCompatDelegate -import me.zhanghai.android.files.theme.custom.CustomThemeHelper -import me.zhanghai.android.files.theme.night.NightModeHelper - -abstract class AppActivity : AppCompatActivity() { - private var isDelegateCreated = false - - override fun getDelegate(): AppCompatDelegate { - val delegate = super.getDelegate() - - if (!isDelegateCreated) { - isDelegateCreated = true - NightModeHelper.apply(this) - } - return delegate - } - - override fun onCreate(savedInstanceState: Bundle?) { - CustomThemeHelper.apply(this) - - super.onCreate(savedInstanceState) - } - - override fun onSupportNavigateUp(): Boolean { - if (!super.onSupportNavigateUp()) { - finish() - } - return true - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/AppInitializers.kt b/app/src/main/java/me/zhanghai/android/files/app/AppInitializers.kt deleted file mode 100644 index e4faa9d10..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/AppInitializers.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.os.AsyncTask -import android.os.Build -import android.webkit.WebView -import com.jakewharton.threetenabp.AndroidThreeTen -import jcifs.context.SingletonContext -import me.zhanghai.android.files.BuildConfig -import me.zhanghai.android.files.coil.initializeCoil -import me.zhanghai.android.files.filejob.fileJobNotificationTemplate -import me.zhanghai.android.files.ftpserver.ftpServerServiceNotificationTemplate -import me.zhanghai.android.files.hiddenapi.HiddenApi -import me.zhanghai.android.files.provider.FileSystemProviders -import me.zhanghai.android.files.settings.Settings -import me.zhanghai.android.files.storage.FtpServerAuthenticator -import me.zhanghai.android.files.storage.SftpServerAuthenticator -import me.zhanghai.android.files.storage.SmbServerAuthenticator -import me.zhanghai.android.files.storage.StorageVolumeListLiveData -import me.zhanghai.android.files.storage.WebDavServerAuthenticator -import me.zhanghai.android.files.theme.custom.CustomThemeHelper -import me.zhanghai.android.files.theme.night.NightModeHelper -import java.util.Properties -import me.zhanghai.android.files.provider.ftp.client.Client as FtpClient -import me.zhanghai.android.files.provider.sftp.client.Client as SftpClient -import me.zhanghai.android.files.provider.smb.client.Client as SmbClient -import me.zhanghai.android.files.provider.webdav.client.Client as WebDavClient - -val appInitializers = listOf( - ::initializeCrashlytics, ::disableHiddenApiChecks, ::initializeThreeTen, - ::initializeWebViewDebugging, ::initializeCoil, ::initializeFileSystemProviders, ::upgradeApp, - ::initializeLiveDataObjects, ::initializeCustomTheme, ::initializeNightMode, - ::createNotificationChannels -) - -private fun initializeCrashlytics() { -//#ifdef NONFREE - me.zhanghai.android.files.nonfree.CrashlyticsInitializer.initialize() -//#endif -} - -private fun disableHiddenApiChecks() { - HiddenApi.disableHiddenApiChecks() -} - -private fun initializeThreeTen() { - AndroidThreeTen.init(application) -} - -private fun initializeWebViewDebugging() { - if (BuildConfig.DEBUG) { - WebView.setWebContentsDebuggingEnabled(true) - } -} - -private fun initializeFileSystemProviders() { - FileSystemProviders.install() - FileSystemProviders.overflowWatchEvents = true - // SingletonContext.init() calls NameServiceClientImpl.initCache() which connects to network. - AsyncTask.THREAD_POOL_EXECUTOR.execute { - SingletonContext.init( - Properties().apply { - setProperty("jcifs.netbios.cachePolicy", "0") - setProperty("jcifs.smb.client.maxVersion", "SMB1") - } - ) - } - FtpClient.authenticator = FtpServerAuthenticator - SftpClient.authenticator = SftpServerAuthenticator - SmbClient.authenticator = SmbServerAuthenticator - WebDavClient.authenticator = WebDavServerAuthenticator -} - -private fun initializeLiveDataObjects() { - // Force initialization of LiveData objects so that it won't happen on a background thread. - StorageVolumeListLiveData.value - Settings.FILE_LIST_DEFAULT_DIRECTORY.value -} - -private fun initializeCustomTheme() { - CustomThemeHelper.initialize(application) -} - -private fun initializeNightMode() { - NightModeHelper.initialize(application) -} - -private fun createNotificationChannels() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - notificationManager.createNotificationChannels( - listOf( - backgroundActivityStartNotificationTemplate.channelTemplate, - fileJobNotificationTemplate.channelTemplate, - ftpServerServiceNotificationTemplate.channelTemplate - ).map { it.create(application) } - ) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/AppProvider.kt b/app/src/main/java/me/zhanghai/android/files/app/AppProvider.kt deleted file mode 100644 index 87c347bc1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/AppProvider.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.app.Application -import android.content.ContentProvider -import android.content.ContentValues -import android.database.Cursor -import android.net.Uri - -lateinit var application: Application private set - -class AppProvider : ContentProvider() { - override fun onCreate(): Boolean { - application = context as Application - appInitializers.forEach { it() } - return true - } - - override fun query( - uri: Uri, - projection: Array?, - selection: String?, - selectionArgs: Array?, - sortOrder: String? - ): Cursor? { - throw UnsupportedOperationException() - } - - override fun getType(uri: Uri): String? { - throw UnsupportedOperationException() - } - - override fun insert(uri: Uri, values: ContentValues?): Uri? { - throw UnsupportedOperationException() - } - - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { - throw UnsupportedOperationException() - } - - override fun update( - uri: Uri, - values: ContentValues?, - selection: String?, - selectionArgs: Array? - ): Int { - throw UnsupportedOperationException() - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/AppUpgrader.kt b/app/src/main/java/me/zhanghai/android/files/app/AppUpgrader.kt deleted file mode 100644 index 5faea314b..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/AppUpgrader.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import androidx.core.content.edit -import me.zhanghai.android.files.BuildConfig - -private const val KEY_VERSION_CODE = "key_version_code" - -private const val VERSION_CODE_BELOW_1_1_0 = 17 -private const val VERSION_CODE_1_1_0 = 18 -private const val VERSION_CODE_1_2_0 = 22 -private const val VERSION_CODE_1_3_0 = 24 -private const val VERSION_CODE_1_4_0 = 26 -private const val VERSION_CODE_1_5_0 = 29 -private const val VERSION_CODE_1_6_0 = 32 -private const val VERSION_CODE_1_7_2 = 37 -private const val VERSION_CODE_LATEST = BuildConfig.VERSION_CODE - -private var lastVersionCode: Int - get() { - if (defaultSharedPreferences.all.isEmpty()) { - // This is a new install. - lastVersionCode = VERSION_CODE_LATEST - return VERSION_CODE_LATEST - } - return defaultSharedPreferences.getInt(KEY_VERSION_CODE, VERSION_CODE_BELOW_1_1_0) - } - set(value) { - defaultSharedPreferences.edit { putInt(KEY_VERSION_CODE, value) } - } - -fun upgradeApp() { - upgradeAppFrom(lastVersionCode) - lastVersionCode = VERSION_CODE_LATEST -} - -private fun upgradeAppFrom(lastVersionCode: Int) { - if (lastVersionCode < VERSION_CODE_1_1_0) { - upgradeAppTo1_1_0() - } - if (lastVersionCode < VERSION_CODE_1_2_0) { - upgradeAppTo1_2_0() - } - if (lastVersionCode < VERSION_CODE_1_3_0) { - upgradeAppTo1_3_0() - } - if (lastVersionCode < VERSION_CODE_1_4_0) { - upgradeAppTo1_4_0() - } - if (lastVersionCode < VERSION_CODE_1_5_0) { - upgradeAppTo1_5_0() - } - if (lastVersionCode < VERSION_CODE_1_6_0) { - upgradeAppTo1_6_0() - } - if (lastVersionCode < VERSION_CODE_1_7_2) { - upgradeAppTo1_7_2() - } - // Continue with new `if`s on lastVersionCode instead of `else if`. -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/AppUpgraders.kt b/app/src/main/java/me/zhanghai/android/files/app/AppUpgraders.kt deleted file mode 100644 index b6b24c7fd..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/AppUpgraders.kt +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.content.SharedPreferences -import android.net.Uri -import android.os.Build -import android.os.Parcel -import android.os.Parcelable -import androidx.annotation.StringRes -import androidx.core.content.edit -import me.zhanghai.android.files.R -import me.zhanghai.android.files.compat.PreferenceManagerCompat -import me.zhanghai.android.files.compat.getDescriptionCompat -import me.zhanghai.android.files.compat.readBooleanCompat -import me.zhanghai.android.files.compat.writeBooleanCompat -import me.zhanghai.android.files.compat.writeParcelableListCompat -import me.zhanghai.android.files.file.DocumentTreeUri -import me.zhanghai.android.files.file.asExternalStorageUriOrNull -import me.zhanghai.android.files.file.displayName -import me.zhanghai.android.files.file.storageVolume -import me.zhanghai.android.files.filelist.FileSortOptions -import me.zhanghai.android.files.navigation.BookmarkDirectory -import me.zhanghai.android.files.navigation.StandardDirectorySettings -import me.zhanghai.android.files.provider.archive.ArchiveFileSystem -import me.zhanghai.android.files.provider.common.ByteString -import me.zhanghai.android.files.provider.common.moveToByteString -import me.zhanghai.android.files.provider.content.ContentFileSystem -import me.zhanghai.android.files.provider.document.DocumentFileSystem -import me.zhanghai.android.files.provider.document.resolver.ExternalStorageProviderHacks -import me.zhanghai.android.files.provider.linux.LinuxFileSystem -import me.zhanghai.android.files.provider.root.RootStrategy -import me.zhanghai.android.files.provider.sftp.SftpFileSystem -import me.zhanghai.android.files.provider.smb.SmbFileSystem -import me.zhanghai.android.files.storage.DocumentTree -import me.zhanghai.android.files.storage.FileSystemRoot -import me.zhanghai.android.files.storage.PrimaryStorageVolume -import me.zhanghai.android.files.util.StableUriParceler -import me.zhanghai.android.files.util.asBase64 -import me.zhanghai.android.files.util.readParcelable -import me.zhanghai.android.files.util.readParcelableListCompat -import me.zhanghai.android.files.util.toBase64 -import me.zhanghai.android.files.util.toByteArray -import me.zhanghai.android.files.util.use - -internal fun upgradeAppTo1_1_0() { - // Migrate settings. - migratePathSetting1_1_0(R.string.pref_key_file_list_default_directory) - migrateFileSortOptionsSetting1_1_0() - migrateCreateArchiveTypeSetting1_1_0() - migrateStandardDirectorySettingsSetting1_1_0() - migrateBookmarkDirectoriesSetting1_1_0() - migratePathSetting1_1_0(R.string.pref_key_ftp_server_home_directory) - for (key in pathSharedPreferences.all.keys) { - migrateFileSortOptionsSetting1_1_0(pathSharedPreferences, key) - } -} - -private const val PARCEL_VAL_PARCELABLE = 4 -private const val PARCEL_VAL_LIST = 11 - -private fun migratePathSetting1_1_0(@StringRes keyRes: Int) { - val key = application.getString(keyRes) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - migratePath1_1_0(oldParcel, newParcel) - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun migrateFileSortOptionsSetting1_1_0() { - migrateFileSortOptionsSetting1_1_0( - defaultSharedPreferences, application.getString(R.string.pref_key_file_list_sort_options) - ) -} - -private fun migrateFileSortOptionsSetting1_1_0(sharedPreferences: SharedPreferences, key: String) { - val oldBytes = sharedPreferences.getString(key, null)?.asBase64()?.toByteArray() ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeString(oldParcel.readString()) - newParcel.writeString(FileSortOptions.By.entries[oldParcel.readInt()].name) - newParcel.writeString(FileSortOptions.Order.entries[oldParcel.readInt()].name) - newParcel.writeInt(oldParcel.readByte().toInt()) - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - sharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -fun migrateCreateArchiveTypeSetting1_1_0() { - val key = application.getString(R.string.pref_key_create_archive_type) - val oldValue = defaultSharedPreferences.getString(key, null) ?: return - val newValue = oldValue.replace(Regex("type_.+$")) { - when (it.value) { - "type_zip" -> "zipRadio" - "type_tar_xz" -> "tarXzRadio" - "type_seven_z" -> "sevenZRadio" - else -> "zipRadio" - } - } - defaultSharedPreferences.edit { putString(key, newValue) } -} - -private fun migrateStandardDirectorySettingsSetting1_1_0() { - val key = application.getString(R.string.pref_key_standard_directory_settings) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - newParcel.writeInt(PARCEL_VAL_LIST) - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - oldParcel.readInt() - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - newParcel.writeString(StandardDirectorySettings::class.java.name) - newParcel.writeString(oldParcel.readString()) - newParcel.writeString(oldParcel.readString()) - newParcel.writeInt(oldParcel.readByte().toInt()) - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun migrateBookmarkDirectoriesSetting1_1_0() { - val key = application.getString(R.string.pref_key_bookmark_directories) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - newParcel.writeInt(PARCEL_VAL_LIST) - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - oldParcel.readInt() - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - newParcel.writeString(BookmarkDirectory::class.java.name) - newParcel.writeLong(oldParcel.readLong()) - newParcel.writeString(oldParcel.readString()) - migratePath1_1_0(oldParcel, newParcel) - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private val oldByteStringCreator = object : Parcelable.Creator { - override fun createFromParcel(source: Parcel): ByteString = - source.createByteArray()!!.moveToByteString() - - override fun newArray(size: Int): Array = arrayOfNulls(size) -} - -private fun migratePath1_1_0(oldParcel: Parcel, newParcel: Parcel) { - val className = oldParcel.readString() - newParcel.writeString(className) - newParcel.writeByte(oldParcel.readByte()) - newParcel.writeBooleanCompat(oldParcel.readByte() != 0.toByte()) - newParcel.writeParcelableListCompat(oldParcel.createTypedArrayList(oldByteStringCreator), 0) - when (className) { - "me.zhanghai.android.files.provider.archive.ArchivePath" -> { - oldParcel.readString() - newParcel.writeString(ArchiveFileSystem::class.java.name) - migratePath1_1_0(oldParcel, newParcel) - } - "me.zhanghai.android.files.provider.content.ContentPath" -> { - oldParcel.readString() - newParcel.writeString(ContentFileSystem::class.java.name) - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - } - "me.zhanghai.android.files.provider.document.DocumentPath" -> { - oldParcel.readString() - newParcel.writeString(DocumentFileSystem::class.java.name) - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - } - "me.zhanghai.android.files.provider.linux.LinuxPath" -> { - oldParcel.readString() - newParcel.writeString(LinuxFileSystem::class.java.name) - newParcel.writeBooleanCompat(oldParcel.readByte() != 0.toByte()) - } - else -> throw IllegalStateException(className) - } -} - -private val pathSharedPreferences: SharedPreferences - get() { - val name = "${PreferenceManagerCompat.getDefaultSharedPreferencesName(application)}_path" - val mode = PreferenceManagerCompat.defaultSharedPreferencesMode - return application.getSharedPreferences(name, mode) - } - -internal fun upgradeAppTo1_2_0() { - migrateStoragesSetting1_2_0() -} - -private fun migrateStoragesSetting1_2_0() { - val key = application.getString(R.string.pref_key_storages) - val storages = (listOf(FileSystemRoot(null, true), PrimaryStorageVolume(null, true)) - + DocumentTreeUri.persistedUris.map { - DocumentTree( - null, it.storageVolume?.getDescriptionCompat(application) ?: it.displayName - ?: it.value.toString(), it - ) - }) - val bytes = Parcel.obtain().use { parcel -> - parcel.writeValue(storages) - parcel.marshall() - } - defaultSharedPreferences.edit { putString(key, bytes.toBase64().value) } -} - -internal fun upgradeAppTo1_3_0() { - migrateSmbServersSetting1_3_0() -} - -private fun migrateSmbServersSetting1_3_0() { - val key = application.getString(R.string.pref_key_storages) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - newParcel.writeInt(PARCEL_VAL_LIST) - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - val oldPosition = oldParcel.dataPosition() - oldParcel.readInt() - val className = oldParcel.readString() - if (className == "me.zhanghai.android.files.storage.SmbServer") { - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - newParcel.writeString("me.zhanghai.android.files.storage.SmbServer") - val id = oldParcel.readLong() - newParcel.writeLong(id) - val customName = oldParcel.readString() - newParcel.writeString(customName) - oldParcel.readString() - newParcel.writeString( - "me.zhanghai.android.files.provider.smb.client.Authority" - ) - val authorityHost = oldParcel.readString() - newParcel.writeString(authorityHost) - val authorityPort = oldParcel.readInt() - newParcel.writeInt(authorityPort) - oldParcel.readString() - newParcel.writeString( - "me.zhanghai.android.files.provider.smb.client.Authentication" - ) - val authenticationUsername = oldParcel.readString() - newParcel.writeString(authenticationUsername) - val authenticationDomain = oldParcel.readString() - newParcel.writeString(authenticationDomain) - val authenticationPassword = oldParcel.readString() - newParcel.writeString(authenticationPassword) - val relativePath = "" - newParcel.writeString(relativePath) - } else { - oldParcel.setDataPosition(oldPosition) - val storage = oldParcel.readValue(appClassLoader) - newParcel.writeValue(storage) - } - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -internal fun upgradeAppTo1_4_0() { - migratePathSetting1_4_0(R.string.pref_key_file_list_default_directory) - migrateSftpServersSetting1_4_0() - migrateBookmarkDirectoriesSetting1_4_0() - migrateRootStrategySetting1_4_0() - migratePathSetting1_4_0(R.string.pref_key_ftp_server_home_directory) -} - -private fun migratePathSetting1_4_0(@StringRes keyRes: Int) { - val key = application.getString(keyRes) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeInt(oldParcel.readInt()) - migratePath1_4_0(oldParcel, newParcel) - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun migrateBookmarkDirectoriesSetting1_4_0() { - val key = application.getString(R.string.pref_key_bookmark_directories) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeInt(oldParcel.readInt()) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - newParcel.writeInt(oldParcel.readInt()) - newParcel.writeString(oldParcel.readString()) - newParcel.writeLong(oldParcel.readLong()) - newParcel.writeString(oldParcel.readString()) - migratePath1_4_0(oldParcel, newParcel) - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun migratePath1_4_0(oldParcel: Parcel, newParcel: Parcel) { - val className = oldParcel.readString() - newParcel.writeString(className) - newParcel.writeByte(oldParcel.readByte()) - newParcel.writeBooleanCompat(oldParcel.readBooleanCompat()) - newParcel.writeParcelableListCompat(oldParcel.readParcelableListCompat(), 0) - when (className) { - "me.zhanghai.android.files.provider.archive.ArchivePath" -> { - newParcel.writeString(oldParcel.readString()) - migratePath1_4_0(oldParcel, newParcel) - } - "me.zhanghai.android.files.provider.content.ContentPath" -> { - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - } - "me.zhanghai.android.files.provider.document.DocumentPath" -> - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - "me.zhanghai.android.files.provider.linux.LinuxPath" -> { - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - oldParcel.readBooleanCompat() - } - "me.zhanghai.android.files.provider.sftp.SftpPath" -> - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - "me.zhanghai.android.files.provider.smb.SmbPath" -> - newParcel.writeParcelable(oldParcel.readParcelable(), 0) - else -> throw IllegalStateException(className) - } -} - -private fun migrateSftpServersSetting1_4_0() { - val key = application.getString(R.string.pref_key_storages) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeInt(oldParcel.readInt()) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - val oldPosition = oldParcel.dataPosition() - oldParcel.readInt() - when (oldParcel.readString()) { - "me.zhanghai.android.files.storage.SftpServer" -> { - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - newParcel.writeString("me.zhanghai.android.files.storage.SftpServer") - val id = oldParcel.readLong() - newParcel.writeLong(id) - val customName = oldParcel.readString() - newParcel.writeString(customName) - val authorityHost = oldParcel.readString() - newParcel.writeString(authorityHost) - val authorityPort = oldParcel.readInt() - newParcel.writeInt(authorityPort) - val authenticationClassName = oldParcel.readString() - val authorityUsername = oldParcel.readString() - newParcel.writeString(authorityUsername) - newParcel.writeString(authenticationClassName) - val authenticationPasswordOrPrivateKey = oldParcel.readString() - newParcel.writeString(authenticationPasswordOrPrivateKey) - val relativePath = oldParcel.readString() - newParcel.writeString(relativePath) - } - else -> { - oldParcel.setDataPosition(oldPosition) - val storage = oldParcel.readValue(appClassLoader) - newParcel.writeValue(storage) - } - } - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun migrateRootStrategySetting1_4_0() { - val key = application.getString(R.string.pref_key_root_strategy) - val oldValue = defaultSharedPreferences.getString(key, null)?.toInt() ?: return - val newValue = when (oldValue) { - 0 -> RootStrategy.NEVER - 3 -> RootStrategy.ALWAYS - else -> RootStrategy.AUTOMATIC - }.ordinal.toString() - defaultSharedPreferences.edit { putString(key, newValue) } -} - -internal fun upgradeAppTo1_5_0() { - migrateSftpServersSetting1_5_0() -} - -private fun migrateSftpServersSetting1_5_0() { - val key = application.getString(R.string.pref_key_storages) - val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() - ?: return - val newBytes = try { - Parcel.obtain().use { newParcel -> - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeInt(oldParcel.readInt()) - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - val oldPosition = oldParcel.dataPosition() - oldParcel.readInt() - when (oldParcel.readString()) { - "me.zhanghai.android.files.storage.SftpServer" -> { - newParcel.writeInt(PARCEL_VAL_PARCELABLE) - newParcel.writeString("me.zhanghai.android.files.storage.SftpServer") - val id = oldParcel.readLong() - newParcel.writeLong(id) - val customName = oldParcel.readString() - newParcel.writeString(customName) - val authorityHost = oldParcel.readString() - newParcel.writeString(authorityHost) - val authorityPort = oldParcel.readInt() - newParcel.writeInt(authorityPort) - val authorityUsername = oldParcel.readString() - newParcel.writeString(authorityUsername) - val authenticationClassName = oldParcel.readString() - newParcel.writeString(authenticationClassName) - val authenticationPasswordOrPrivateKey = oldParcel.readString() - newParcel.writeString(authenticationPasswordOrPrivateKey) - if (authenticationClassName == "me.zhanghai.android.files.provider.sftp" - + ".client.PublicKeyAuthentication") { - newParcel.writeString(null) - } - val relativePath = oldParcel.readString() - newParcel.writeString(relativePath) - } - else -> { - oldParcel.setDataPosition(oldPosition) - val storage = oldParcel.readValue(appClassLoader) - newParcel.writeValue(storage) - } - } - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -internal fun upgradeAppTo1_6_0() { - addViewTypePathSetting1_6_0() -} - -private fun addViewTypePathSetting1_6_0() { - val keys = pathSharedPreferences.all.keys.toSet() - val sortOptionsKey = application.getString(R.string.pref_key_file_list_sort_options) - val viewTypeKey = application.getString(R.string.pref_key_file_list_view_type) - val defaultViewType = application.getString(R.string.pref_default_value_file_list_view_type) - for (key in keys) { - if (!key.startsWith(sortOptionsKey)) { - continue - } - val newKey = key.replaceFirst(sortOptionsKey, viewTypeKey) - if (newKey in keys) { - continue - } - pathSharedPreferences.edit { putString(newKey, defaultViewType) } - } -} - -internal fun upgradeAppTo1_7_2() { - migrateDocumentManagerShortcutSetting1_7_2() -} - -private fun migrateDocumentManagerShortcutSetting1_7_2() { - val key = application.getString(R.string.pref_key_storages) - val oldBytes = - defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() ?: return - val newBytes = - try { - Parcel.obtain().use { newParcel -> - Parcel.obtain().use { oldParcel -> - oldParcel.unmarshall(oldBytes, 0, oldBytes.size) - oldParcel.setDataPosition(0) - newParcel.writeInt(oldParcel.readInt()) - readWriteLengthPrefixedValue(oldParcel, newParcel) { - val size = oldParcel.readInt() - newParcel.writeInt(size) - repeat(size) { - val oldPosition = oldParcel.dataPosition() - oldParcel.readInt() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - // Skip prefix length. - oldParcel.readInt() - } - val className = oldParcel.readString() - oldParcel.setDataPosition(oldPosition) - when (className) { - "me.zhanghai.android.files.storage.DocumentManagerShortcut" -> { - newParcel.writeInt(oldParcel.readInt()) - readWriteLengthPrefixedValue(oldParcel, newParcel) { - oldParcel.readString() - newParcel.writeString( - "me.zhanghai.android.files.storage" + - ".ExternalStorageShortcut" - ) - val id = oldParcel.readLong() - newParcel.writeLong(id) - val customName = oldParcel.readString() - newParcel.writeString(customName) - var uri = StableUriParceler.create(oldParcel)!! - if (uri.asExternalStorageUriOrNull() == null) { - // Reset to a valid external storage URI. - uri = - ExternalStorageProviderHacks - .DOCUMENT_URI_ANDROID_DATA - } - with(StableUriParceler) { uri.write(newParcel, 0) } - } - } - else -> { - val storage = oldParcel.readValue(appClassLoader) - newParcel.writeValue(storage) - } - } - } - } - } - newParcel.marshall() - } - } catch (e: Exception) { - e.printStackTrace() - null - } - defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) } -} - -private fun readWriteLengthPrefixedValue(oldParcel: Parcel, newParcel: Parcel, block: () -> Unit) { - var lengthPosition = 0 - var startPosition = 0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - oldParcel.readInt() - lengthPosition = newParcel.dataPosition() - newParcel.writeInt(-1) - startPosition = newParcel.dataPosition() - } - block() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - val endPosition = newParcel.dataPosition() - newParcel.setDataPosition(lengthPosition) - newParcel.writeInt(endPosition - startPosition) - newParcel.setDataPosition(endPosition) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/BackgroundActivityStarter.kt b/app/src/main/java/me/zhanghai/android/files/app/BackgroundActivityStarter.kt deleted file mode 100644 index 415d5aadf..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/BackgroundActivityStarter.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.os.Build -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.ProcessLifecycleOwner -import me.zhanghai.android.files.R -import me.zhanghai.android.files.util.NotificationChannelTemplate -import me.zhanghai.android.files.util.NotificationTemplate -import me.zhanghai.android.files.util.startActivitySafe - -val backgroundActivityStartNotificationTemplate = - NotificationTemplate( - NotificationChannelTemplate( - "background_activity_start", - R.string.notification_channel_background_activity_start_name, - NotificationManagerCompat.IMPORTANCE_HIGH, - descriptionRes = R.string.notification_channel_background_activity_start_description, - showBadge = false - ), - colorRes = R.color.color_primary, - smallIcon = R.drawable.notification_icon, - ongoing = true, - autoCancel = true, - category = NotificationCompat.CATEGORY_ERROR, - priority = NotificationCompat.PRIORITY_HIGH - ) - -object BackgroundActivityStarter { - fun startActivity(intent: Intent, title: CharSequence, text: CharSequence?, context: Context) { - // TODO: Only use new task when in background? - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - if (isInForeground) { - context.startActivitySafe(intent) - } else { - notifyStartActivity(intent, title, text, context) - } - } - - private val isInForeground: Boolean - get() = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast( - Lifecycle.State.STARTED - ) - - private fun notifyStartActivity( - intent: Intent, - title: CharSequence, - text: CharSequence?, - context: Context - ) { - var pendingIntentFlags = PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_CANCEL_CURRENT - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - pendingIntentFlags = pendingIntentFlags or PendingIntent.FLAG_IMMUTABLE - } - val pendingIntent = PendingIntent.getActivity( - context, intent.hashCode(), intent, pendingIntentFlags - ) - val notification = backgroundActivityStartNotificationTemplate.createBuilder(context) - .setContentTitle(title) - .setContentText(text) - .setContentIntent(pendingIntent) - .build() - notificationManager.notify(intent.hashCode(), notification) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/NotificationIds.kt b/app/src/main/java/me/zhanghai/android/files/app/NotificationIds.kt deleted file mode 100644 index caabb2066..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/NotificationIds.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -object NotificationIds { - const val FTP_SERVER = 1 -} diff --git a/app/src/main/java/me/zhanghai/android/files/app/SystemServices.kt b/app/src/main/java/me/zhanghai/android/files/app/SystemServices.kt deleted file mode 100644 index 388fdbbb1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/app/SystemServices.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.app - -import android.content.ClipboardManager -import android.content.ContentResolver -import android.content.SharedPreferences -import android.content.pm.PackageManager -import android.net.wifi.WifiManager -import android.os.PowerManager -import android.os.storage.StorageManager -import android.view.inputmethod.InputMethodManager -import androidx.core.app.NotificationManagerCompat -import androidx.preference.PreferenceManager -import me.zhanghai.android.files.compat.getSystemServiceCompat -import me.zhanghai.android.files.compat.mainExecutorCompat -import okhttp3.OkHttpClient -import java.util.concurrent.Executor - -val appClassLoader = AppProvider::class.java.classLoader - -val clipboardManager: ClipboardManager by lazy { - application.getSystemServiceCompat(ClipboardManager::class.java) -} - -val contentResolver: ContentResolver by lazy { application.contentResolver } - -val defaultSharedPreferences: SharedPreferences by lazy { - PreferenceManager.getDefaultSharedPreferences(application) -} - -val okHttpClient: OkHttpClient by lazy { OkHttpClient() } - -val inputMethodManager: InputMethodManager by lazy { - application.getSystemServiceCompat(InputMethodManager::class.java) -} - -val mainExecutor: Executor by lazy { application.mainExecutorCompat } - -val notificationManager: NotificationManagerCompat by lazy { - NotificationManagerCompat.from(application) -} - -val packageManager: PackageManager by lazy { application.packageManager } - -val powerManager: PowerManager by lazy { - application.getSystemServiceCompat(PowerManager::class.java) -} - -val storageManager: StorageManager by lazy { - application.getSystemServiceCompat(StorageManager::class.java) -} - -val wifiManager: WifiManager by lazy { - application.getSystemServiceCompat(WifiManager::class.java) -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/AppIconApplicationInfoFetcherFactory.kt b/app/src/main/java/me/zhanghai/android/files/coil/AppIconApplicationInfoFetcherFactory.kt deleted file mode 100644 index f9f804a27..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/AppIconApplicationInfoFetcherFactory.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.content.Context -import android.content.pm.ApplicationInfo -import coil.key.Keyer -import coil.request.Options -import me.zhanghai.android.appiconloader.AppIconLoader -import me.zhanghai.android.files.R -import me.zhanghai.android.files.compat.longVersionCodeCompat -import me.zhanghai.android.files.util.getDimensionPixelSize -import java.io.Closeable - -class AppIconApplicationInfoKeyer : Keyer { - override fun key(data: ApplicationInfo, options: Options): String = - AppIconLoader.getIconKey(data, data.longVersionCodeCompat, options.context) -} - -class AppIconApplicationInfoFetcherFactory( - context: Context -) : AppIconFetcher.Factory( - // This is used by PrincipalListAdapter. - context.getDimensionPixelSize(R.dimen.icon_size), context -) { - override fun getApplicationInfo(data: ApplicationInfo): Pair = - data to null -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/AppIconFetcher.kt b/app/src/main/java/me/zhanghai/android/files/coil/AppIconFetcher.kt deleted file mode 100644 index 7a8c8c920..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/AppIconFetcher.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.content.Context -import android.content.pm.ApplicationInfo -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DataSource -import coil.fetch.DrawableResult -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.request.Options -import me.zhanghai.android.appiconloader.AppIconLoader -import java.io.Closeable - -class AppIconFetcher( - private val options: Options, - private val appIconLoader: AppIconLoader, - private val getApplicationInfo: () -> Pair -) : Fetcher { - override suspend fun fetch(): FetchResult { - val (applicationInfo, closeable) = getApplicationInfo() - val icon = closeable.use { appIconLoader.loadIcon(applicationInfo) } - // Not sampled because we only load with one fixed size. - return DrawableResult(icon.toDrawable(options.context.resources), false, DataSource.DISK) - } - - abstract class Factory( - iconSize: Int, - context: Context, - shrinkNonAdaptiveIcons: Boolean = false - ) : Fetcher.Factory { - private val appIconLoader = - AppIconLoader(iconSize, shrinkNonAdaptiveIcons, context) - - override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher = - AppIconFetcher(options, appIconLoader) { getApplicationInfo(data) } - - abstract fun getApplicationInfo(data: T): Pair - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/AppIconPackageNameFetcherFactory.kt b/app/src/main/java/me/zhanghai/android/files/coil/AppIconPackageNameFetcherFactory.kt deleted file mode 100644 index 4e8977660..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/AppIconPackageNameFetcherFactory.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.content.Context -import android.content.pm.ApplicationInfo -import coil.key.Keyer -import coil.request.Options -import me.zhanghai.android.files.R -import me.zhanghai.android.files.compat.PackageManagerCompat -import me.zhanghai.android.files.util.getDimensionPixelSize -import java.io.Closeable - -data class AppIconPackageName(val packageName: String) - -class AppIconPackageNameKeyer : Keyer { - override fun key(data: AppIconPackageName, options: Options): String = data.packageName -} - -class AppIconPackageNameFetcherFactory( - private val context: Context -) : AppIconFetcher.Factory( - // This is used by FileListAdapter, and shrinking non-adaptive icons makes it look better as a - // badge. - context.getDimensionPixelSize(R.dimen.badge_size_plus_1dp), context, true -) { - override fun getApplicationInfo(data: AppIconPackageName): Pair { - // PackageManager.MATCH_UNINSTALLED_PACKAGES allows using PackageManager.MATCH_ANY_USER - // without the INTERACT_ACROSS_USERS permission when we are in the system user and it has a - // managed profile. It may also help corner cases like when the package is hidden. - return context.packageManager.getApplicationInfo( - data.packageName, PackageManagerCompat.MATCH_UNINSTALLED_PACKAGES - ) to null - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/CoilExtensions.kt b/app/src/main/java/me/zhanghai/android/files/coil/CoilExtensions.kt deleted file mode 100644 index 90ca73813..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/CoilExtensions.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import coil.request.ImageRequest -import coil.transition.CrossfadeTransition - -fun ImageRequest.Builder.fadeIn(durationMillis: Int): ImageRequest.Builder = - apply { - placeholder(android.R.color.transparent) - transitionFactory(CrossfadeTransition.Factory(durationMillis, true)) - } diff --git a/app/src/main/java/me/zhanghai/android/files/coil/CoilInitializer.kt b/app/src/main/java/me/zhanghai/android/files/coil/CoilInitializer.kt deleted file mode 100644 index 4072b3b06..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/CoilInitializer.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.os.Build -import coil.Coil -import coil.ImageLoader -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.decode.SvgDecoder -import me.zhanghai.android.files.app.application - -fun initializeCoil() { - Coil.setImageLoader( - ImageLoader.Builder(application) - .components { - add(AppIconApplicationInfoKeyer()) - add(AppIconApplicationInfoFetcherFactory(application)) - add(AppIconPackageNameKeyer()) - add(AppIconPackageNameFetcherFactory(application)) - add(PathAttributesKeyer()) - add(PathAttributesFetcher.Factory(application)) - add( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - ImageDecoderDecoder.Factory() - } else { - GifDecoder.Factory() - } - ) - add(SvgDecoder.Factory(false)) - } - .build() - ) -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/CoilUtils.kt b/app/src/main/java/me/zhanghai/android/files/coil/CoilUtils.kt deleted file mode 100644 index 4617f40e3..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/CoilUtils.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.graphics.Bitmap -import android.os.Build -import coil.decode.DataSource -import coil.size.Dimension -import coil.size.Scale -import coil.size.Size -import coil.size.isOriginal -import coil.size.pxOrElse -import java8.nio.file.Path -import me.zhanghai.android.files.filelist.isRemotePath - -val Bitmap.Config.isHardware: Boolean - get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.HARDWARE - -fun Bitmap.Config.toSoftware(): Bitmap.Config = if (isHardware) Bitmap.Config.ARGB_8888 else this - -val Path.dataSource: DataSource - get() = if (isRemotePath) DataSource.NETWORK else DataSource.DISK - -inline fun Size.widthPx(scale: Scale, original: () -> Int): Int = - if (isOriginal) original() else width.toPx(scale) - -inline fun Size.heightPx(scale: Scale, original: () -> Int): Int = - if (isOriginal) original() else height.toPx(scale) - -fun Dimension.toPx(scale: Scale) = - pxOrElse { - when (scale) { - Scale.FILL -> Int.MIN_VALUE - Scale.FIT -> Int.MAX_VALUE - } - } diff --git a/app/src/main/java/me/zhanghai/android/files/coil/LoadRequestBuilderExtensions.kt b/app/src/main/java/me/zhanghai/android/files/coil/LoadRequestBuilderExtensions.kt deleted file mode 100644 index 3dcca04e3..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/LoadRequestBuilderExtensions.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.graphics.drawable.Drawable -import coil.request.ImageRequest -import coil.target.ImageViewTarget - -// Setting the placeholder drawable as error drawable again causes animation glitches, so we just -// ignore the onError() callback. -fun ImageRequest.Builder.ignoreError() { - val view = (build().target as ImageViewTarget).view - target(object : ImageViewTarget(view) { - override fun onError(error: Drawable?) {} - }) -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/PathAttributesFetcher.kt b/app/src/main/java/me/zhanghai/android/files/coil/PathAttributesFetcher.kt deleted file mode 100644 index b32fd18bd..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/PathAttributesFetcher.kt +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.content.Context -import android.content.pm.ApplicationInfo -import android.media.MediaMetadataRetriever -import android.os.ParcelFileDescriptor -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.ImageSource -import coil.fetch.DrawableResult -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.key.Keyer -import coil.request.Options -import coil.size.Dimension -import java8.nio.file.Path -import java8.nio.file.attribute.BasicFileAttributes -import me.zhanghai.android.files.R -import me.zhanghai.android.files.compat.use -import me.zhanghai.android.files.file.MimeType -import me.zhanghai.android.files.file.asMimeType -import me.zhanghai.android.files.file.isApk -import me.zhanghai.android.files.file.isImage -import me.zhanghai.android.files.file.isMedia -import me.zhanghai.android.files.file.isPdf -import me.zhanghai.android.files.file.isVideo -import me.zhanghai.android.files.file.lastModifiedInstant -import me.zhanghai.android.files.filelist.isRemotePath -import me.zhanghai.android.files.provider.common.AndroidFileTypeDetector -import me.zhanghai.android.files.provider.common.newInputStream -import me.zhanghai.android.files.provider.content.resolver.ResolverException -import me.zhanghai.android.files.provider.document.documentSupportsThumbnail -import me.zhanghai.android.files.provider.document.isDocumentPath -import me.zhanghai.android.files.provider.document.resolver.DocumentResolver -import me.zhanghai.android.files.provider.ftp.isFtpPath -import me.zhanghai.android.files.provider.linux.isLinuxPath -import me.zhanghai.android.files.settings.Settings -import me.zhanghai.android.files.util.getDimensionPixelSize -import me.zhanghai.android.files.util.getPackageArchiveInfoCompat -import me.zhanghai.android.files.util.isGetPackageArchiveInfoCompatible -import me.zhanghai.android.files.util.isMediaMetadataRetrieverCompatible -import me.zhanghai.android.files.util.runWithCancellationSignal -import me.zhanghai.android.files.util.setDataSource -import me.zhanghai.android.files.util.valueCompat -import okio.buffer -import okio.source -import java.io.Closeable -import java.io.IOException -import me.zhanghai.android.files.util.setDataSource as appSetDataSource - -class PathAttributesKeyer : Keyer> { - override fun key(data: Pair, options: Options): String { - val (path, attributes) = data - return "$path:${attributes.lastModifiedInstant.toEpochMilli()}" - } -} - -class PathAttributesFetcher( - private val data: Pair, - private val options: Options, - private val imageLoader: ImageLoader, - private val appIconFetcherFactory: AppIconFetcher.Factory, - private val videoFrameFetcherFactory: VideoFrameFetcher.Factory, - private val pdfPageFetcherFactory: PdfPageFetcher.Factory -) : Fetcher { - override suspend fun fetch(): FetchResult? { - val (path, attributes) = data - val (width, height) = options.size - // @see android.provider.MediaStore.ThumbnailConstants.MINI_SIZE - val isThumbnail = width is Dimension.Pixels && width.px <= 512 - && height is Dimension.Pixels && height.px <= 384 - if (isThumbnail) { - width as Dimension.Pixels - height as Dimension.Pixels - if (path.isDocumentPath && attributes.documentSupportsThumbnail) { - val thumbnail = runWithCancellationSignal { signal -> - try { - DocumentResolver.getThumbnail( - path as DocumentResolver.Path, width.px, height.px, signal - ) - } catch (e: ResolverException) { - e.printStackTrace() - null - } - } - if (thumbnail != null) { - return DrawableResult( - thumbnail.toDrawable(options.context.resources), true, path.dataSource - ) - } - } - if (path.isRemotePath) { - // FTP doesn't support random access and requires one connection per parallel read. - val shouldReadRemotePath = !path.isFtpPath - && Settings.READ_REMOTE_FILES_FOR_THUMBNAIL.valueCompat - if (!shouldReadRemotePath) { - error("Cannot read $path for thumbnail") - } - } - } - val mimeType = AndroidFileTypeDetector.getMimeType(data.first, data.second).asMimeType() - when { - mimeType.isApk && path.isGetPackageArchiveInfoCompatible -> { - try { - return appIconFetcherFactory.create(path, options, imageLoader).fetch() - } catch (e: Exception) { - e.printStackTrace() - } - } - mimeType.isImage || mimeType == MimeType.GENERIC -> { - val inputStream = path.newInputStream() - return SourceResult( - ImageSource(inputStream.source().buffer(), options.context), - if (mimeType != MimeType.GENERIC) mimeType.value else null, path.dataSource - ) - } - mimeType.isMedia && path.isMediaMetadataRetrieverCompatible -> { - val embeddedPicture = try { - MediaMetadataRetriever().use { retriever -> - retriever.setDataSource(path) - retriever.embeddedPicture - } - } catch (e: Exception) { - e.printStackTrace() - null - } - if (embeddedPicture != null) { - return SourceResult( - ImageSource( - embeddedPicture.inputStream().source().buffer(), options.context - ), null, path.dataSource - ) - } - if (mimeType.isVideo) { - try { - return videoFrameFetcherFactory.create(path, options, imageLoader).fetch() - } catch (e: Exception) { - e.printStackTrace() - } - } - } - mimeType.isPdf && (path.isLinuxPath || path.isDocumentPath) -> { - try { - return pdfPageFetcherFactory.create(path, options, imageLoader).fetch() - } catch (e: Exception) { - e.printStackTrace() - } - } - } - return null - } - - class Factory(private val context: Context) : Fetcher.Factory> { - private val appIconFetcherFactory = object : AppIconFetcher.Factory( - // This is used by FileListAdapter. - context.getDimensionPixelSize(R.dimen.large_icon_size), context - ) { - override fun getApplicationInfo(data: Path): Pair { - val (packageInfo, closeable) = - context.packageManager.getPackageArchiveInfoCompat(data, 0) - val applicationInfo = packageInfo?.applicationInfo - if (applicationInfo == null) { - closeable?.close() - throw IOException("ApplicationInfo is null") - } - return applicationInfo to closeable - } - } - - private val videoFrameFetcherFactory = object : VideoFrameFetcher.Factory() { - override fun MediaMetadataRetriever.setDataSource(data: Path) { - appSetDataSource(data) - } - } - - private val pdfPageFetcherFactory = object : PdfPageFetcher.Factory() { - override fun openParcelFileDescriptor(data: Path): ParcelFileDescriptor = - when { - data.isLinuxPath -> - ParcelFileDescriptor.open(data.toFile(), ParcelFileDescriptor.MODE_READ_ONLY) - data.isDocumentPath -> - DocumentResolver.openParcelFileDescriptor(data as DocumentResolver.Path, "r") - else -> throw IllegalArgumentException(data.toString()) - } - } - - override fun create( - data: Pair, - options: Options, - imageLoader: ImageLoader - ): Fetcher = - PathAttributesFetcher( - data, options, imageLoader, appIconFetcherFactory, videoFrameFetcherFactory, - pdfPageFetcherFactory - ) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/PdfPageFetcher.kt b/app/src/main/java/me/zhanghai/android/files/coil/PdfPageFetcher.kt deleted file mode 100644 index 253d68c8a..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/PdfPageFetcher.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.graphics.Color -import android.graphics.pdf.PdfRenderer -import android.os.ParcelFileDescriptor -import androidx.annotation.ColorInt -import androidx.annotation.IntRange -import androidx.core.graphics.createBitmap -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.DecodeUtils -import coil.fetch.DrawableResult -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.request.ImageRequest -import coil.request.Options -import coil.request.Parameters -import kotlin.math.roundToInt - -fun ImageRequest.Builder.pdfBackgroundColor(@ColorInt backgroundColor: Int): ImageRequest.Builder = - setParameter(PdfPageFetcher.PDF_BACKGROUND_COLOR_KEY, backgroundColor) - -fun ImageRequest.Builder.pdfPageIndex(@IntRange(from = 0) pageIndex: Int): ImageRequest.Builder { - require(pageIndex >= 0) { "pageIndex must be >= 0." } - return setParameter(PdfPageFetcher.PDF_PAGE_INDEX_KEY, pageIndex) -} - -@ColorInt -fun Parameters.pdfBackgroundColor(): Int? = value(PdfPageFetcher.PDF_BACKGROUND_COLOR_KEY) as Int? - -@IntRange(from = 0) -fun Parameters.pdfPageIndex(): Int? = value(PdfPageFetcher.PDF_PAGE_INDEX_KEY) as Int? - -class PdfPageFetcher( - private val options: Options, - private val openParcelFileDescriptor: () -> ParcelFileDescriptor -) : Fetcher { - override suspend fun fetch(): FetchResult = - openParcelFileDescriptor().use { pfd -> - PdfRenderer(pfd).use { renderer -> - val pageIndex = options.parameters.pdfPageIndex() ?: 0 - renderer.openPage(pageIndex).use { page -> - val srcWidth = page.width - check(srcWidth > 0) { - "PDF page $pageIndex width $srcWidth isn't greater than 0" - } - val srcHeight = page.height - check(srcWidth > 0) { - "PDF page $pageIndex height $srcHeight isn't greater than 0" - } - val dstWidth = options.size.widthPx(options.scale) { srcWidth } - val dstHeight = options.size.heightPx(options.scale) { srcHeight } - val rawScale = DecodeUtils.computeSizeMultiplier( - srcWidth = srcWidth, - srcHeight = srcHeight, - dstWidth = dstWidth, - dstHeight = dstHeight, - scale = options.scale - ) - val scale = if (options.allowInexactSize) { - rawScale.coerceAtMost(1.0) - } else { - rawScale - } - val width = (scale * srcWidth).roundToInt() - val height = (scale * srcHeight).roundToInt() - val config = options.config.toSoftware() - val bitmap = createBitmap(width, height, config) - val backgroundColor = options.parameters.pdfBackgroundColor() ?: Color.WHITE - bitmap.eraseColor(backgroundColor) - page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) - DrawableResult( - drawable = bitmap.toDrawable(options.context.resources), - isSampled = scale < 1.0, - dataSource = DataSource.DISK - ) - } - } - } - - companion object { - const val PDF_BACKGROUND_COLOR_KEY = "coil#pdf_background_color" - const val PDF_PAGE_INDEX_KEY = "coil#pdf_page_index" - } - - abstract class Factory : Fetcher.Factory { - override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher = - PdfPageFetcher(options) { openParcelFileDescriptor(data) } - - protected abstract fun openParcelFileDescriptor(data: T): ParcelFileDescriptor - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/coil/VideoFrameFetcher.kt b/app/src/main/java/me/zhanghai/android/files/coil/VideoFrameFetcher.kt deleted file mode 100644 index 6600823d6..000000000 --- a/app/src/main/java/me/zhanghai/android/files/coil/VideoFrameFetcher.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2022 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.coil - -import android.graphics.Paint -import android.media.MediaMetadataRetriever -import android.os.Build -import androidx.core.graphics.applyCanvas -import androidx.core.graphics.createBitmap -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.DecodeUtils -import coil.fetch.DrawableResult -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.request.Options -import coil.request.videoFrameOption -import coil.request.videoFramePercent -import me.zhanghai.android.files.compat.getFrameAtTimeCompat -import me.zhanghai.android.files.compat.getScaledFrameAtTimeCompat -import me.zhanghai.android.files.compat.use -import java.util.concurrent.TimeUnit -import kotlin.math.roundToInt -import kotlin.math.roundToLong - -class VideoFrameFetcher( - private val options: Options, - private val setDataSource: MediaMetadataRetriever.() -> Unit -) : Fetcher { - override suspend fun fetch(): FetchResult = - MediaMetadataRetriever().use { retriever -> - retriever.setDataSource() - val rotation = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) - ?.toIntOrNull() ?: 0 - var srcWidth: Int - var srcHeight: Int - when (rotation) { - 90, 270 -> { - srcWidth = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT) - ?.toIntOrNull() ?: 0 - srcHeight = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH) - ?.toIntOrNull() ?: 0 - } - else -> { - srcWidth = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH) - ?.toIntOrNull() ?: 0 - srcHeight = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT) - ?.toIntOrNull() ?: 0 - } - } - val durationMillis = - retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION) - ?.toLongOrNull() ?: 0L - // 1/3 is the first percentage tried by totem-video-thumbnailer. - // @see https://gitlab.gnome.org/GNOME/totem/-/blob/master/src/totem-video-thumbnailer.c#L543 - val framePercent = options.parameters.videoFramePercent() ?: (1.0 / 3.0) - val frameMicros = TimeUnit.MICROSECONDS.convert( - (framePercent * durationMillis).roundToLong(), TimeUnit.MILLISECONDS - ) - val frameOption = options.parameters.videoFrameOption() - ?: MediaMetadataRetriever.OPTION_CLOSEST_SYNC - val bitmapParams = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - MediaMetadataRetriever.BitmapParams().apply { preferredConfig = options.config } - } else { - null - } - val outBitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 - && srcWidth > 0 && srcHeight > 0) { - val dstWidth = options.size.widthPx(options.scale) { srcWidth } - val dstHeight = options.size.heightPx(options.scale) { srcHeight } - val rawScale = DecodeUtils.computeSizeMultiplier( - srcWidth = srcWidth, - srcHeight = srcHeight, - dstWidth = dstWidth, - dstHeight = dstHeight, - scale = options.scale - ) - val scale = if (options.allowInexactSize) { - rawScale.coerceAtMost(1.0) - } else { - rawScale - } - val width = (scale * srcWidth).roundToInt() - val height = (scale * srcHeight).roundToInt() - retriever.getScaledFrameAtTimeCompat( - frameMicros, frameOption, width, height, bitmapParams - ) - } else { - retriever.getFrameAtTimeCompat(frameMicros, frameOption, bitmapParams)?.also { - srcWidth = it.width - srcHeight = it.height - } - } - val dstWidth = options.size.widthPx(options.scale) { srcWidth } - val dstHeight = options.size.heightPx(options.scale) { srcHeight } - val rawScale = DecodeUtils.computeSizeMultiplier( - srcWidth = srcWidth, - srcHeight = srcHeight, - dstWidth = dstWidth, - dstHeight = dstHeight, - scale = options.scale - ) - checkNotNull(outBitmap) { "Failed to decode frame at $frameMicros microseconds" } - val scale = if (options.allowInexactSize) { - rawScale.coerceAtMost(1.0) - } else { - rawScale - } - val width = (scale * srcWidth).roundToInt() - val height = (scale * srcHeight).roundToInt() - val isValidSize = if (options.allowInexactSize) { - outBitmap.width <= width && outBitmap.height <= height - } else { - outBitmap.width == width && outBitmap.height == height - } - val isValidConfig = !outBitmap.config.isHardware || options.config.isHardware - val bitmap = if (isValidSize && isValidConfig) { - outBitmap - } else { - val config = options.config.toSoftware() - createBitmap(width, height, config).applyCanvas { - scale(scale.toFloat(), scale.toFloat()) - val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG) - drawBitmap(outBitmap, 0f, 0f, paint) - outBitmap.recycle() - } - } - DrawableResult( - drawable = bitmap.toDrawable(options.context.resources), - isSampled = scale < 1.0, - dataSource = DataSource.DISK - ) - } - - abstract class Factory : Fetcher.Factory { - override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher = - VideoFrameFetcher(options) { setDataSource(data) } - - protected abstract fun MediaMetadataRetriever.setDataSource(data: T) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/colorpicker/BaseColorPreference.kt b/app/src/main/java/me/zhanghai/android/files/colorpicker/BaseColorPreference.kt deleted file mode 100644 index d9fe4c1fd..000000000 --- a/app/src/main/java/me/zhanghai/android/files/colorpicker/BaseColorPreference.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.colorpicker - -import android.content.Context -import android.graphics.drawable.GradientDrawable -import android.util.AttributeSet -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.annotation.StyleRes -import androidx.preference.DialogPreference -import androidx.preference.PreferenceViewHolder -import me.zhanghai.android.files.R -import me.zhanghai.android.files.util.getFloatByAttr -import kotlin.math.roundToInt - -abstract class BaseColorPreference : DialogPreference { - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - - constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super( - context, attrs, defStyleAttr - ) - - constructor( - context: Context, - attrs: AttributeSet?, - @AttrRes defStyleAttr: Int, - @StyleRes defStyleRes: Int - ) : super(context, attrs, defStyleAttr, defStyleRes) - - init { - widgetLayoutResource = R.layout.color_preference_widget - dialogLayoutResource = R.layout.color_picker_dialog - setPositiveButtonText(android.R.string.ok) - setNegativeButtonText(android.R.string.cancel) - } - - override fun onBindViewHolder(holder: PreferenceViewHolder) { - super.onBindViewHolder(holder) - - val swatchView = holder.findViewById(R.id.swatch) - if (swatchView != null) { - val swatchDrawable = swatchView.background as GradientDrawable - swatchDrawable.setColor(value) - var alpha = 0xFF - if (!isEnabled) { - val disabledAlpha = context.getFloatByAttr(android.R.attr.disabledAlpha) - alpha = (disabledAlpha * alpha).roundToInt() - } - swatchDrawable.alpha = alpha - } - } - - @get:ColorInt - abstract var value: Int - - @get:ColorInt - abstract val defaultValue: Int - - abstract val entryValues: IntArray -} diff --git a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPaletteAdapter.kt b/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPaletteAdapter.kt deleted file mode 100644 index dcd100392..000000000 --- a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPaletteAdapter.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.colorpicker - -import android.view.View -import android.view.ViewGroup -import android.widget.AbsListView -import android.widget.BaseAdapter - -class ColorPaletteAdapter(private val colors: IntArray) : BaseAdapter() { - override fun hasStableIds(): Boolean = true - - override fun getCount(): Int = colors.size - - override fun getItem(position: Int): Int = colors[position] - - override fun getItemId(position: Int): Long = getItem(position).toLong() - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val swatchView = convertView as ColorSwatchView? - ?: ColorSwatchView(parent.context).apply { - layoutParams = AbsListView.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT - ) - } - return swatchView.apply { - setColor(getItem(position)) - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPreferenceDialogFragment.kt b/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPreferenceDialogFragment.kt deleted file mode 100644 index 01f598b1d..000000000 --- a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPreferenceDialogFragment.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.colorpicker - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import android.view.View -import android.widget.GridView -import androidx.appcompat.app.AlertDialog -import androidx.core.view.ViewCompat -import kotlinx.parcelize.Parcelize -import me.zhanghai.android.files.R -import me.zhanghai.android.files.ui.MaterialPreferenceDialogFragmentCompat -import me.zhanghai.android.files.util.ParcelableState -import me.zhanghai.android.files.util.getState -import me.zhanghai.android.files.util.putState -import me.zhanghai.android.files.util.withTheme - -class ColorPreferenceDialogFragment : MaterialPreferenceDialogFragmentCompat() { - override val preference: BaseColorPreference - get() = super.preference as BaseColorPreference - - private lateinit var colors: IntArray - private var checkedColor = 0 - private var defaultColor = 0 - - private lateinit var paletteGrid: GridView - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - if (savedInstanceState == null) { - val preference = preference - colors = preference.entryValues - checkedColor = preference.value - defaultColor = preference.defaultValue - } else { - val state = savedInstanceState.getState() - colors = state.colors - checkedColor = state.checkedColor - defaultColor = state.defaultColor - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - - val checkedPosition = paletteGrid.checkedItemPosition - val checkedColor = if (checkedPosition != -1) colors[checkedPosition] else checkedColor - outState.putState(State(colors, checkedColor, defaultColor)) - } - - override fun onCreateDialogView(context: Context): View? = - super.onCreateDialogView(context.withTheme(theme)) - - override fun onBindDialogView(view: View) { - super.onBindDialogView(view) - - paletteGrid = ViewCompat.requireViewById(view, R.id.palette) - paletteGrid.adapter = ColorPaletteAdapter(colors) - val checkedPosition = colors.indexOf(checkedColor) - if (checkedPosition != -1) { - paletteGrid.setItemChecked(checkedPosition, true) - } - } - - override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { - super.onPrepareDialogBuilder(builder) - - if (defaultColor in colors) { - builder.setNeutralButton(R.string.default_, null) - } - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = - (super.onCreateDialog(savedInstanceState) as AlertDialog).apply { - if (defaultColor in colors) { - // Override the listener here so that we won't close the dialog. - setOnShowListener { - getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener { - paletteGrid.setItemChecked(colors.indexOf(defaultColor), true) - } - } - } - } - - override fun onDialogClosed(positiveResult: Boolean) { - if (!positiveResult) { - return - } - val checkedPosition = paletteGrid.checkedItemPosition - if (checkedPosition == -1) { - return - } - val checkedColor = colors[checkedPosition] - preference.value = checkedColor - } - - @Parcelize - private class State( - val colors: IntArray, - val checkedColor: Int, - val defaultColor: Int - ) : ParcelableState -} diff --git a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorSwatchView.kt b/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorSwatchView.kt deleted file mode 100644 index 5df9783b9..000000000 --- a/app/src/main/java/me/zhanghai/android/files/colorpicker/ColorSwatchView.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.colorpicker - -import android.content.Context -import android.graphics.drawable.GradientDrawable -import android.graphics.drawable.LayerDrawable -import android.util.AttributeSet -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.annotation.StyleRes -import androidx.appcompat.content.res.AppCompatResources -import me.zhanghai.android.files.R -import me.zhanghai.android.files.ui.CheckableView - -class ColorSwatchView : CheckableView { - private val gradientDrawable: GradientDrawable - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - - constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super( - context, attrs, defStyleAttr - ) - - constructor( - context: Context, - attrs: AttributeSet?, - @AttrRes defStyleAttr: Int, - @StyleRes defStyleRes: Int - ) : super(context, attrs, defStyleAttr, defStyleRes) - - init { - val background = AppCompatResources.getDrawable( - context, R.drawable.color_swatch_view_background - ) as LayerDrawable - gradientDrawable = background.getDrawable(0) as GradientDrawable - setBackground(background) - } - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - setMeasuredDimension( - resolveSize(suggestedMinimumWidth, widthMeasureSpec), - resolveSize(suggestedMinimumHeight, heightMeasureSpec) - ) - } - - fun setColor(@ColorInt color: Int) { - gradientDrawable.apply { - mutate() - setColor(color) - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ActivityCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ActivityCompat.kt deleted file mode 100644 index 24dd791db..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ActivityCompat.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.app.Activity -import android.app.ActivityManager.TaskDescription -import android.graphics.Color -import android.os.Build -import androidx.annotation.StyleRes -import androidx.core.app.ActivityCompat -import me.zhanghai.android.files.util.getColorByAttr - -fun Activity.recreateCompat() { - ActivityCompat.recreate(this) -} - -fun Activity.setThemeCompat(@StyleRes resid: Int) { - setTheme(resid) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val surfaceColor = getColorByAttr(com.google.android.material.R.attr.colorSurface) - if (surfaceColor != 0 && Color.alpha(surfaceColor) == 0xFF) { - @Suppress("DEPRECATION") - setTaskDescription(TaskDescription(null, null, surfaceColor)) - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ApplicationInfoCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ApplicationInfoCompat.kt deleted file mode 100644 index db686a786..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ApplicationInfoCompat.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.pm.ApplicationInfo -import android.os.Build -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedField - -@RestrictedHiddenApi -private val versionCodeField by lazyReflectedField(ApplicationInfo::class.java, "versionCode") - -@RestrictedHiddenApi -private val longVersionCodeField by lazyReflectedField( - ApplicationInfo::class.java, "longVersionCode" -) - -val ApplicationInfo.longVersionCodeCompat: Long - get() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - longVersionCodeField.getLong(this) - } else { - versionCodeField.getInt(this).toLong() - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ContextCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ContextCompat.kt deleted file mode 100644 index 49f696e7f..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ContextCompat.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.annotation.SuppressLint -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.content.res.ColorStateList -import android.graphics.drawable.Drawable -import android.util.AttributeSet -import androidx.annotation.AttrRes -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes -import androidx.annotation.StyleRes -import androidx.annotation.StyleableRes -import androidx.appcompat.content.res.AppCompatResources -import androidx.appcompat.widget.TintTypedArray -import androidx.core.content.ContextCompat -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedMethod -import java.util.concurrent.Executor -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract - -fun Context.checkSelfPermissionCompat(permission: String): Int = - ContextCompat.checkSelfPermission(this, permission) - -@ColorInt -fun Context.getColorCompat(@ColorRes id: Int): Int = getColorStateListCompat(id).defaultColor - -fun Context.getColorStateListCompat(@ColorRes id: Int): ColorStateList = - AppCompatResources.getColorStateList(this, id)!! - -fun Context.getDrawableCompat(@DrawableRes id: Int): Drawable = - AppCompatResources.getDrawable(this, id)!! - -fun Context.getSystemServiceCompat(serviceClass: Class): T = - ContextCompat.getSystemService(this, serviceClass)!! - -val Context.mainExecutorCompat: Executor - get() = ContextCompat.getMainExecutor(this) - -@SuppressLint("RestrictedApi") -fun Context.obtainStyledAttributesCompat( - set: AttributeSet? = null, - @StyleableRes attrs: IntArray, - @AttrRes defStyleAttr: Int = 0, - @StyleRes defStyleRes: Int = 0 -): TintTypedArray = - TintTypedArray.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes) - -@OptIn(ExperimentalContracts::class) -@SuppressLint("RestrictedApi") -inline fun TintTypedArray.use(block: (TintTypedArray) -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return try { - block(this) - } finally { - recycle() - } -} - -fun Context.registerReceiverCompat( - receiver: BroadcastReceiver?, - filter: IntentFilter, - flags: Int -): Intent? = ContextCompat.registerReceiver(this, receiver, filter, flags) - -@RestrictedHiddenApi -private val getThemeResIdMethod by lazyReflectedMethod(Context::class.java, "getThemeResId") - -val Context.themeResIdCompat: Int - @StyleRes - get() = getThemeResIdMethod.invoke(this) as Int diff --git a/app/src/main/java/me/zhanghai/android/files/compat/DateTimeCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/DateTimeCompat.kt deleted file mode 100644 index a1ad53167..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/DateTimeCompat.kt +++ /dev/null @@ -1,12 +0,0 @@ -package me.zhanghai.android.files.compat - -import org.threeten.bp.DateTimeUtils -import org.threeten.bp.Instant -import java.util.Calendar -import java.util.Date - -fun Calendar.toInstantCompat(): Instant = DateTimeUtils.toInstant(this) - -fun Date.toInstantCompat(): Instant = DateTimeUtils.toInstant(this) - -fun Instant.toDateCompat(): Date = DateTimeUtils.toDate(this) diff --git a/app/src/main/java/me/zhanghai/android/files/compat/DialogCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/DialogCompat.kt deleted file mode 100644 index b8851a2c7..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/DialogCompat.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.app.Dialog -import android.view.View -import androidx.annotation.IdRes -import androidx.core.app.DialogCompat - -@Suppress("UNCHECKED_CAST") -fun Dialog.requireViewByIdCompat(@IdRes id: Int): T = - DialogCompat.requireViewById(this, id) as T diff --git a/app/src/main/java/me/zhanghai/android/files/compat/DigitsKeyListenerCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/DigitsKeyListenerCompat.kt deleted file mode 100644 index 7442dd72e..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/DigitsKeyListenerCompat.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.text.method.DigitsKeyListener -import java.util.Locale - -object DigitsKeyListenerCompat { - fun getInstance(locale: Locale?, sign: Boolean, decimal: Boolean): DigitsKeyListener = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - DigitsKeyListener.getInstance(locale, sign, decimal) - } else { - @Suppress("DEPRECATION") - DigitsKeyListener.getInstance(sign, decimal) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/DocumentsContractCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/DocumentsContractCompat.kt deleted file mode 100644 index c5e6d4d13..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/DocumentsContractCompat.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.ContentResolver -import android.net.Uri -import android.os.Build -import android.provider.DocumentsContract -import me.zhanghai.android.files.app.packageManager - -object DocumentsContractCompat { - const val EXTRA_INITIAL_URI = "android.provider.extra.INITIAL_URI" - const val EXTRA_SHOW_ADVANCED = "android.provider.extra.SHOW_ADVANCED" - - const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents" - const val EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = "primary" - - private const val PATH_DOCUMENT = "document" - private const val PATH_CHILDREN = "children" - private const val PATH_TREE = "tree" - - /** @see DocumentsContract.PACKAGE_DOCUMENTS_UI */ - fun getDocumentsUiPackage(): String? { - // See android.permission.cts.ProviderPermissionTest.testManageDocuments() - val packageInfos = packageManager.getPackagesHoldingPermissions( - arrayOf(android.Manifest.permission.MANAGE_DOCUMENTS), 0 - ) - val packageInfo = packageInfos.firstOrNull { it.packageName.endsWith(".documentsui") } - ?: packageInfos.firstOrNull() - return packageInfo?.packageName - } - - /** @see DocumentsContract.isDocumentUri */ - fun isDocumentUri(uri: Uri): Boolean { - if (uri.scheme != ContentResolver.SCHEME_CONTENT) { - return false - } - val pathSegments = uri.pathSegments - return when (pathSegments.size) { - 2 -> pathSegments[0] == PATH_DOCUMENT - 4 -> pathSegments[0] == PATH_TREE && pathSegments[2] == PATH_DOCUMENT - else -> false - } - } - - fun isTreeUri(uri: Uri): Boolean = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - DocumentsContract.isTreeUri(uri) - } else { - uri.pathSegments.let { it.size >= 2 && it[0] == PATH_TREE } - } - - fun isChildDocumentsUri(uri: Uri): Boolean { - val pathSegments = uri.pathSegments - return when (pathSegments.size) { - 3 -> pathSegments[0] == PATH_DOCUMENT && pathSegments[2] == PATH_CHILDREN - 5 -> - pathSegments[0] == PATH_TREE && pathSegments[2] == PATH_DOCUMENT - && pathSegments[4] == PATH_CHILDREN - else -> false - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/DrawableCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/DrawableCompat.kt deleted file mode 100644 index 6bb38606b..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/DrawableCompat.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.graphics.drawable.Drawable -import androidx.annotation.ColorInt -import androidx.core.graphics.drawable.DrawableCompat - -var Drawable.layoutDirectionCompat: Int - get() = DrawableCompat.getLayoutDirection(this) - set(value) { - DrawableCompat.setLayoutDirection(this, value) - } - -fun Drawable.setTintCompat(@ColorInt tint: Int) { - DrawableCompat.setTint(this, tint) -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/EnvironmentCompat2.kt b/app/src/main/java/me/zhanghai/android/files/compat/EnvironmentCompat2.kt deleted file mode 100644 index f48b8695f..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/EnvironmentCompat2.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -object EnvironmentCompat2 { - /** - * @see android.os.Environment.DIRECTORY_SCREENSHOTS - */ - const val DIRECTORY_SCREENSHOTS = "Screenshots" -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ErrnoExceptionCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ErrnoExceptionCompat.kt deleted file mode 100644 index a34e3e3fc..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ErrnoExceptionCompat.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.system.ErrnoException -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedField - -@RestrictedHiddenApi -private val functionNameField by lazyReflectedField(ErrnoException::class.java, "functionName") - -val ErrnoException.functionNameCompat: String - get() = functionNameField.get(this) as String diff --git a/app/src/main/java/me/zhanghai/android/files/compat/InputStreamCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/InputStreamCompat.kt deleted file mode 100644 index 445e9187b..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/InputStreamCompat.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import java.io.IOException -import java.io.InputStream -import kotlin.reflect.KClass - -fun KClass.nullInputStream(): InputStream = - object : InputStream() { - private var closed = false - - override fun read(): Int { - ensureOpen() - return -1 - } - - override fun read(bytes: ByteArray, offset: Int, length: Int): Int { - if (!(offset >= 0 && length >= 0 && length <= bytes.size - offset)) { - throw IndexOutOfBoundsException() - } - ensureOpen() - return if (length == 0) 0 else -1 - } - - override fun skip(length: Long): Long { - ensureOpen() - return 0 - } - - override fun available(): Int { - ensureOpen() - return 0 - } - - override fun close() { - closed = true - } - - private fun ensureOpen() { - if (closed) { - throw IOException("Stream closed") - } - } - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/IntentCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/IntentCompat.kt deleted file mode 100644 index 839eeb955..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/IntentCompat.kt +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.Intent -import me.zhanghai.android.files.util.andInv - -fun Intent.removeFlagsCompat(flags: Int) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - removeFlags(flags) - } else { - setFlags(this.flags andInv flags) - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/KotlinCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/KotlinCompat.kt deleted file mode 100644 index b9066c1dc..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/KotlinCompat.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import kotlin.comparisons.reversed as kotlinReversed - -fun Comparator.reversedCompat(): Comparator = kotlinReversed() diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ListFormatterCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ListFormatterCompat.kt deleted file mode 100644 index 7eb220fc7..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ListFormatterCompat.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.icu.text.ListFormatter -import android.os.Build - -object ListFormatterCompat { - fun format(vararg items: Any?): String = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ListFormatter.getInstance().format(*items) - } else { - formatCompat(items.asList()) - } - - fun format(items: Collection<*>): String = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ListFormatter.getInstance().format(items) - } else { - formatCompat(items) - } - - private fun formatCompat(items: Collection<*>): String = items.joinToString(", ") -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/LocaleConfigCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/LocaleConfigCompat.kt deleted file mode 100644 index ed98883f6..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/LocaleConfigCompat.kt +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.app.LocaleConfig -import android.content.Context -import android.content.res.XmlResourceParser -import android.os.Build -import android.util.Log -import androidx.annotation.RequiresApi -import androidx.annotation.XmlRes -import androidx.core.content.res.ResourcesCompat -import androidx.core.os.LocaleListCompat -import org.xmlpull.v1.XmlPullParser -import java.io.FileNotFoundException - -/** - * @see android.app.LocaleConfig - */ -class LocaleConfigCompat(context: Context) { - var status = 0 - private set - - var supportedLocales: LocaleListCompat? = null - private set - - init { - val impl = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - Api33Impl(context) - } else { - Api21Impl(context) - } - status = impl.status - supportedLocales = impl.supportedLocales - } - - companion object { - /** - * Succeeded reading the LocaleConfig structure stored in an XML file. - */ - const val STATUS_SUCCESS = 0 - - /** - * No android:localeConfig tag on . - */ - const val STATUS_NOT_SPECIFIED = 1 - - /** - * Malformed input in the XML file where the LocaleConfig was stored. - */ - const val STATUS_PARSING_FAILED = 2 - } - - private abstract class Impl { - abstract val status: Int - abstract val supportedLocales: LocaleListCompat? - } - - private class Api21Impl(context: Context) : Impl() { - override var status = 0 - private set - - override var supportedLocales: LocaleListCompat? = null - private set - - init { - val resourceId = try { - getLocaleConfigResourceId(context) - } catch (e: Exception) { - Log.w(TAG, "The resource file pointed to by the given resource ID isn't found.", e) - } - if (resourceId == ResourcesCompat.ID_NULL) { - status = STATUS_NOT_SPECIFIED - } else { - val resources = context.resources - try { - supportedLocales = resources.getXml(resourceId).use { parseLocaleConfig(it) } - status = STATUS_SUCCESS - } catch (e: Exception) { - val resourceEntryName = resources.getResourceEntryName(resourceId) - Log.w(TAG, "Failed to parse XML configuration from $resourceEntryName", e) - status = STATUS_PARSING_FAILED - } - } - } - - // @see com.android.server.pm.pkg.parsing.ParsingPackageUtils - @XmlRes - private fun getLocaleConfigResourceId(context: Context): Int { - // Java cookies starts at 1, while passing 0 (invalid cookie for Java) makes - // AssetManager pick the last asset containing such a file name. - // We should go over all the assets containing AndroidManifest.xml, however there's no - // API to do that, so the best we can do is to start from the first asset and iterate - // until we can't find the next asset containing AndroidManifest.xml. - var cookie = 1 - var isAndroidManifestFound = false - while (true) { - val parser = try { - context.assets.openXmlResourceParser(cookie, FILE_NAME_ANDROID_MANIFEST) - } catch (e: FileNotFoundException) { - if (!isAndroidManifestFound) { - ++cookie - continue - } else { - break - } - } - isAndroidManifestFound = true - parser.use { - do { - if (parser.eventType != XmlPullParser.START_TAG) { - continue - } - if (parser.name != TAG_MANIFEST) { - parser.skipCurrentTag() - continue - } - if (parser.getAttributeValue(null, ATTR_PACKAGE) != context.packageName) { - break - } - while (parser.next() != XmlPullParser.END_TAG) { - if (parser.eventType != XmlPullParser.START_TAG) { - continue - } - if (parser.name != TAG_APPLICATION) { - parser.skipCurrentTag() - continue - } - return parser.getAttributeResourceValue( - NAMESPACE_ANDROID, ATTR_LOCALE_CONFIG, ResourcesCompat.ID_NULL - ) - } - } while (parser.next() != XmlPullParser.END_DOCUMENT) - } - ++cookie - } - return ResourcesCompat.ID_NULL - } - - private fun parseLocaleConfig(parser: XmlResourceParser): LocaleListCompat { - val localeNames = mutableSetOf() - do { - if (parser.eventType != XmlPullParser.START_TAG) { - continue - } - if (parser.name != TAG_LOCALE_CONFIG) { - parser.skipCurrentTag() - continue - } - while (parser.next() != XmlPullParser.END_TAG) { - if (parser.eventType != XmlPullParser.START_TAG) { - continue - } - if (parser.name != TAG_LOCALE) { - parser.skipCurrentTag() - continue - } - localeNames += parser.getAttributeValue(NAMESPACE_ANDROID, ATTR_NAME) - parser.skipCurrentTag() - } - } while (parser.next() != XmlPullParser.END_DOCUMENT) - return LocaleListCompat.forLanguageTags(localeNames.joinToString(",")) - } - - private fun XmlPullParser.skipCurrentTag() { - val outerDepth = depth - var type: Int - do { - type = next() - } while (type != XmlPullParser.END_DOCUMENT && - (type != XmlPullParser.END_TAG || depth > outerDepth)) - } - - companion object { - private const val TAG = "LocaleConfigCompat" - - private const val FILE_NAME_ANDROID_MANIFEST = "AndroidManifest.xml" - - private const val TAG_APPLICATION = "application" - private const val TAG_LOCALE_CONFIG = "locale-config" - private const val TAG_LOCALE = "locale" - private const val TAG_MANIFEST = "manifest" - - private const val NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android" - - private const val ATTR_LOCALE_CONFIG = "localeConfig" - private const val ATTR_NAME = "name" - private const val ATTR_PACKAGE = "package" - } - } - - @RequiresApi(Build.VERSION_CODES.TIRAMISU) - private class Api33Impl(context: Context) : Impl() { - override var status: Int = 0 - private set - - override var supportedLocales: LocaleListCompat? = null - private set - - init { - val platformLocaleConfig = LocaleConfig(context) - status = platformLocaleConfig.status - supportedLocales = platformLocaleConfig.supportedLocales - ?.let { LocaleListCompat.wrap(it) } - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/MediaMetadataRetrieverCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/MediaMetadataRetrieverCompat.kt deleted file mode 100644 index 012f29d53..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/MediaMetadataRetrieverCompat.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.graphics.Bitmap -import android.media.MediaMetadataRetriever -import android.os.Build -import androidx.annotation.RequiresApi -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.reflect.KClass - -val KClass.METADATA_KEY_SAMPLERATE: Int - @RequiresApi(Build.VERSION_CODES.Q) - get() = 38 - -fun MediaMetadataRetriever.getFrameAtTimeCompat( - timeUs: Long, - option: Int, - params: MediaMetadataRetriever.BitmapParams? -): Bitmap? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && params != null) { - getFrameAtTime(timeUs, option, params) - } else { - getFrameAtTime(timeUs, option) - } - -@RequiresApi(Build.VERSION_CODES.O_MR1) -fun MediaMetadataRetriever.getScaledFrameAtTimeCompat( - timeUs: Long, - option: Int, - dstWidth: Int, - dstHeight: Int, - params: MediaMetadataRetriever.BitmapParams? -): Bitmap? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && params != null) { - getScaledFrameAtTime(timeUs, option, dstWidth, dstHeight, params) - } else { - getScaledFrameAtTime(timeUs, option, dstWidth, dstHeight) - } - -@OptIn(ExperimentalContracts::class) -inline fun MediaMetadataRetriever.use(block: (MediaMetadataRetriever) -> R): R { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - val autoCloseable: AutoCloseable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - this - } else { - AutoCloseable { release() } - } - return autoCloseable.use { block(this) } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/MenuCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/MenuCompat.kt deleted file mode 100644 index 2e7846ca6..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/MenuCompat.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.view.Menu -import androidx.core.view.MenuCompat - -fun Menu.setGroupDividerEnabledCompat(groupDividerEnabled: Boolean) { - MenuCompat.setGroupDividerEnabled(this, groupDividerEnabled) -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/MimeTypeMapCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/MimeTypeMapCompat.kt deleted file mode 100644 index a351c0ff8..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/MimeTypeMapCompat.kt +++ /dev/null @@ -1,1617 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.webkit.MimeTypeMap - -// Generated by mime/generate-extensions.sh and mime/generate-code.sh . -private val extensionToMimeTypeMap = mapOf( - "%" to "application/x-trash", - "123" to "application/vnd.lotus-1-2-3", - "1905.1" to "application/vnd.ieee.1905", - "1clr" to "application/clr", - "1km" to "application/vnd.1000minds.decision-model+xml", - "210" to "application/p21", - "3dm" to "text/vnd.in3d.3dml", - "3dml" to "text/vnd.in3d.3dml", - "3g2" to "video/3gpp2", - "3ga" to "audio/3gpp", - "3gp" to "video/3gpp", - "3gp2" to "video/3gpp2", - "3gpp" to "video/3gpp", - "3gpp2" to "video/3gpp2", - "3mf" to "application/vnd.ms-3mfdocument", - "3tz" to "application/vnd.maxar.archive.3tz+zip", - "726" to "audio/32kadpcm", - "7z" to "application/x-7z-compressed", - "AMR" to "audio/AMR", - "AWB" to "audio/AMR-WB", - "CQL" to "text/cql", - "ELN" to "application/vnd.eln+zip", - "J2C" to "image/j2c", - "J2K" to "image/j2c", - "PGB" to "image/vnd.globalgraphics.pgb", - "QCP" to "audio/EVRC-QCP", - "SAR" to "application/vnd.sar", - "VES" to "application/vnd.ves.encrypted", - "VFK" to "text/vnd.exchangeable", - "a" to "text/vnd.a", - "a2l" to "application/A2L", - "a52" to "audio/ac3", - "aa3" to "audio/ATRAC3", - "aac" to "audio/aac", - "aal" to "audio/ATRAC-ADVANCED-LOSSLESS", - "abc" to "text/vnd.abc", - "abw" to "application/x-abiword", - "ac" to "application/pkix-attr-cert", - "ac2" to "application/vnd.banana-accounting", - "ac3" to "audio/ac3", - "acc" to "application/vnd.americandynamics.acc", - "acn" to "audio/asc", - "acu" to "application/vnd.acucobol", - "acutc" to "application/vnd.acucorp", - "adt" to "audio/aac", - "adts" to "audio/aac", - "aep" to "application/vnd.audiograph", - "afp" to "application/vnd.afpc.modca", - "age" to "application/vnd.age", - "ahead" to "application/vnd.ahead.space", - "ahv" to "application/vnd.android.haptics.vibration+xml", - "ai" to "application/postscript", - "aif" to "audio/x-aiff", - "aifc" to "audio/x-aiff", - "aiff" to "audio/x-aiff", - "aion" to "application/vnd.veritone.aion+json", - "ait" to "application/vnd.dvb.ait", - "alc" to "chemical/x-alchemy", - "ami" to "application/vnd.amiga.ami", - "aml" to "application/AML", - "amlx" to "application/automationml-amlx+zip", - "amr" to "audio/amr", - "anx" to "application/annodex", - "apex" to "application/vnd.apexlang", - "apexlang" to "application/vnd.apexlang", - "apk" to "application/vnd.android.package-archive", - "apkg" to "application/vnd.anki", - "apng" to "image/apng", - "appcache" to "text/cache-manifest", - "apr" to "application/vnd.lotus-approach", - "apxml" to "application/auth-policy+xml", - "arrow" to "application/vnd.apache.arrow.file", - "arrows" to "application/vnd.apache.arrow.stream", - "art" to "image/x-jg", - "artisan" to "application/vnd.artisan+json", - "arw" to "image/x-sony-arw", - "asc" to "application/pgp-keys", - "ascii" to "text/vnd.ascii-art", - "asf" to "application/vnd.ms-asf", - "asice" to "application/vnd.etsi.asic-e+zip", - "asics" to "application/vnd.etsi.asic-s+zip", - "asn" to "chemical/x-ncbi-asn1-spec", - "aso" to "chemical/x-ncbi-asn1-binary", - "ass" to "audio/aac", - "at3" to "audio/ATRAC3", - "atc" to "application/vnd.acucorp", - "atf" to "application/ATF", - "atfx" to "application/ATFX", - "atom" to "application/atom+xml", - "atomcat" to "application/atomcat+xml", - "atomdeleted" to "application/atomdeleted+xml", - "atomsrv" to "application/atomserv+xml", - "atomsvc" to "application/atomsvc+xml", - "atx" to "audio/ATRAC-X", - "atxml" to "application/ATXML", - "au" to "audio/basic", - "auc" to "application/tamp-apex-update-confirm", - "avci" to "image/avci", - "avcs" to "image/avcs", - "avi" to "video/avi", - "avif" to "image/avif", - "awb" to "audio/AMR-WB", - "axa" to "audio/annodex", - "axv" to "video/annodex", - "azf" to "application/vnd.airzip.filesecure.azf", - "azs" to "application/vnd.airzip.filesecure.azs", - "azv" to "image/vnd.airzip.accelerator.azv", - "azw3" to "application/vnd.amazon.mobi8-ebook", - "b" to "chemical/x-molconn-Z", - "b16" to "image/vnd.pco.b16", - "bak" to "application/x-trash", - "bar" to "application/vnd.qualcomm.brew-app-res", - "bary" to "model/vnd.bary", - "bat" to "application/x-msdos-program", - "bcpio" to "application/x-bcpio", - "bdm" to "application/vnd.syncml.dm+wbxml", - "bed" to "application/vnd.realvnc.bed", - "bh2" to "application/vnd.fujitsu.oasysprs", - "bib" to "text/x-bibtex", - "bik" to "video/vnd.radgamettools.bink", - "bin" to "application/octet-stream", - "bk2" to "video/vnd.radgamettools.bink", - "bkm" to "application/vnd.nervana", - "bmed" to "multipart/vnd.bint.med-plus", - "bmi" to "application/vnd.bmi", - "bmml" to "application/vnd.balsamiq.bmml+xml", - "bmp" to "image/x-ms-bmp", - "bmpr" to "application/vnd.balsamiq.bmpr", - "boo" to "text/x-boo", - "book" to "application/x-maker", - "box" to "application/vnd.previewsystems.box", - "bpd" to "application/vnd.hbci", - "brf" to "text/plain", - "bsd" to "chemical/x-crossfire", - "bsp" to "model/vnd.valve.source.compiled-map", - "btf" to "image/prs.btif", - "btif" to "image/prs.btif", - "c" to "text/x-csrc", - "c++" to "text/x-c++src", - "c11amc" to "application/vnd.cluetrust.cartomobile-config", - "c11amz" to "application/vnd.cluetrust.cartomobile-config-pkg", - "c3d" to "chemical/x-chem3d", - "c3ex" to "application/cccex", - "c4d" to "application/vnd.clonk.c4group", - "c4f" to "application/vnd.clonk.c4group", - "c4g" to "application/vnd.clonk.c4group", - "c4p" to "application/vnd.clonk.c4group", - "c4u" to "application/vnd.clonk.c4group", - "c9r" to "application/vnd.cryptomator.encrypted", - "c9s" to "application/vnd.cryptomator.encrypted", - "cab" to "application/vnd.ms-cab-compressed", - "cac" to "chemical/x-cache", - "cache" to "chemical/x-cache", - "cap" to "application/vnd.tcpdump.pcap", - "car" to "application/vnd.ipld.car", - "carjson" to "application/vnd.eu.kasparian.car+json", - "cascii" to "chemical/x-cactvs-binary", - "cat" to "application/vnd.ms-pki.seccat", - "cbin" to "chemical/x-cactvs-binary", - "cbor" to "application/cbor", - "cbr" to "application/vnd.comicbook-rar", - "cbz" to "application/vnd.comicbook+zip", - "cc" to "text/x-c++src", - "ccc" to "text/vnd.net2phone.commcenter.command", - "ccmp" to "application/ccmp+xml", - "ccxml" to "application/ccxml+xml", - "cda" to "application/x-cdf", - "cdbcmsg" to "application/vnd.contact.cmsg", - "cdf" to "application/x-cdf", - "cdfx" to "application/CDFX+XML", - "cdkey" to "application/vnd.mediastation.cdkey", - "cdmia" to "application/cdmi-capability", - "cdmic" to "application/cdmi-container", - "cdmid" to "application/cdmi-domain", - "cdmio" to "application/cdmi-object", - "cdmiq" to "application/cdmi-queue", - "cdr" to "image/x-coreldraw", - "cdt" to "image/x-coreldrawtemplate", - "cdx" to "chemical/x-cdx", - "cdxml" to "application/vnd.chemdraw+xml", - "cdy" to "application/vnd.cinderella", - "cea" to "application/CEA", - "cef" to "chemical/x-cxf", - "cellml" to "application/cellml+xml", - "cer" to "application/pkix-cert", - "cgm" to "image/cgm", - "chm" to "chemical/x-chemdraw", - "chrt" to "application/vnd.kde.kchart", - "cif" to "chemical/x-cif", - "cii" to "application/vnd.anser-web-certificate-issue-initiation", - "cil" to "application/vnd.ms-artgalry", - "cl" to "application/simple-filter+xml", - "cla" to "application/vnd.claymore", - "class" to "application/java-vm", - "cld" to "model/vnd.cld", - "clkk" to "application/vnd.crick.clicker.keyboard", - "clkp" to "application/vnd.crick.clicker.palette", - "clkt" to "application/vnd.crick.clicker.template", - "clkw" to "application/vnd.crick.clicker.wordbank", - "clkx" to "application/vnd.crick.clicker", - "cls" to "text/x-tex", - "clue" to "application/clue_info+xml", - "cmc" to "application/vnd.cosmocaller", - "cmdf" to "chemical/x-cmdf", - "cml" to "chemical/x-cml", - "cmp" to "application/vnd.yellowriver-custom-menu", - "cmsc" to "application/cms", - "cnd" to "text/jcr-cnd", - "cod" to "application/vnd.rim.cod", - "coffee" to "application/vnd.coffeescript", - "com" to "application/x-msdos-program", - "copyright" to "text/vnd.debian.copyright", - "coswid" to "application/swid+cbor", - "cpa" to "chemical/x-compass", - "cpio" to "application/x-cpio", - "cpkg" to "application/vnd.xmpie.cpkg", - "cpl" to "application/cpl+xml", - "cpp" to "text/x-c++src", - "cpt" to "image/x-corelphotopaint", - "cr2" to "image/x-canon-cr2", - "crl" to "application/pkix-crl", - "crt" to "application/x-x509-ca-cert", - "crtr" to "application/vnd.multiad.creator", - "crw" to "image/x-canon-crw", - "cryptomator" to "application/vnd.cryptomator.vault", - "cryptonote" to "application/vnd.rig.cryptonote", - "csd" to "audio/csound", - "csf" to "chemical/x-cache-csf", - "csh" to "text/x-csh", - "csl" to "application/vnd.citationstyles.style+xml", - "csm" to "chemical/x-csml", - "csml" to "chemical/x-csml", - "csp" to "application/vnd.commonspace", - "csrattrs" to "application/csrattrs", - "css" to "text/css", - "cst" to "application/vnd.commonspace", - "csv" to "text/comma-separated-values", - "csvs" to "text/csv-schema", - "ctab" to "chemical/x-cactvs-binary", - "ctx" to "chemical/x-ctx", - "cu" to "application/cu-seeme", - "cub" to "chemical/x-gaussian-cube", - "cuc" to "application/tamp-community-update-confirm", - "cur" to "image/ico", - "curl" to "text/vnd.curl", - "cw" to "application/prs.cww", - "cwl" to "application/cwl", - "cwl.json" to "application/cwl+json", - "cww" to "application/prs.cww", - "cxf" to "chemical/x-cxf", - "cxx" to "text/x-c++src", - "d" to "text/x-dsrc", - "dae" to "model/vnd.collada+xml", - "daf" to "application/vnd.Mobius.DAF", - "dart" to "application/vnd.dart", - "dataless" to "application/vnd.fdsn.seed", - "davmount" to "application/davmount+xml", - "dbf" to "application/vnd.dbf", - "dcd" to "application/DCD", - "dcm" to "application/dicom", - "dcr" to "application/x-director", - "dd2" to "application/vnd.oma.dd2+xml", - "ddd" to "application/vnd.fujixerox.ddd", - "ddeb" to "application/vnd.debian.binary-package", - "ddf" to "application/vnd.syncml.dmddf+xml", - "deb" to "application/vnd.debian.binary-package", - "deploy" to "application/octet-stream", - "der" to "application/x-x509-ca-cert", - "dfac" to "application/vnd.dreamfactory", - "dfxp" to "application/ttml+xml", - "dif" to "video/dv", - "diff" to "text/plain", - "dii" to "application/DII", - "dim" to "application/vnd.fastcopy-disk-image", - "dir" to "application/x-director", - "dis" to "application/vnd.Mobius.DIS", - "dist" to "application/vnd.apple.installer+xml", - "distz" to "application/vnd.apple.installer+xml", - "dit" to "application/DIT", - "dive" to "application/vnd.patentdive", - "djv" to "image/vnd.djvu", - "djvu" to "image/vnd.djvu", - "dl" to "application/vnd.datalog", - "dll" to "application/x-msdos-program", - "dls" to "audio/dls", - "dmg" to "application/x-apple-diskimage", - "dmp" to "application/vnd.tcpdump.pcap", - "dms" to "text/vnd.DMClientScript", - "dna" to "application/vnd.dna", - "dng" to "image/x-adobe-dng", - "doc" to "application/msword", - "docjson" to "application/vnd.document+json", - "docm" to "application/vnd.ms-word.document.macroEnabled.12", - "docx" to "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - "dor" to "model/vnd.gdl", - "dot" to "text/vnd.graphviz", - "dotm" to "application/vnd.ms-word.template.macroEnabled.12", - "dotx" to "application/vnd.openxmlformats-officedocument.wordprocessingml.template", - "dp" to "application/vnd.osgi.dp", - "dpg" to "application/vnd.dpgraph", - "dpgraph" to "application/vnd.dpgraph", - "dpkg" to "application/vnd.xmpie.dpkg", - "dpx" to "image/dpx", - "drle" to "image/dicom-rle", - "dsc" to "text/prs.lines.tag", - "dsm" to "application/vnd.desmume.movie", - "dssc" to "application/dssc+der", - "dtd" to "application/xml-dtd", - "dts" to "audio/vnd.dts", - "dtshd" to "audio/vnd.dts.hd", - "dv" to "video/dv", - "dvb" to "video/vnd.dvb.file", - "dvc" to "application/dvcs", - "dvi" to "application/x-dvi", - "dwd" to "application/atsc-dwd+xml", - "dwf" to "model/vnd.dwf", - "dwg" to "image/vnd.dwg", - "dx" to "chemical/x-jcamp-dx", - "dxf" to "image/vnd.dxf", - "dxp" to "application/vnd.spotfire.dxp", - "dxr" to "application/x-director", - "dzr" to "application/vnd.dzr", - "ebuild" to "application/vnd.gentoo.ebuild", - "ecelp4800" to "audio/vnd.nuera.ecelp4800", - "ecelp7470" to "audio/vnd.nuera.ecelp7470", - "ecelp9600" to "audio/vnd.nuera.ecelp9600", - "ecig" to "application/vnd.evolv.ecig.settings", - "ecigprofile" to "application/vnd.evolv.ecig.profile", - "ecigtheme" to "application/vnd.evolv.ecig.theme", - "eclass" to "application/vnd.gentoo.eclass", - "edm" to "application/vnd.novadigm.EDM", - "edx" to "application/vnd.novadigm.EDX", - "efi" to "application/efi", - "efif" to "application/vnd.picsel", - "ei6" to "application/vnd.pg.osasli", - "emb" to "chemical/x-embl-dl-nucleotide", - "embl" to "chemical/x-embl-dl-nucleotide", - "emf" to "image/emf", - "eml" to "message/rfc822", - "emm" to "application/vnd.ibm.electronic-media", - "emma" to "application/emma+xml", - "emotionml" to "application/emotionml+xml", - "ent" to "application/xml-external-parsed-entity", - "entity" to "application/vnd.nervana", - "enw" to "audio/EVRCNW", - "eol" to "audio/vnd.digital-winds", - "eot" to "application/vnd.ms-fontobject", - "ep" to "application/vnd.bluetooth.ep.oob", - "eps" to "application/postscript", - "eps2" to "application/postscript", - "eps3" to "application/postscript", - "epsf" to "application/postscript", - "epsi" to "application/postscript", - "epub" to "application/epub+zip", - "erf" to "image/x-epson-erf", - "es" to "text/javascript", - "es3" to "application/vnd.eszigno3+xml", - "esa" to "application/vnd.osgi.subsystem", - "esf" to "application/vnd.epson.esf", - "espass" to "application/vnd.espass-espass+zip", - "et3" to "application/vnd.eszigno3+xml", - "etx" to "text/x-setext", - "evb" to "audio/EVRCB", - "evc" to "audio/EVRC", - "evw" to "audio/EVRCWB", - "exe" to "application/x-msdos-program", - "exi" to "application/exi", - "exp" to "application/express", - "exr" to "image/aces", - "ext" to "application/vnd.novadigm.EXT", - "ez" to "application/andrew-inset", - "ez2" to "application/vnd.ezpix-album", - "ez3" to "application/vnd.ezpix-package", - "f4a" to "audio/mp4", - "f4b" to "audio/mp4", - "f4p" to "audio/mp4", - "f4v" to "video/mp4", - "fb" to "application/x-maker", - "fbdoc" to "application/x-maker", - "fbs" to "image/vnd.fastbidsheet", - "fcdt" to "application/vnd.adobe.formscentral.fcdt", - "fch" to "chemical/x-gaussian-checkpoint", - "fchk" to "chemical/x-gaussian-checkpoint", - "fcs" to "application/vnd.isac.fcs", - "fdf" to "application/fdf", - "fdt" to "application/fdt+xml", - "fe_launch" to "application/vnd.denovo.fcselayout-link", - "fg5" to "application/vnd.fujitsu.oasysgp", - "fig" to "application/x-xfig", - "finf" to "application/fastinfoset", - "fit" to "image/fits", - "fits" to "image/fits", - "fl" to "application/x-android-drm-fl", - "fla" to "application/vnd.dtg.local.flash", - "flac" to "audio/flac", - "flb" to "application/vnd.ficlab.flb+zip", - "fli" to "video/fli", - "flo" to "application/vnd.micrografx.flo", - "flt" to "text/vnd.ficlab.flt", - "flv" to "video/x-flv", - "flw" to "application/vnd.kde.kivio", - "flx" to "text/vnd.fmi.flexstor", - "fly" to "text/vnd.fly", - "fm" to "application/x-maker", - "fo" to "application/vnd.software602.filler.form+xml", - "fpx" to "image/vnd.fpx", - "frame" to "application/x-maker", - "frm" to "application/x-maker", - "fsc" to "application/vnd.fsc.weblaunch", - "fst" to "image/vnd.fst", - "ftc" to "application/vnd.fluxtime.clip", - "fti" to "application/vnd.anser-web-funds-transfer-initiation", - "fts" to "image/fits", - "fvt" to "video/vnd.fvt", - "fxp" to "application/vnd.adobe.fxp", - "fxpl" to "application/vnd.adobe.fxp", - "fzs" to "application/vnd.fuzzysheet", - "g2w" to "application/vnd.geoplan", - "g3w" to "application/vnd.geospace", - "gac" to "application/vnd.groove-account", - "gal" to "chemical/x-gaussian-log", - "gam" to "chemical/x-gamess-input", - "gamin" to "chemical/x-gamess-input", - "gan" to "application/x-ganttproject", - "gau" to "chemical/x-gaussian-input", - "gbr" to "application/rpki-ghostbusters", - "gcd" to "text/x-pcs-gcd", - "gcf" to "application/x-graphing-calculator", - "gcg" to "chemical/x-gcg8-sequence", - "gdl" to "model/vnd.gdl", - "gdz" to "application/vnd.familysearch.gedcom+zip", - "ged" to "text/vnd.familysearch.gedcom", - "gen" to "chemical/x-genbank", - "genozip" to "application/vnd.genozip", - "geo" to "application/vnd.dynageo", - "geojson" to "application/geo+json", - "gex" to "application/vnd.geometry-explorer", - "gf" to "application/x-tex-gf", - "gff3" to "text/gff3", - "ggb" to "application/vnd.geogebra.file", - "ggs" to "application/vnd.geogebra.slides", - "ggt" to "application/vnd.geogebra.tool", - "ghf" to "application/vnd.groove-help", - "gif" to "image/gif", - "gim" to "application/vnd.groove-identity-message", - "gjc" to "chemical/x-gaussian-input", - "gjf" to "chemical/x-gaussian-input", - "gl" to "video/gl", - "glb" to "model/gltf-binary", - "glbin" to "application/gltf-buffer", - "glbuf" to "application/gltf-buffer", - "gltf" to "model/gltf+json", - "gml" to "application/gml+xml", - "gnumeric" to "application/x-gnumeric", - "gph" to "application/vnd.FloGraphIt", - "gpkg" to "application/geopackage+sqlite3", - "gpkg.tar" to "application/vnd.gentoo.gpkg", - "gpt" to "chemical/x-mopac-graph", - "gqf" to "application/vnd.grafeq", - "gqs" to "application/vnd.grafeq", - "gram" to "application/srgs", - "grd" to "application/vnd.gentics.grd+json", - "gre" to "application/vnd.geometry-explorer", - "grv" to "application/vnd.groove-injector", - "grxml" to "application/srgs+xml", - "gsf" to "application/x-font", - "gsheet" to "application/urc-grpsheet+xml", - "gsm" to "audio/x-gsm", - "gtar" to "application/x-gtar", - "gtm" to "application/vnd.groove-tool-message", - "gtw" to "model/vnd.gtw", - "gv" to "text/vnd.graphviz", - "gxt" to "application/vnd.geonext", - "gz" to "application/gzip", - "h" to "text/x-chdr", - "h++" to "text/x-c++hdr", - "hal" to "application/vnd.hal+xml", - "hans" to "text/vnd.hans", - "hbc" to "application/vnd.hbci", - "hbci" to "application/vnd.hbci", - "hdf" to "application/x-hdf", - "hdr" to "image/vnd.radiance", - "hdt" to "application/vnd.hdt", - "heic" to "image/heic", - "heics" to "image/heic-sequence", - "heif" to "image/heif", - "heifs" to "image/heif-sequence", - "hej2" to "image/hej2k", - "held" to "application/atsc-held+xml", - "hgl" to "text/vnd.hgl", - "hh" to "text/x-c++hdr", - "hif" to "image/heif", - "hin" to "chemical/x-hin", - "hpgl" to "application/vnd.hp-HPGL", - "hpi" to "application/vnd.hp-hpid", - "hpid" to "application/vnd.hp-hpid", - "hpp" to "text/x-c++hdr", - "hps" to "application/vnd.hp-hps", - "hpub" to "application/prs.hpub+zip", - "hqx" to "application/mac-binhex40", - "hs" to "text/x-haskell", - "hsj2" to "image/hsj2", - "hsl" to "application/vnd.hsl", - "hta" to "application/hta", - "htc" to "text/x-component", - "htke" to "application/vnd.kenameaapp", - "htm" to "text/html", - "html" to "text/html", - "hvd" to "application/vnd.yamaha.hv-dic", - "hvp" to "application/vnd.yamaha.hv-voice", - "hvs" to "application/vnd.yamaha.hv-script", - "hwp" to "application/x-hwp", - "hxx" to "text/x-c++hdr", - "i2g" to "application/vnd.intergeo", - "ic0" to "application/vnd.commerce-battelle", - "ic1" to "application/vnd.commerce-battelle", - "ic2" to "application/vnd.commerce-battelle", - "ic3" to "application/vnd.commerce-battelle", - "ic4" to "application/vnd.commerce-battelle", - "ic5" to "application/vnd.commerce-battelle", - "ic6" to "application/vnd.commerce-battelle", - "ic7" to "application/vnd.commerce-battelle", - "ic8" to "application/vnd.commerce-battelle", - "ica" to "application/x-ica", - "icc" to "application/vnd.iccprofile", - "icd" to "application/vnd.commerce-battelle", - "icf" to "application/vnd.commerce-battelle", - "icm" to "application/vnd.iccprofile", - "ico" to "image/x-icon", - "ics" to "text/calendar", - "ief" to "image/ief", - "ifb" to "text/calendar", - "ifc" to "application/p21", - "ifm" to "application/vnd.shana.informed.formdata", - "iges" to "model/iges", - "igl" to "application/vnd.igloader", - "igm" to "application/vnd.insors.igm", - "ign" to "application/vnd.coreos.ignition+json", - "ignition" to "application/vnd.coreos.ignition+json", - "igs" to "model/iges", - "igx" to "application/vnd.micrografx.igx", - "iif" to "application/vnd.shana.informed.interchange", - "iii" to "application/x-iphone", - "imf" to "application/vnd.imagemeter.folder+zip", - "imgcal" to "application/vnd.3lightssoftware.imagescal", - "imi" to "application/vnd.imagemeter.image+zip", - "imp" to "application/vnd.accpac.simply.imp", - "ims" to "application/vnd.ms-ims", - "imscc" to "application/vnd.ims.imsccv1p1", - "imy" to "audio/imelody", - "info" to "application/x-info", - "ink" to "application/inkml+xml", - "inkml" to "application/inkml+xml", - "inp" to "chemical/x-gamess-input", - "ins" to "application/x-internet-signup", - "iota" to "application/vnd.astraea-software.iota", - "ipfix" to "application/ipfix", - "ipk" to "application/vnd.shana.informed.package", - "ipns-record" to "application/vnd.ipfs.ipns-record", - "irm" to "application/vnd.ibm.rights-management", - "irp" to "application/vnd.irepository.package+xml", - "ism" to "model/vnd.gdl", - "iso" to "application/x-iso9660-image", - "isp" to "application/x-internet-signup", - "ist" to "chemical/x-isostar", - "istc" to "application/vnd.veryant.thin", - "istr" to "chemical/x-isostar", - "isws" to "application/vnd.veryant.thin", - "itp" to "application/vnd.shana.informed.formtemplate", - "its" to "application/its+xml", - "ivp" to "application/vnd.immervision-ivp", - "ivu" to "application/vnd.immervision-ivu", - "j2c" to "image/j2c", - "j2k" to "image/j2c", - "jad" to "text/vnd.sun.j2me.app-descriptor", - "jam" to "application/vnd.jam", - "jar" to "application/java-archive", - "java" to "text/x-java", - "jdx" to "chemical/x-jcamp-dx", - "jfif" to "image/jpeg", - "jhc" to "image/jphc", - "jisp" to "application/vnd.jisp", - "jls" to "image/jls", - "jlt" to "application/vnd.hp-jlyt", - "jmz" to "application/x-jmol", - "jng" to "image/x-jng", - "jnlp" to "application/x-java-jnlp-file", - "joda" to "application/vnd.joost.joda-archive", - "jp2" to "image/jp2", - "jpe" to "image/jpeg", - "jpeg" to "image/jpeg", - "jpf" to "image/jpx", - "jpg" to "image/jpeg", - "jpg2" to "image/jp2", - "jpgm" to "image/jpm", - "jph" to "image/jph", - "jphc" to "image/jphc", - "jpm" to "image/jpm", - "jpx" to "image/jpx", - "jrd" to "application/jrd+json", - "js" to "text/javascript", - "json" to "application/json", - "json-patch" to "application/json-patch+json", - "jsonld" to "application/ld+json", - "jsontd" to "application/td+json", - "jsontm" to "application/tm+json", - "jt" to "model/JT", - "jtd" to "text/vnd.esmertec.theme-descriptor", - "jxl" to "image/jxl", - "jxr" to "image/jxr", - "jxra" to "image/jxrA", - "jxrs" to "image/jxrS", - "jxs" to "image/jxs", - "jxsc" to "image/jxsc", - "jxsi" to "image/jxsi", - "jxss" to "image/jxss", - "karbon" to "application/vnd.kde.karbon", - "kcm" to "application/vnd.nervana", - "key" to "application/pgp-keys", - "keynote" to "application/vnd.apple.keynote", - "kfo" to "application/vnd.kde.kformula", - "kia" to "application/vnd.kidspiration", - "kil" to "application/x-killustrator", - "kin" to "chemical/x-kinemage", - "kml" to "application/vnd.google-earth.kml+xml", - "kmz" to "application/vnd.google-earth.kmz", - "kne" to "application/vnd.Kinar", - "knp" to "application/vnd.Kinar", - "kom" to "application/vnd.hbci", - "kon" to "application/vnd.kde.kontour", - "koz" to "audio/vnd.audiokoz", - "kpr" to "application/vnd.kde.kpresenter", - "kpt" to "application/vnd.kde.kpresenter", - "ksp" to "application/vnd.kde.kspread", - "ktr" to "application/vnd.kahootz", - "ktx" to "image/ktx", - "ktx2" to "image/ktx2", - "ktz" to "application/vnd.kahootz", - "kwd" to "application/vnd.kde.kword", - "kwt" to "application/vnd.kde.kword", - "l16" to "audio/L16", - "las" to "application/vnd.las", - "lasjson" to "application/vnd.las.las+json", - "lasxml" to "application/vnd.las.las+xml", - "latex" to "application/x-latex", - "lbc" to "audio/iLBC", - "lbd" to "application/vnd.llamagraphics.life-balance.desktop", - "lbe" to "application/vnd.llamagraphics.life-balance.exchange+xml", - "lca" to "application/vnd.logipipe.circuit+zip", - "lcs" to "application/vnd.logipipe.circuit+zip", - "le" to "application/vnd.bluetooth.le.oob", - "les" to "application/vnd.hhe.lesson-player", - "lgr" to "application/lgr+xml", - "lha" to "application/x-lha", - "lhs" to "text/x-literate-haskell", - "lhzd" to "application/vnd.belightsoft.lhzd+zip", - "lhzl" to "application/vnd.belightsoft.lhzl+zip", - "lin" to "application/bbolin", - "line" to "application/vnd.nebumind.line", - "link66" to "application/vnd.route66.link66+xml", - "list3820" to "application/vnd.afpc.modca", - "listafp" to "application/vnd.afpc.modca", - "lmp" to "model/vnd.gdl", - "loas" to "audio/usac", - "loom" to "application/vnd.loom", - "lostsyncxml" to "application/lostsync+xml", - "lostxml" to "application/lost+xml", - "lpf" to "application/lpf+zip", - "lrc" to "application/lrc", - "lrm" to "application/vnd.ms-lrm", - "lsf" to "video/x-la-asf", - "lsx" to "video/x-la-asf", - "ltx" to "text/x-tex", - "lvp" to "audio/vnd.lucent.voice", - "lwp" to "application/vnd.lotus-wordpro", - "lxf" to "application/LXF", - "ly" to "text/x-lilypond", - "lyx" to "application/x-lyx", - "lzh" to "application/x-lzh", - "lzx" to "application/x-lzx", - "m" to "application/vnd.wolfram.mathematica.package", - "m1v" to "video/mpeg", - "m21" to "application/mp21", - "m2t" to "video/mpeg", - "m2ts" to "video/mp2t", - "m2v" to "video/mpeg", - "m3g" to "application/m3g", - "m3u" to "audio/x-mpegurl", - "m3u8" to "audio/x-mpegurl", - "m4a" to "audio/mpeg", - "m4b" to "audio/mp4", - "m4p" to "audio/mp4", - "m4r" to "audio/mpeg", - "m4s" to "video/iso.segment", - "m4u" to "video/vnd.mpegurl", - "m4v" to "video/mp4", - "ma" to "application/mathematica", - "mads" to "application/mads+xml", - "maei" to "application/mmt-aei+xml", - "mag" to "application/vnd.ecowin.chart", - "mail" to "message/rfc822", - "maker" to "application/x-maker", - "man" to "application/x-troff-man", - "manifest" to "text/cache-manifest", - "markdown" to "text/markdown", - "mb" to "application/mathematica", - "mbk" to "application/vnd.Mobius.MBK", - "mbox" to "application/mbox", - "mbsdf" to "application/vnd.mdl-mbsdf", - "mc1" to "application/vnd.medcalcdata", - "mc2" to "text/vnd.senx.warpscript", - "mcd" to "application/vnd.mcd", - "mcif" to "chemical/x-mmcif", - "mcm" to "chemical/x-macmolecule", - "md" to "text/markdown", - "mdb" to "application/msaccess", - "mdc" to "application/vnd.marlin.drm.mdcf", - "mdi" to "image/vnd.ms-modi", - "mdl" to "application/vnd.mdl", - "me" to "application/x-troff-me", - "mesh" to "model/mesh", - "meta4" to "application/metalink4+xml", - "mets" to "application/mets+xml", - "mf4" to "application/MF4", - "mfm" to "application/vnd.mfmp", - "mft" to "application/rpki-manifest", - "mgp" to "application/vnd.osgeo.mapguide.package", - "mgz" to "application/vnd.proteus.magazine", - "mhas" to "audio/mhas", - "mid" to "audio/sp-midi", - "mif" to "application/vnd.mif", - "miz" to "text/mizar", - "mj2" to "video/mj2", - "mjp2" to "video/mj2", - "mjs" to "text/javascript", - "mka" to "audio/x-matroska", - "mkv" to "video/x-matroska", - "ml2" to "application/vnd.sybyl.mol2", - "mlp" to "audio/vnd.dolby.mlp", - "mm" to "application/x-freemind", - "mmd" to "application/vnd.chipnuts.karaoke-mmd", - "mmdb" to "application/vnd.maxmind.maxmind-db", - "mmf" to "application/vnd.smaf", - "mml" to "application/mathml+xml", - "mmod" to "chemical/x-macromodel-input", - "mmr" to "image/vnd.fujixerox.edmics-mmr", - "mng" to "video/x-mng", - "mobi" to "application/x-mobipocket-ebook", - "moc" to "text/x-moc", - "mod" to "application/xml-dtd", - "model-inter" to "application/vnd.vd-study", - "modl" to "application/vnd.modl", - "mods" to "application/mods+xml", - "mol" to "chemical/x-mdl-molfile", - "mol2" to "application/vnd.sybyl.mol2", - "moml" to "model/vnd.moml+xml", - "moo" to "chemical/x-mopac-out", - "mop" to "chemical/x-mopac-input", - "mopcrt" to "chemical/x-mopac-input", - "mov" to "video/quicktime", - "movie" to "video/x-sgi-movie", - "mp1" to "audio/mpeg", - "mp1v" to "video/mpeg", - "mp2" to "audio/mpeg", - "mp21" to "application/mp21", - "mp2v" to "video/mpeg", - "mp3" to "audio/mpeg", - "mp4" to "video/mp4", - "mp4v" to "video/mp4", - "mpa" to "audio/mpeg", - "mpc" to "chemical/x-mopac-input", - "mpd" to "application/dash+xml", - "mpdd" to "application/dashdelta", - "mpe" to "video/mpeg", - "mpeg" to "video/mpeg", - "mpeg1" to "video/mpeg", - "mpeg2" to "video/mpeg", - "mpeg4" to "video/mp4", - "mpega" to "audio/mpeg", - "mpf" to "text/vnd.ms-mediapackage", - "mpg" to "video/mpeg", - "mpg4" to "video/mp4", - "mpga" to "audio/mpeg", - "mph" to "application/x-comsol", - "mpkg" to "application/vnd.apple.installer+xml", - "mpm" to "application/vnd.blueice.multipass", - "mpn" to "application/vnd.mophun.application", - "mpp" to "application/vnd.ms-project", - "mpt" to "application/vnd.ms-project", - "mpv" to "video/x-matroska", - "mpv1" to "video/mpeg", - "mpv2" to "video/mpeg", - "mpw" to "application/vnd.exstream-empower+zip", - "mpy" to "application/vnd.ibm.MiniPay", - "mqy" to "application/vnd.Mobius.MQY", - "mrc" to "application/marc", - "mrcx" to "application/marcxml+xml", - "ms" to "application/x-troff-ms", - "msa" to "application/vnd.msa-disk-image", - "msd" to "application/vnd.fdsn.mseed", - "mseed" to "application/vnd.fdsn.mseed", - "mseq" to "application/vnd.mseq", - "msf" to "application/vnd.epson.msf", - "msh" to "model/mesh", - "msi" to "application/x-msi", - "msl" to "application/vnd.Mobius.MSL", - "msm" to "model/vnd.gdl", - "msp" to "application/octet-stream", - "msty" to "application/vnd.muvee.style", - "msu" to "application/octet-stream", - "mtl" to "model/mtl", - "mts" to "video/mp2t", - "multitrack" to "audio/vnd.presonus.multitrack", - "mus" to "application/vnd.musician", - "musd" to "application/mmt-usd+xml", - "mvb" to "chemical/x-mopac-vib", - "mvt" to "application/vnd.mapbox-vector-tile", - "mwc" to "application/vnd.dpgraph", - "mwf" to "application/vnd.MFER", - "mxf" to "application/mxf", - "mxi" to "application/vnd.vd-study", - "mxl" to "application/vnd.recordare.musicxml", - "mxmf" to "audio/mobile-xmf", - "mxml" to "application/xv+xml", - "mxs" to "application/vnd.triscape.mxs", - "mxu" to "video/vnd.mpegurl", - "n3" to "text/n3", - "nb" to "application/vnd.wolfram.mathematica", - "nbp" to "application/vnd.wolfram.player", - "nc" to "application/x-netcdf", - "ndc" to "application/vnd.osa.netdeploy", - "ndl" to "application/vnd.lotus-notes", - "nds" to "application/vnd.nintendo.nitro.rom", - "nebul" to "application/vnd.nebumind.line", - "nef" to "image/x-nikon-nef", - "ngdat" to "application/vnd.nokia.n-gage.data", - "nim" to "video/vnd.nokia.interleaved-multimedia", - "nimn" to "application/vnd.nimn", - "nitf" to "application/vnd.nitf", - "nlu" to "application/vnd.neurolanguage.nlu", - "nml" to "application/vnd.enliven", - "nnd" to "application/vnd.noblenet-directory", - "nns" to "application/vnd.noblenet-sealer", - "nnw" to "application/vnd.noblenet-web", - "notebook" to "application/vnd.smart.notebook", - "nq" to "application/n-quads", - "nrw" to "image/x-nikon-nrw", - "ns2" to "application/vnd.lotus-notes", - "ns3" to "application/vnd.lotus-notes", - "ns4" to "application/vnd.lotus-notes", - "nsf" to "application/vnd.lotus-notes", - "nsg" to "application/vnd.lotus-notes", - "nsh" to "application/vnd.lotus-notes", - "nt" to "application/n-triples", - "ntf" to "application/vnd.lotus-notes", - "numbers" to "application/vnd.apple.numbers", - "nwc" to "application/x-nwc", - "o" to "application/x-object", - "oa2" to "application/vnd.fujitsu.oasys2", - "oa3" to "application/vnd.fujitsu.oasys3", - "oas" to "application/vnd.fujitsu.oasys", - "ob" to "application/vnd.1ob", - "obg" to "application/vnd.openblox.game-binary", - "obgx" to "application/vnd.openblox.game+xml", - "obj" to "model/obj", - "oda" to "application/ODA", - "odb" to "application/vnd.oasis.opendocument.base", - "odc" to "application/vnd.oasis.opendocument.chart", - "odd" to "application/tei+xml", - "odf" to "application/vnd.oasis.opendocument.formula", - "odg" to "application/vnd.oasis.opendocument.graphics", - "odi" to "application/vnd.oasis.opendocument.image", - "odm" to "application/vnd.oasis.opendocument.text-master", - "odp" to "application/vnd.oasis.opendocument.presentation", - "ods" to "application/vnd.oasis.opendocument.spreadsheet", - "odt" to "application/vnd.oasis.opendocument.text", - "odx" to "application/ODX", - "oeb" to "application/vnd.openeye.oeb", - "oga" to "audio/ogg", - "ogex" to "model/vnd.opengex", - "ogg" to "audio/ogg", - "ogv" to "video/ogg", - "ogx" to "application/ogg", - "old" to "application/x-trash", - "omg" to "audio/ATRAC3", - "one" to "application/onenote", - "onepkg" to "application/onenote", - "onetmp" to "application/onenote", - "onetoc2" to "application/onenote", - "opf" to "application/oebps-package+xml", - "oprc" to "application/vnd.palm", - "opus" to "audio/ogg", - "or2" to "application/vnd.lotus-organizer", - "or3" to "application/vnd.lotus-organizer", - "orc" to "audio/csound", - "orf" to "image/x-olympus-orf", - "org" to "application/vnd.lotus-organizer", - "orq" to "application/ocsp-request", - "ors" to "application/ocsp-response", - "osf" to "application/vnd.yamaha.openscoreformat", - "osm" to "application/vnd.openstreetmap.data+xml", - "ota" to "application/vnd.android.ota", - "otc" to "application/vnd.oasis.opendocument.chart-template", - "otf" to "font/otf", - "otg" to "application/vnd.oasis.opendocument.graphics-template", - "oth" to "application/vnd.oasis.opendocument.text-web", - "oti" to "application/vnd.oasis.opendocument.image-template", - "otm" to "application/vnd.oasis.opendocument.text-master-template", - "otp" to "application/vnd.oasis.opendocument.presentation-template", - "ots" to "application/vnd.oasis.opendocument.spreadsheet-template", - "ott" to "application/vnd.oasis.opendocument.text-template", - "ovl" to "application/vnd.afpc.modca-overlay", - "oxlicg" to "application/vnd.oxli.countgraph", - "oxps" to "application/oxps", - "oxt" to "application/vnd.openofficeorg.extension", - "oza" to "application/x-oz-application", - "p" to "text/x-pascal", - "p10" to "application/pkcs10", - "p12" to "application/x-pkcs12", - "p21" to "application/p21", - "p2p" to "application/vnd.wfa.p2p", - "p7c" to "application/pkcs7-mime", - "p7m" to "application/pkcs7-mime", - "p7r" to "application/x-pkcs7-certreqresp", - "p7s" to "application/pkcs7-signature", - "p7z" to "application/pkcs7-mime", - "p8" to "application/pkcs8", - "p8e" to "application/pkcs8-encrypted", - "pac" to "application/x-ns-proxy-autoconfig", - "package" to "application/vnd.autopackage", - "pages" to "application/vnd.apple.pages", - "pas" to "text/x-pascal", - "pat" to "image/x-coreldrawpattern", - "patch" to "text/x-diff", - "paw" to "application/vnd.pawaafile", - "pbd" to "application/vnd.powerbuilder6", - "pbm" to "image/x-portable-bitmap", - "pcap" to "application/vnd.tcpdump.pcap", - "pcf" to "application/x-font", - "pcf.Z" to "application/x-font-pcf", - "pcl" to "application/vnd.hp-PCL", - "pcx" to "image/vnd.zbrush.pcx", - "pdb" to "chemical/x-pdb", - "pdf" to "application/pdf", - "pdx" to "application/PDX", - "pef" to "image/x-pentax-pef", - "pem" to "application/x-pem-file", - "pfa" to "application/x-font", - "pfb" to "application/x-font", - "pfr" to "application/font-tdpfr", - "pfx" to "application/x-pkcs12", - "pgb" to "image/vnd.globalgraphics.pgb", - "pgm" to "image/x-portable-graymap", - "pgn" to "application/vnd.chess-pgn", - "pgp" to "application/pgp-signature", - "phps" to "text/text", - "pil" to "application/vnd.piaccess.application-licence", - "pk" to "application/x-tex-pk", - "pkd" to "application/vnd.hbci", - "pkg" to "application/vnd.apple.installer+xml", - "pki" to "application/pkixcmp", - "pkipath" to "application/pkix-pkipath", - "pkpass" to "application/vnd.apple.pkpass", - "pkpasses" to "application/vnd.apple.pkpasses", - "pl" to "text/x-perl", - "plb" to "application/vnd.3gpp.pic-bw-large", - "plc" to "application/vnd.Mobius.PLC", - "plf" to "application/vnd.pocketlearn", - "plj" to "audio/vnd.everad.plj", - "plp" to "application/vnd.panoply", - "pls" to "audio/x-scpls", - "pm" to "text/x-perl", - "pml" to "application/vnd.ctc-posml", - "png" to "image/png", - "pnm" to "image/x-portable-anymap", - "po" to "text/plain", - "portpkg" to "application/vnd.macports.portpkg", - "pot" to "application/vnd.ms-powerpoint", - "potm" to "application/vnd.ms-powerpoint.template.macroEnabled.12", - "potx" to "application/vnd.openxmlformats-officedocument.presentationml.template", - "ppam" to "application/vnd.ms-powerpoint.addin.macroEnabled.12", - "ppd" to "application/vnd.cups-ppd", - "ppkg" to "application/vnd.xmpie.ppkg", - "ppm" to "image/x-portable-pixmap", - "pps" to "application/vnd.ms-powerpoint", - "ppsm" to "application/vnd.ms-powerpoint.slideshow.macroEnabled.12", - "ppsx" to "application/vnd.openxmlformats-officedocument.presentationml.slideshow", - "ppt" to "application/vnd.ms-powerpoint", - "pptm" to "application/vnd.ms-powerpoint.presentation.macroEnabled.12", - "ppttc" to "application/vnd.think-cell.ppttc+json", - "pptx" to "application/vnd.openxmlformats-officedocument.presentationml.presentation", - "pqa" to "application/vnd.palm", - "prc" to "application/x-mobipocket-ebook", - "pre" to "application/vnd.lotus-freelance", - "preminet" to "application/vnd.preminet", - "prf" to "application/pics-rules", - "provn" to "text/provenance-notation", - "provx" to "application/provenance+xml", - "prt" to "chemical/x-ncbi-asn1-ascii", - "prz" to "application/vnd.lotus-freelance", - "ps" to "application/postscript", - "psb" to "application/vnd.3gpp.pic-bw-small", - "psd" to "image/x-photoshop", - "pseg3820" to "application/vnd.afpc.modca", - "psfs" to "application/vnd.psfs", - "psg" to "application/vnd.afpc.modca-pagesegment", - "psid" to "audio/prs.sid", - "pskcxml" to "application/pskc+xml", - "pt" to "application/vnd.snesdev-page-table", - "pti" to "image/prs.pti", - "ptid" to "application/vnd.pvi.ptid1", - "ptrom" to "application/vnd.snesdev-page-table", - "pub" to "application/vnd.exstream-package", - "pvb" to "application/vnd.3gpp.pic-bw-var", - "pwn" to "application/vnd.3M.Post-it-Notes", - "py" to "text/x-python", - "pya" to "audio/vnd.ms-playready.media.pya", - "pyc" to "application/x-python-code", - "pyo" to "application/x-python-code", - "pyox" to "model/vnd.pytha.pyox", - "pyv" to "video/vnd.ms-playready.media.pyv", - "qam" to "application/vnd.epson.quickanime", - "qbo" to "application/vnd.intu.qbo", - "qca" to "application/vnd.ericsson.quickcall", - "qcall" to "application/vnd.ericsson.quickcall", - "qcp" to "audio/EVRC-QCP", - "qfx" to "application/vnd.intu.qfx", - "qgs" to "application/x-qgis", - "qps" to "application/vnd.publishare-delta-tree", - "qt" to "video/quicktime", - "qtl" to "application/x-quicktimeplayer", - "quiz" to "application/vnd.quobject-quoxdocument", - "quox" to "application/vnd.quobject-quoxdocument", - "qvd" to "application/vnd.theqvd", - "qwd" to "application/vnd.Quark.QuarkXPress", - "qwt" to "application/vnd.Quark.QuarkXPress", - "qxb" to "application/vnd.Quark.QuarkXPress", - "qxd" to "application/vnd.Quark.QuarkXPress", - "qxl" to "application/vnd.Quark.QuarkXPress", - "qxt" to "application/vnd.Quark.QuarkXPress", - "ra" to "audio/x-pn-realaudio", - "raf" to "image/x-fuji-raf", - "ram" to "audio/x-pn-realaudio", - "rapd" to "application/route-apd+xml", - "rar" to "application/vnd.rar", - "ras" to "image/x-cmu-raster", - "rb" to "application/x-ruby", - "rcprofile" to "application/vnd.ipunplugged.rcprofile", - "rct" to "application/prs.nprend", - "rd" to "chemical/x-mdl-rdfile", - "rdf" to "application/rdf+xml", - "rdf-crypt" to "application/prs.rdf-xml-crypt", - "rdp" to "application/x-rdp", - "rdz" to "application/vnd.data-vision.rdz", - "relo" to "application/p2p-overlay+xml", - "reload" to "application/vnd.resilient.logic", - "rep" to "application/vnd.businessobjects", - "request" to "application/vnd.nervana", - "rfcxml" to "application/rfc+xml", - "rgb" to "image/x-rgb", - "rgbe" to "image/vnd.radiance", - "rif" to "application/reginfo+xml", - "rip" to "audio/vnd.rip", - "rl" to "application/resource-lists+xml", - "rlc" to "image/vnd.fujixerox.edmics-rlc", - "rld" to "application/resource-lists-diff+xml", - "rlm" to "application/vnd.resilient.logic", - "rm" to "audio/x-pn-realaudio", - "rms" to "application/vnd.jcp.javame.midlet-rms", - "rnc" to "application/relax-ng-compact-syntax", - "rnd" to "application/prs.nprend", - "roa" to "application/rpki-roa", - "roff" to "text/troff", - "ros" to "chemical/x-rosdal", - "rp9" to "application/vnd.cloanto.rp9", - "rpm" to "application/x-redhat-package-manager", - "rpss" to "application/vnd.nokia.radio-presets", - "rpst" to "application/vnd.nokia.radio-preset", - "rq" to "application/sparql-query", - "rs" to "application/rls-services+xml", - "rsat" to "application/atsc-rsat+xml", - "rsheet" to "application/urc-ressheet+xml", - "rsm" to "model/vnd.gdl", - "rss" to "application/rss+xml", - "rst" to "text/prs.fallenstein.rst", - "rtf" to "text/rtf", - "rtttl" to "audio/midi", - "rtx" to "audio/midi", - "rusd" to "application/route-usd+xml", - "rw2" to "image/x-panasonic-rw2", - "rxn" to "chemical/x-mdl-rxnfile", - "rxt" to "application/vnd.medicalholodeck.recordxr", - "s11" to "video/vnd.sealed.mpeg1", - "s14" to "video/vnd.sealed.mpeg4", - "s1a" to "application/vnd.sealedmedia.softseal.pdf", - "s1e" to "application/vnd.sealed.xls", - "s1g" to "image/vnd.sealedmedia.softseal.gif", - "s1h" to "application/vnd.sealedmedia.softseal.html", - "s1j" to "image/vnd.sealedmedia.softseal.jpg", - "s1m" to "audio/vnd.sealedmedia.softseal.mpeg", - "s1n" to "image/vnd.sealed.png", - "s1p" to "application/vnd.sealed.ppt", - "s1q" to "video/vnd.sealedmedia.softseal.mov", - "s1w" to "application/vnd.sealed.doc", - "s3df" to "application/vnd.sealed.3df", - "sac" to "application/tamp-sequence-adjust-confirm", - "saf" to "application/vnd.yamaha.smaf-audio", - "sam" to "application/vnd.lotus-wordpro", - "sarif" to "application/sarif+json", - "sarif-external-properties" to "application/sarif-external-properties+json", - "sarif-external-properties.json" to "application/sarif-external-properties+json", - "sarif.json" to "application/sarif+json", - "sc" to "application/vnd.ibm.secure-container", - "scala" to "text/x-scala", - "scd" to "application/vnd.scribus", - "sce" to "application/x-scilab", - "sci" to "application/x-scilab", - "scim" to "application/scim+json", - "scl" to "application/vnd.sycle+xml", - "scld" to "application/vnd.doremir.scorecloud-binary-document", - "scm" to "application/vnd.lotus-screencam", - "sco" to "audio/csound", - "scq" to "application/scvp-cv-request", - "scr" to "application/x-silverlight", - "scs" to "application/scvp-cv-response", - "scsf" to "application/vnd.sealed.csf", - "sd" to "chemical/x-mdl-sdfile", - "sd2" to "audio/x-sd2", - "sda" to "application/vnd.stardivision.draw", - "sdc" to "application/vnd.stardivision.calc", - "sdd" to "application/vnd.stardivision.impress", - "sdf" to "chemical/x-mdl-sdfile", - "sdkd" to "application/vnd.solent.sdkm+xml", - "sdkm" to "application/vnd.solent.sdkm+xml", - "sdo" to "application/vnd.sealed.doc", - "sdoc" to "application/vnd.sealed.doc", - "sdp" to "application/vnd.stardivision.impress", - "sds" to "application/vnd.stardivision.chart", - "sdw" to "application/vnd.stardivision.writer", - "see" to "application/vnd.seemail", - "seed" to "application/vnd.fdsn.seed", - "sem" to "application/vnd.sealed.eml", - "sema" to "application/vnd.sema", - "semd" to "application/vnd.semd", - "semf" to "application/vnd.semf", - "seml" to "application/vnd.sealed.eml", - "senml" to "application/senml+json", - "senml-etchc" to "application/senml-etch+cbor", - "senml-etchj" to "application/senml-etch+json", - "senmlc" to "application/senml+cbor", - "senmle" to "application/senml-exi", - "senmlx" to "application/senml+xml", - "sensml" to "application/sensml+json", - "sensmlc" to "application/sensml+cbor", - "sensmle" to "application/sensml-exi", - "sensmlx" to "application/sensml+xml", - "ser" to "application/java-serialized-object", - "sfc" to "application/vnd.nintendo.snes.rom", - "sfd" to "application/vnd.font-fontforge-sfd", - "sfd-hdstx" to "application/vnd.hydrostatix.sof-data", - "sfs" to "application/vnd.spotfire.sfs", - "sfv" to "text/x-sfv", - "sgf" to "application/x-go-sgf", - "sgi" to "image/vnd.sealedmedia.softseal.gif", - "sgif" to "image/vnd.sealedmedia.softseal.gif", - "sgl" to "application/vnd.stardivision.writer-global", - "sgm" to "text/SGML", - "sgml" to "text/SGML", - "sh" to "text/x-sh", - "shaclc" to "text/shaclc", - "shar" to "application/x-shar", - "shc" to "text/shaclc", - "shex" to "text/shex", - "shf" to "application/shf+xml", - "shp" to "application/x-qgis", - "shtml" to "text/html", - "shx" to "application/x-qgis", - "si" to "text/vnd.wap.si", - "sic" to "application/vnd.wap.sic", - "sid" to "audio/prs.sid", - "sieve" to "application/sieve", - "sig" to "application/pgp-signature", - "sik" to "application/x-trash", - "silo" to "model/mesh", - "sipa" to "application/vnd.smintio.portals.archive", - "sis" to "application/vnd.symbian.install", - "sit" to "application/x-stuffit", - "sitx" to "application/x-stuffit", - "siv" to "application/sieve", - "sjp" to "image/vnd.sealedmedia.softseal.jpg", - "sjpg" to "image/vnd.sealedmedia.softseal.jpg", - "skd" to "application/vnd.koan", - "skm" to "application/vnd.koan", - "skp" to "application/vnd.koan", - "skt" to "application/vnd.koan", - "sl" to "text/vnd.wap.sl", - "sla" to "application/vnd.scribus", - "slaz" to "application/vnd.scribus", - "slc" to "application/vnd.wap.slc", - "sldm" to "application/vnd.ms-powerpoint.slide.macroEnabled.12", - "sldx" to "application/vnd.openxmlformats-officedocument.presentationml.slide", - "sls" to "application/route-s-tsid+xml", - "slt" to "application/vnd.epson.salt", - "sm" to "application/vnd.stepmania.stepchart", - "smc" to "application/vnd.nintendo.snes.rom", - "smf" to "audio/sp-midi", - "smh" to "application/vnd.sealed.mht", - "smht" to "application/vnd.sealed.mht", - "smi" to "application/smil+xml", - "smil" to "application/smil+xml", - "smk" to "video/vnd.radgamettools.smacker", - "sml" to "application/smil+xml", - "smo" to "video/vnd.sealedmedia.softseal.mov", - "smov" to "video/vnd.sealedmedia.softseal.mov", - "smp" to "audio/vnd.sealedmedia.softseal.mpeg", - "smp3" to "audio/vnd.sealedmedia.softseal.mpeg", - "smpg" to "video/vnd.sealed.mpeg1", - "sms" to "application/vnd.3gpp2.sms", - "smv" to "audio/SMV", - "smzip" to "application/vnd.stepmania.package", - "snd" to "audio/basic", - "soa" to "text/dns", - "soc" to "application/sgml-open-catalog", - "sofa" to "audio/sofa", - "sos" to "text/vnd.sosi", - "spc" to "chemical/x-galactic-spc", - "spd" to "application/vnd.sealedmedia.softseal.pdf", - "spdf" to "application/vnd.sealedmedia.softseal.pdf", - "spdx" to "text/spdx", - "spdx.json" to "application/spdx+json", - "spf" to "application/vnd.yamaha.smaf-phrase", - "spl" to "application/futuresplash", - "spn" to "image/vnd.sealed.png", - "spng" to "image/vnd.sealed.png", - "spo" to "text/vnd.in3d.spot", - "spot" to "text/vnd.in3d.spot", - "spp" to "application/scvp-vp-response", - "sppt" to "application/vnd.sealed.ppt", - "spq" to "application/scvp-vp-request", - "spx" to "audio/ogg", - "sql" to "application/sql", - "sqlite" to "application/vnd.sqlite3", - "sqlite3" to "application/vnd.sqlite3", - "sr" to "application/vnd.sigrok.session", - "src" to "application/x-wais-source", - "srt" to "application/x-subrip", - "sru" to "application/sru+xml", - "srw" to "image/x-samsung-srw", - "srx" to "application/sparql-results+xml", - "sse" to "application/vnd.kodak-descriptor", - "ssf" to "application/vnd.epson.ssf", - "ssml" to "application/ssml+xml", - "ssv" to "application/vnd.shade-save-file", - "ssvc" to "application/vnd.crypto-shade-file", - "ssw" to "video/vnd.sealed.swf", - "sswf" to "video/vnd.sealed.swf", - "st" to "application/vnd.sailingtracker.track", - "stc" to "application/vnd.sun.xml.calc.template", - "std" to "application/vnd.sun.xml.draw.template", - "step" to "model/step", - "stf" to "application/vnd.wt.stf", - "sti" to "application/vnd.sun.xml.impress.template", - "stif" to "application/vnd.sealed.tiff", - "stix" to "application/stix+json", - "stk" to "application/hyperstudio", - "stl" to "application/vnd.ms-pki.stl", - "stml" to "application/vnd.sealedmedia.softseal.html", - "stp" to "model/step", - "stpnc" to "application/p21", - "stpx" to "model/step+xml", - "stpxz" to "model/step-xml+zip", - "stpz" to "model/step+zip", - "str" to "application/vnd.pg.format", - "study-inter" to "application/vnd.vd-study", - "stw" to "application/vnd.sun.xml.writer.template", - "sty" to "text/x-tex", - "sus" to "application/vnd.sus-calendar", - "susp" to "application/vnd.sus-calendar", - "sv4cpio" to "application/x-sv4cpio", - "sv4crc" to "application/x-sv4crc", - "svc" to "application/vnd.dvb.service", - "svg" to "image/svg+xml", - "svgz" to "image/svg+xml", - "sw" to "chemical/x-swissprot", - "swf" to "application/vnd.adobe.flash.movie", - "swi" to "application/vnd.aristanetworks.swi", - "swidtag" to "application/swid+xml", - "sxc" to "application/vnd.sun.xml.calc", - "sxd" to "application/vnd.sun.xml.draw", - "sxg" to "application/vnd.sun.xml.writer.global", - "sxi" to "application/vnd.sun.xml.impress", - "sxl" to "application/vnd.sealed.xls", - "sxls" to "application/vnd.sealed.xls", - "sxm" to "application/vnd.sun.xml.math", - "sxw" to "application/vnd.sun.xml.writer", - "sy2" to "application/vnd.sybyl.mol2", - "syft.json" to "application/vnd.syft+json", - "t" to "text/troff", - "tag" to "text/prs.lines.tag", - "taglet" to "application/vnd.mynfc", - "tam" to "application/vnd.onepager", - "tamp" to "application/vnd.onepagertamp", - "tamx" to "application/vnd.onepagertamx", - "tao" to "application/vnd.tao.intent-module-archive", - "tap" to "image/vnd.tencent.tap", - "tar" to "application/x-tar", - "tat" to "application/vnd.onepagertat", - "tatp" to "application/vnd.onepagertatp", - "tatx" to "application/vnd.onepagertatx", - "tau" to "application/tamp-apex-update", - "taz" to "application/x-gtar-compressed", - "tcap" to "application/vnd.3gpp2.tcap", - "tcl" to "text/x-tcl", - "tcu" to "application/tamp-community-update", - "td" to "application/urc-targetdesc+xml", - "teacher" to "application/vnd.smart.teacher", - "tei" to "application/tei+xml", - "teiCorpus" to "application/tei+xml", - "ter" to "application/tamp-error", - "tex" to "text/x-tex", - "texi" to "application/x-texinfo", - "texinfo" to "application/x-texinfo", - "text" to "text/plain", - "tfi" to "application/thraud+xml", - "tfx" to "image/tiff-fx", - "tgf" to "chemical/x-mdl-tgf", - "tgz" to "application/x-gtar-compressed", - "thmx" to "application/vnd.ms-officetheme", - "tif" to "image/tiff", - "tiff" to "image/tiff", - "tk" to "text/x-tcl", - "tlclient" to "application/vnd.cendio.thinlinc.clientconf", - "tm" to "text/texmacs", - "tm.json" to "application/tm+json", - "tm.jsonld" to "application/tm+json", - "tmo" to "application/vnd.tmobile-livetv", - "tnef" to "application/vnd.ms-tnef", - "tnf" to "application/vnd.ms-tnef", - "torrent" to "application/x-bittorrent", - "tpl" to "application/vnd.groove-tool-template", - "tpt" to "application/vnd.trid.tpt", - "tr" to "text/troff", - "tra" to "application/vnd.trueapp", - "tree" to "application/vnd.rainstor.data", - "trig" to "application/trig", - "ts" to "video/mp2ts", - "tsa" to "application/tamp-sequence-adjust", - "tsd" to "application/timestamped-data", - "tsp" to "application/dsptype", - "tsq" to "application/timestamp-query", - "tsr" to "application/timestamp-reply", - "tst" to "application/vnd.etsi.timestamp-token", - "tsv" to "text/tab-separated-values", - "ttc" to "font/collection", - "ttf" to "font/ttf", - "ttl" to "text/turtle", - "ttml" to "application/ttml+xml", - "tuc" to "application/tamp-update-confirm", - "tur" to "application/tamp-update", - "twd" to "application/vnd.SimTech-MindMapper", - "twds" to "application/vnd.SimTech-MindMapper", - "txd" to "application/vnd.genomatix.tuxedo", - "txf" to "application/vnd.Mobius.TXF", - "txt" to "text/plain", - "u3d" to "model/u3d", - "u8dsn" to "message/global-delivery-status", - "u8hdr" to "message/global-headers", - "u8mdn" to "message/global-disposition-notification", - "u8msg" to "message/global", - "udeb" to "application/vnd.debian.binary-package", - "ufd" to "application/vnd.ufdl", - "ufdl" to "application/vnd.ufdl", - "uis" to "application/urc-uisocketdesc+xml", - "umj" to "application/vnd.umajin", - "unityweb" to "application/vnd.unity", - "uo" to "application/vnd.uoml+xml", - "uoml" to "application/vnd.uoml+xml", - "upa" to "application/vnd.hbci", - "uri" to "text/uri-list", - "urim" to "application/vnd.uri-map", - "urimap" to "application/vnd.uri-map", - "uris" to "text/uri-list", - "usda" to "model/vnd.usda", - "usdz" to "model/vnd.usdz+zip", - "ustar" to "application/x-ustar", - "utz" to "application/vnd.uiq.theme", - "uva" to "audio/vnd.dece.audio", - "uvd" to "application/vnd.dece.data", - "uvf" to "application/vnd.dece.data", - "uvg" to "image/vnd.dece.graphic", - "uvh" to "video/vnd.dece.hd", - "uvi" to "image/vnd.dece.graphic", - "uvm" to "video/vnd.dece.mobile", - "uvp" to "video/vnd.dece.pd", - "uvs" to "video/vnd.dece.sd", - "uvt" to "application/vnd.dece.ttml+xml", - "uvu" to "video/vnd.dece.mp4", - "uvv" to "video/vnd.dece.video", - "uvva" to "audio/vnd.dece.audio", - "uvvd" to "application/vnd.dece.data", - "uvvf" to "application/vnd.dece.data", - "uvvg" to "image/vnd.dece.graphic", - "uvvh" to "video/vnd.dece.hd", - "uvvi" to "image/vnd.dece.graphic", - "uvvm" to "video/vnd.dece.mobile", - "uvvp" to "video/vnd.dece.pd", - "uvvs" to "video/vnd.dece.sd", - "uvvt" to "application/vnd.dece.ttml+xml", - "uvvu" to "video/vnd.dece.mp4", - "uvvv" to "video/vnd.dece.video", - "uvvx" to "application/vnd.dece.unspecified", - "uvvz" to "application/vnd.dece.zip", - "uvx" to "application/vnd.dece.unspecified", - "uvz" to "application/vnd.dece.zip", - "val" to "chemical/x-ncbi-asn1-binary", - "vbk" to "audio/vnd.nortel.vbk", - "vbox" to "application/vnd.previewsystems.box", - "vcard" to "text/vcard", - "vcd" to "application/x-cdlink", - "vcf" to "text/x-vcard", - "vcg" to "application/vnd.groove-vcard", - "vcj" to "application/voucher-cms+json", - "vcs" to "text/x-vcalendar", - "vcx" to "application/vnd.vcx", - "vds" to "model/vnd.sap.vds", - "vew" to "application/vnd.lotus-approach", - "vfr" to "application/vnd.tml", - "viaframe" to "application/vnd.tml", - "vis" to "application/vnd.visionary", - "viv" to "video/vnd.vivo", - "vmd" to "chemical/x-vmd", - "vms" to "chemical/x-vamas-iso14976", - "vmt" to "application/vnd.valve.source.material", - "vor" to "application/vnd.stardivision.writer", - "vpm" to "multipart/voice-message", - "vrm" to "model/vrml", - "vrml" to "model/vrml", - "vsc" to "application/vnd.vidsoft.vidconference", - "vsd" to "application/vnd.visio", - "vsf" to "application/vnd.vsf", - "vss" to "application/vnd.visio", - "vst" to "application/vnd.visio", - "vsw" to "application/vnd.visio", - "vtf" to "image/vnd.valve.source.texture", - "vtnstd" to "application/vnd.veritone.aion+json", - "vtt" to "text/vtt", - "vtu" to "model/vnd.vtu", - "vwx" to "application/vnd.vectorworks", - "vxml" to "application/voicexml+xml", - "wad" to "application/x-doom", - "wadl" to "application/vnd.sun.wadl+xml", - "wafl" to "application/vnd.wasmflow.wafl", - "wasm" to "application/wasm", - "wav" to "audio/x-wav", - "wax" to "audio/x-ms-wax", - "wbmp" to "image/vnd.wap.wbmp", - "wbs" to "application/vnd.criticaltools.wbs+xml", - "wbxml" to "application/vnd.wap.wbxml", - "wcm" to "application/vnd.ms-works", - "wdb" to "application/vnd.ms-works", - "webarchive" to "application/x-webarchive", - "webarchivexml" to "application/x-webarchive-xml", - "webm" to "video/webm", - "webmanifest" to "application/manifest+json", - "webp" to "image/webp", - "wg" to "application/vnd.pmi.widget", - "wgsl" to "text/wgsl", - "wgt" to "application/widget", - "wif" to "application/watcherinfo+xml", - "win" to "model/vnd.gdl", - "wk" to "application/x-123", - "wk1" to "application/vnd.lotus-1-2-3", - "wk3" to "application/vnd.lotus-1-2-3", - "wk4" to "application/vnd.lotus-1-2-3", - "wks" to "application/vnd.ms-works", - "wlnk" to "application/link-format", - "wm" to "video/x-ms-wm", - "wma" to "audio/x-ms-wma", - "wmc" to "application/vnd.wmc", - "wmd" to "application/x-ms-wmd", - "wmf" to "image/wmf", - "wml" to "text/vnd.wap.wml", - "wmlc" to "application/vnd.wap.wmlc", - "wmls" to "text/vnd.wap.wmlscript", - "wmlsc" to "application/vnd.wap.wmlscriptc", - "wmv" to "video/x-ms-wmv", - "wmx" to "video/x-ms-wmx", - "wmz" to "application/x-ms-wmz", - "woff" to "font/woff", - "woff2" to "font/woff2", - "wpd" to "application/vnd.wordperfect", - "wpl" to "application/vnd.ms-wpl", - "wps" to "application/vnd.ms-works", - "wqd" to "application/vnd.wqd", - "wrf" to "video/x-webex", - "wrl" to "model/vrml", - "wsc" to "application/vnd.wfa.wsc", - "wsdl" to "application/wsdl+xml", - "wspolicy" to "application/wspolicy+xml", - "wtb" to "application/vnd.webturbo", - "wv" to "application/vnd.wv.csp+wbxml", - "wvx" to "video/x-ms-wvx", - "wz" to "application/x-wingz", - "x3d" to "model/x3d+xml", - "x3db" to "model/x3d+fastinfoset", - "x3dv" to "model/x3d-vrml", - "x3dvz" to "model/x3d-vrml", - "x3dz" to "model/x3d+xml", - "x_b" to "model/vnd.parasolid.transmit.binary", - "x_t" to "model/vnd.parasolid.transmit.text", - "xar" to "application/vnd.xara", - "xav" to "application/xcap-att+xml", - "xbd" to "application/vnd.fujixerox.docuworks.binder", - "xbm" to "image/x-xbitmap", - "xca" to "application/xcap-caps+xml", - "xcf" to "image/x-xcf", - "xcos" to "application/x-scilab-xcos", - "xcs" to "application/calendar+xml", - "xct" to "application/vnd.fujixerox.docuworks.container", - "xdd" to "application/bacnet-xdd+zip", - "xdf" to "application/xcap-diff+xml", - "xdm" to "application/vnd.syncml.dm+xml", - "xdp" to "application/vnd.adobe.xdp+xml", - "xdssc" to "application/dssc+xml", - "xdw" to "application/vnd.fujixerox.docuworks", - "xel" to "application/xcap-el+xml", - "xer" to "application/xcap-error+xml", - "xfd" to "application/vnd.xfdl", - "xfdf" to "application/xfdf", - "xfdl" to "application/vnd.xfdl", - "xhe" to "audio/usac", - "xht" to "application/xhtml+xml", - "xhtm" to "application/xhtml+xml", - "xhtml" to "application/xhtml+xml", - "xhvml" to "application/xv+xml", - "xif" to "image/vnd.xiff", - "xla" to "application/vnd.ms-excel", - "xlam" to "application/vnd.ms-excel.addin.macroEnabled.12", - "xlc" to "application/vnd.ms-excel", - "xlf" to "application/xliff+xml", - "xlim" to "application/vnd.xmpie.xlim", - "xlm" to "application/vnd.ms-excel", - "xls" to "application/vnd.ms-excel", - "xlsb" to "application/vnd.ms-excel.sheet.binary.macroEnabled.12", - "xlsm" to "application/vnd.ms-excel.sheet.macroEnabled.12", - "xlsx" to "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - "xlt" to "application/vnd.ms-excel", - "xltm" to "application/vnd.ms-excel.template.macroEnabled.12", - "xltx" to "application/vnd.openxmlformats-officedocument.spreadsheetml.template", - "xlw" to "application/vnd.ms-excel", - "xmf" to "audio/midi", - "xml" to "text/xml", - "xmls" to "application/dskpp+xml", - "xmt_bin" to "model/vnd.parasolid.transmit.binary", - "xmt_txt" to "model/vnd.parasolid.transmit.text", - "xns" to "application/xcap-ns+xml", - "xo" to "application/vnd.olpc-sugar", - "xodp" to "application/vnd.collabio.xodocuments.presentation", - "xods" to "application/vnd.collabio.xodocuments.spreadsheet", - "xodt" to "application/vnd.collabio.xodocuments.document", - "xop" to "application/xop+xml", - "xotp" to "application/vnd.collabio.xodocuments.presentation-template", - "xots" to "application/vnd.collabio.xodocuments.spreadsheet-template", - "xott" to "application/vnd.collabio.xodocuments.document-template", - "xpak" to "application/vnd.gentoo.xpak", - "xpi" to "application/x-xpinstall", - "xpm" to "image/x-xpixmap", - "xpr" to "application/vnd.is-xpr", - "xps" to "application/vnd.ms-xpsdocument", - "xpw" to "application/vnd.intercon.formnet", - "xpx" to "application/vnd.intercon.formnet", - "xsf" to "application/prs.xsf+xml", - "xsl" to "application/xslt+xml", - "xslt" to "application/xslt+xml", - "xsm" to "application/vnd.syncml+xml", - "xspf" to "application/xspf+xml", - "xtel" to "chemical/x-xtel", - "xul" to "application/vnd.mozilla.xul+xml", - "xvm" to "application/xv+xml", - "xvml" to "application/xv+xml", - "xwd" to "image/x-xwindowdump", - "xyz" to "chemical/x-xyz", - "xyze" to "image/vnd.radiance", - "xz" to "application/x-xz", - "yaml" to "application/yaml", - "yang" to "application/yang", - "yin" to "application/yin+xml", - "yme" to "application/vnd.yaoweme", - "yml" to "application/yaml", - "yt" to "video/vnd.youtube.yt", - "zaz" to "application/vnd.zzazz.deck+xml", - "zfc" to "application/vnd.filmit.zfc", - "zfo" to "application/vnd.software602.filler.form-xml-zip", - "zip" to "application/zip", - "zir" to "application/vnd.zul", - "zirz" to "application/vnd.zul", - "zmm" to "application/vnd.HandHeld-Entertainment+xml", - "zmt" to "chemical/x-mopac-input", - "zone" to "text/dns", - "zst" to "application/zstd", - "~" to "application/x-trash" -) - -fun MimeTypeMap.getMimeTypeFromExtensionCompat(extension: String): String? { - return extensionToMimeTypeMap[extension] ?: getMimeTypeFromExtension(extension) -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/NioUtilsCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/NioUtilsCompat.kt deleted file mode 100644 index 9f4880269..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/NioUtilsCompat.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.system.OsConstants -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedMethod -import java.io.Closeable -import java.io.FileDescriptor -import java.nio.channels.FileChannel - -object NioUtilsCompat { - @RestrictedHiddenApi - private val newFileChannelMethod by lazyReflectedMethod( - "java.nio.NioUtils", "newFileChannel", Closeable::class.java, FileDescriptor::class.java, - Int::class.java - ) - @RestrictedHiddenApi - private val fileChannelImplOpenMethod by lazyReflectedMethod( - "sun.nio.ch.FileChannelImpl", "open", FileDescriptor::class.java, String::class.java, - Boolean::class.java, Boolean::class.java, Boolean::class.java, Any::class.java - ) - - fun newFileChannel(ioObject: Closeable, fd: FileDescriptor, flags: Int): FileChannel = - if (Build.VERSION.SDK_INT in Build.VERSION_CODES.N.. - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.pm.PackageInfo -import androidx.core.content.pm.PackageInfoCompat - -val PackageInfo.longVersionCodeCompat: Long - get() = PackageInfoCompat.getLongVersionCode(this) diff --git a/app/src/main/java/me/zhanghai/android/files/compat/PackageManagerCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/PackageManagerCompat.kt deleted file mode 100644 index 580cef21b..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/PackageManagerCompat.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.annotation.SuppressLint -import android.content.pm.PackageInfo -import android.content.pm.PackageManager -import android.content.pm.SigningInfo -import android.os.Build -import me.zhanghai.android.files.util.andInv -import me.zhanghai.android.files.util.hasBits - -object PackageManagerCompat { - @SuppressLint("InlinedApi") - const val MATCH_UNINSTALLED_PACKAGES = PackageManager.MATCH_UNINSTALLED_PACKAGES -} - -fun PackageManager.getPackageArchiveInfoCompat(archiveFilePath: String, flags: Int): PackageInfo? { - var packageInfo = getPackageArchiveInfo(archiveFilePath, flags) - // getPackageArchiveInfo() returns null for unsigned APKs if signing info is requested. - if (packageInfo == null) { - val flagsWithoutGetSigningInfo = flags.andInv( - @Suppress("DEPRECATION") - PackageManager.GET_SIGNATURES or if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - PackageManager.GET_SIGNING_CERTIFICATES - } else { - 0 - } - ) - if (flags != flagsWithoutGetSigningInfo) { - packageInfo = getPackageArchiveInfo(archiveFilePath, flagsWithoutGetSigningInfo) - ?.apply { - @Suppress("DEPRECATION") - if (flags.hasBits(PackageManager.GET_SIGNATURES)) { - signatures = emptyArray() - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P - && flags.hasBits(PackageManager.GET_SIGNING_CERTIFICATES)) { - signingInfo = SigningInfo() - } - } - } - } - return packageInfo -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ParcelCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ParcelCompat.kt deleted file mode 100644 index 4ca8cc0b6..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ParcelCompat.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.os.Parcel -import android.os.Parcelable -import androidx.core.os.ParcelCompat - -fun Parcel.readBooleanCompat(): Boolean = ParcelCompat.readBoolean(this) - -fun Parcel.writeBooleanCompat(value: Boolean) { - ParcelCompat.writeBoolean(this, value) -} - -fun > Parcel.readParcelableListCompat( - list: L, - classLoader: ClassLoader? -): L { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - @Suppress("UNCHECKED_CAST") - return readParcelableList(list, classLoader) as L - } else { - val size = readInt() - if (size == -1) { - list.clear() - return list - } - val listSize = list.size - for (index in 0..(classLoader) as E - if (index < listSize) { - list[index] = element - } else { - list += element - } - } - if (size < listSize) { - list.subList(size, listSize).clear() - } - return list - } -} - -fun Parcel.writeParcelableListCompat(value: List?, flags: Int) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - writeParcelableList(value, flags) - } else { - if (value == null) { - writeInt(-1) - return - } - writeInt(value.size) - for (element in value) { - writeParcelable(element, flags) - } - } -} - -@Suppress("UNCHECKED_CAST") -fun Parcel.readSerializableCompat(): T? = readSerializable() as T? diff --git a/app/src/main/java/me/zhanghai/android/files/compat/PermissionInfoCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/PermissionInfoCompat.kt deleted file mode 100644 index 239b86f51..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/PermissionInfoCompat.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2021 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.pm.PermissionInfo -import androidx.core.content.pm.PermissionInfoCompat - -val PermissionInfo.protectionCompat: Int - get() = PermissionInfoCompat.getProtection(this) diff --git a/app/src/main/java/me/zhanghai/android/files/compat/PreferenceManagerCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/PreferenceManagerCompat.kt deleted file mode 100644 index 55f3996b8..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/PreferenceManagerCompat.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.Context - -object PreferenceManagerCompat { - fun getDefaultSharedPreferencesName(context: Context): String = - "${context.packageName}_preferences" - - val defaultSharedPreferencesMode: Int - get() = Context.MODE_PRIVATE -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ProxyFileDescriptorCallbackCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ProxyFileDescriptorCallbackCompat.kt deleted file mode 100644 index 73c62a5e6..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ProxyFileDescriptorCallbackCompat.kt +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.os.ProxyFileDescriptorCallback -import android.system.ErrnoException -import android.system.OsConstants -import androidx.annotation.RequiresApi - -abstract class ProxyFileDescriptorCallbackCompat { - @Throws(ErrnoException::class) - open fun onGetSize(): Long { - throw ErrnoException("onGetSize", OsConstants.EBADF) - } - - @Throws(ErrnoException::class) - open fun onRead(offset: Long, size: Int, data: ByteArray): Int { - throw ErrnoException("onRead", OsConstants.EBADF) - } - - @Throws(ErrnoException::class) - open fun onWrite(offset: Long, size: Int, data: ByteArray): Int { - throw ErrnoException("onWrite", OsConstants.EBADF) - } - - @Throws(ErrnoException::class) - open fun onFsync() { - throw ErrnoException("onFsync", OsConstants.EINVAL) - } - - abstract fun onRelease() - - @RequiresApi(Build.VERSION_CODES.O) - fun toProxyFileDescriptorCallback(): ProxyFileDescriptorCallback { - return object : ProxyFileDescriptorCallback() { - @Throws(ErrnoException::class) - override fun onGetSize(): Long = this@ProxyFileDescriptorCallbackCompat.onGetSize() - - @Throws(ErrnoException::class) - override fun onRead(offset: Long, size: Int, data: ByteArray): Int = - this@ProxyFileDescriptorCallbackCompat.onRead(offset, size, data) - - @Throws(ErrnoException::class) - override fun onWrite(offset: Long, size: Int, data: ByteArray): Int = - this@ProxyFileDescriptorCallbackCompat.onWrite(offset, size, data) - - @Throws(ErrnoException::class) - override fun onFsync() = this@ProxyFileDescriptorCallbackCompat.onFsync() - - override fun onRelease() = this@ProxyFileDescriptorCallbackCompat.onRelease() - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ResourcesCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ResourcesCompat.kt deleted file mode 100644 index e534540cc..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ResourcesCompat.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.res.Resources -import androidx.annotation.DimenRes -import androidx.core.content.res.ResourcesCompat - -fun Resources.getFloatCompat(@DimenRes id: Int) = ResourcesCompat.getFloat(this, id) diff --git a/app/src/main/java/me/zhanghai/android/files/compat/RotateDrawableCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/RotateDrawableCompat.kt deleted file mode 100644 index 9423dc28e..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/RotateDrawableCompat.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2022 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.graphics.drawable.RotateDrawable -import android.os.Build -import me.zhanghai.android.files.util.lazyReflectedField -import kotlin.reflect.KClass - -fun KClass.createCompat(): RotateDrawable = - RotateDrawable().apply { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) { - isPivotXRelativeCompat = true - pivotXCompat = 0.5f - isPivotYRelativeCompat = true - pivotYCompat = 0.5f - } - } - -private val rotateDrawableMStateField by lazyReflectedField(RotateDrawable::class.java, "mState") -private val ROTATE_STATE_CLASS_NAME = "${RotateDrawable::class.java.name}\$RotateState" -private val rotateStateMPivotXRelField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, "mPivotXRel") - -var RotateDrawable.isPivotXRelativeCompat: Boolean - get() = isPivotXRelative - set(value) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - isPivotXRelative = value - } else { - if (isPivotXRelative != value) { - rotateStateMPivotXRelField.setBoolean(rotateDrawableMStateField.get(this), value) - invalidateSelf() - } - } - } - -private val rotateStateMPivotXField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, "mPivotX") - -var RotateDrawable.pivotXCompat: Float - get() = pivotX - set(value) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - pivotX = value - } else { - if (pivotX != value) { - rotateStateMPivotXField.setFloat(rotateDrawableMStateField.get(this), value) - invalidateSelf() - } - } - } - -private val rotateStateMPivotYRelField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, "mPivotYRel") - -var RotateDrawable.isPivotYRelativeCompat: Boolean - get() = isPivotYRelative - set(value) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - isPivotYRelative = value - } else { - if (isPivotYRelative != value) { - rotateStateMPivotYRelField.setBoolean(rotateDrawableMStateField.get(this), value) - invalidateSelf() - } - } - } - -private val rotateStateMPivotYField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, "mPivotY") - -var RotateDrawable.pivotYCompat: Float - get() = pivotY - set(value) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { - pivotY = value - } else { - if (pivotY != value) { - rotateStateMPivotYField.setFloat(rotateDrawableMStateField.get(this), value) - invalidateSelf() - } - } - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/SELinuxCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/SELinuxCompat.kt deleted file mode 100644 index f7e56ec8e..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/SELinuxCompat.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import androidx.annotation.RequiresApi -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedClass -import me.zhanghai.android.files.util.lazyReflectedMethod -import java.io.File -import java.io.FileDescriptor - -/* - * @see android.os.SELinux - * @see - * jb-mr1-release/SELinux.java - * @see - * hiddenapi-light-greylist.txt - */ -object SELinuxCompat { - private val seLinuxClass by lazyReflectedClass("android.os.SELinux") - private val isSELinuxEnabledMethod by lazyReflectedMethod(seLinuxClass, "isSELinuxEnabled") - private val isSELinuxEnforcedMethod by lazyReflectedMethod(seLinuxClass, "isSELinuxEnforced") - @RestrictedHiddenApi - private val setFSCreateContextMethod by lazyReflectedMethod( - seLinuxClass, "setFSCreateContext", String::class.java - ) - @RestrictedHiddenApi - private val setFileContextMethod by lazyReflectedMethod( - seLinuxClass, "setFileContext", String::class.java, String::class.java - ) - private val getFileContextStringMethod by lazyReflectedMethod( - seLinuxClass, "getFileContext", String::class.java - ) - @RestrictedHiddenApi - private val getPeerContextMethod by lazyReflectedMethod( - seLinuxClass, "getPeerContext", FileDescriptor::class.java - ) - @get:RequiresApi(Build.VERSION_CODES.Q) - @RestrictedHiddenApi - private val getFileContextFileDescriptorMethod by lazyReflectedMethod( - seLinuxClass, "getFileContext", FileDescriptor::class.java - ) - private val getContextMethod by lazyReflectedMethod(seLinuxClass, "getContext") - private val getPidContextMethod by lazyReflectedMethod( - seLinuxClass, "getPidContext", Int::class.java - ) - private val checkSELinuxAccessMethod by lazyReflectedMethod( - seLinuxClass, "checkSELinuxAccess", String::class.java, String::class.java, - String::class.java, String::class.java - ) - @RestrictedHiddenApi - private val nativeRestoreconMethod by lazyReflectedMethod( - seLinuxClass, "native_restorecon", String::class.java, Int::class.java - ) - @RestrictedHiddenApi - private val restoreconStringMethod by lazyReflectedMethod( - seLinuxClass, "restorecon", String::class.java - ) - @RestrictedHiddenApi - private val restoreconFileMethod by lazyReflectedMethod( - seLinuxClass, "restorecon", File::class.java - ) - private val restoreconRecursiveMethod by lazyReflectedMethod( - seLinuxClass, "restoreconRecursive", File::class.java - ) - - val isSELinuxEnabled: Boolean - get() = isSELinuxEnabledMethod.invoke(null) as Boolean - - val isSELinuxEnforced: Boolean - get() = isSELinuxEnforcedMethod.invoke(null) as Boolean - - fun setFSCreateContext(context: String?): Boolean = - setFSCreateContextMethod.invoke(null, context) as Boolean - - fun setFileContext(path: String, context: String): Boolean = - setFileContextMethod.invoke(null, path, context) as Boolean - - fun getFileContext(path: String): String? = - getFileContextStringMethod.invoke(null, path) as String? - - fun getPeerContext(fd: FileDescriptor): String? = - getPeerContextMethod.invoke(null, fd) as String? - - @RequiresApi(Build.VERSION_CODES.Q) - fun getFileContext(fd: FileDescriptor): String? = - getFileContextFileDescriptorMethod.invoke(null, fd) as String? - - val context: String? - get() = getContextMethod.invoke(null) as String? - - fun getPidContext(pid: Int): String? = getPidContextMethod.invoke(null, pid) as String? - - fun checkSELinuxAccess(scon: String, tcon: String, tclass: String, perm: String): Boolean = - checkSELinuxAccessMethod.invoke(null, scon, tcon, tclass, perm) as Boolean - - fun native_restorecon(pathname: String?, flags: Int): Boolean = - nativeRestoreconMethod.invoke(null, pathname, flags) as Boolean - - fun restorecon(pathname: String): Boolean = - restoreconStringMethod.invoke(null, pathname) as Boolean - - fun restorecon(file: File): Boolean = restoreconFileMethod.invoke(null, file) as Boolean - - fun restoreconRecursive(file: File): Boolean = - restoreconRecursiveMethod.invoke(null, file) as Boolean -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/SeekableByteChannelCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/SeekableByteChannelCompat.kt deleted file mode 100644 index c2303f0ae..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/SeekableByteChannelCompat.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import androidx.annotation.RequiresApi -import java8.nio.channels.SeekableByteChannel -import java.io.IOException -import java.nio.ByteBuffer -import java.nio.channels.SeekableByteChannel as JavaSeekableByteChannel - -@RequiresApi(Build.VERSION_CODES.N) -fun SeekableByteChannel.toJavaSeekableByteChannel(): JavaSeekableByteChannel = - // java8.nio.channels.FileChannel extends from java.nio.channels.FileChannel, so in that case - // the current object may already be implementing java.nio.channels.SeekableByteChannel. - this as? JavaSeekableByteChannel ?: DelegateJavaSeekableByteChannel(this) - -@RequiresApi(Build.VERSION_CODES.N) -private class DelegateJavaSeekableByteChannel( - private val channel: SeekableByteChannel -) : JavaSeekableByteChannel { - @Throws(IOException::class) - override fun read(dst: ByteBuffer): Int = channel.read(dst) - - @Throws(IOException::class) - override fun write(src: ByteBuffer): Int = channel.write(src) - - @Throws(IOException::class) - override fun position(): Long = channel.position() - - @Throws(IOException::class) - override fun position(newPosition: Long): DelegateJavaSeekableByteChannel { - channel.position(newPosition) - return this - } - - @Throws(IOException::class) - override fun size(): Long = channel.size() - - @Throws(IOException::class) - override fun truncate(size: Long): DelegateJavaSeekableByteChannel { - channel.truncate(size) - return this - } - - override fun isOpen(): Boolean = channel.isOpen - - @Throws(IOException::class) - override fun close() { - channel.close() - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ServiceCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ServiceCompat.kt deleted file mode 100644 index 7a98fa0d0..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ServiceCompat.kt +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.app.Service -import androidx.core.app.ServiceCompat - -fun Service.stopForegroundCompat(flags: Int) { - ServiceCompat.stopForeground(this, flags) -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/StorageManagerCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/StorageManagerCompat.kt deleted file mode 100644 index eb5815972..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/StorageManagerCompat.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.os.Handler -import android.os.ParcelFileDescriptor -import android.os.storage.StorageManager -import android.os.storage.StorageVolume -import kotlinx.coroutines.runBlocking -import me.zhanghai.android.files.util.lazyReflectedMethod -import java.io.IOException -import java.util.concurrent.atomic.AtomicInteger -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine - -private val getVolumeListMethod by lazyReflectedMethod(StorageManager::class.java, "getVolumeList") - -val StorageManager.storageVolumesCompat: List - get() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - storageVolumes - } else { - @Suppress("UNCHECKED_CAST") - (getVolumeListMethod.invoke(this) as Array).toList() - } - -// Thanks to fython for https://gist.github.com/fython/924f8d9019bca75d22de116bb69a54a1 -@Throws(IOException::class) -fun StorageManager.openProxyFileDescriptorCompat( - mode: Int, - callback: ProxyFileDescriptorCallbackCompat, - handler: Handler -): ParcelFileDescriptor = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - openProxyFileDescriptor(mode, callback.toProxyFileDescriptorCallback(), handler) - } else { - // TODO: Support other modes? - if (mode != ParcelFileDescriptor.MODE_READ_ONLY) { - throw UnsupportedOperationException("mode $mode") - } - val pfds = ParcelFileDescriptor.createReliablePipe() - PipeWriter(pfds[1], callback, handler).start() - pfds[0] - } - -private class PipeWriter( - private val pfd: ParcelFileDescriptor, - private val callback: ProxyFileDescriptorCallbackCompat, - private val handler: Handler -) : Thread("StorageManagerCompat.PipeWriter-${id.getAndIncrement()}") { - override fun run() { - try { - ParcelFileDescriptor.AutoCloseOutputStream(pfd).use { outputStream -> - var offset = 0L - val buffer = ByteArray(4 * 1024) - while (true) { - val size = runBlocking { - callback.awaitOnRead(offset, buffer.size, buffer, handler) - } - if (size == 0) { - break - } - offset += size.toLong() - outputStream.write(buffer, 0, size) - } - runBlocking { callback.awaitOnRelease(handler) } - } - } catch (e: Exception) { - e.printStackTrace() - try { - pfd.closeWithError(e.message) - } catch (e2: IOException) { - e2.printStackTrace() - } - } - } - - companion object { - private val id = AtomicInteger() - } -} - -private suspend fun ProxyFileDescriptorCallbackCompat.awaitOnRead( - offset: Long, - size: Int, - data: ByteArray, - handler: Handler -): Int = - suspendCoroutine { continuation -> - handler.post { - val readSize = try { - onRead(offset, size, data) - } catch (t: Throwable) { - continuation.resumeWithException(t) - return@post - } - continuation.resume(readSize) - } - } - -private suspend fun ProxyFileDescriptorCallbackCompat.awaitOnRelease(handler: Handler) { - suspendCoroutine { continuation -> - handler.post { - try { - onRelease() - } catch (t: Throwable) { - continuation.resumeWithException(t) - return@post - } - continuation.resume(Unit) - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/StorageVolumeCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/StorageVolumeCompat.kt deleted file mode 100644 index e1b67b5e2..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/StorageVolumeCompat.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent -import android.os.Build -import android.os.Environment -import android.os.storage.StorageVolume -import android.provider.DocumentsContract -import me.zhanghai.android.files.util.lazyReflectedMethod -import java.io.File - -// Work around @SuppressLint not applicable to top level property with delegate. -@SuppressLint("NewApi") -private val storageVolumeClass = StorageVolume::class.java - -private val getPathMethod by lazyReflectedMethod(storageVolumeClass, "getPath") - -val StorageVolume.pathCompat: String - get() = getPathMethod.invoke(this) as String - -private val getPathFileMethod by lazyReflectedMethod(storageVolumeClass, "getPathFile") - -val StorageVolume.pathFileCompat: File - get() = File(pathCompat) - -val StorageVolume.directoryCompat: File? - get() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - directory - } else { - when (stateCompat) { - Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY -> pathFileCompat - else -> null - } - } - -@SuppressLint("NewApi") -fun StorageVolume.getDescriptionCompat(context: Context): String = getDescription(context) - -val StorageVolume.isPrimaryCompat: Boolean - @SuppressLint("NewApi") - get() = isPrimary - -val StorageVolume.isRemovableCompat: Boolean - @SuppressLint("NewApi") - get() = isRemovable - -val StorageVolume.isEmulatedCompat: Boolean - @SuppressLint("NewApi") - get() = isEmulated - -val StorageVolume.uuidCompat: String? - @SuppressLint("NewApi") - get() = uuid - -val StorageVolume.stateCompat: String - @SuppressLint("NewApi") - get() = state - -fun StorageVolume.createOpenDocumentTreeIntentCompat(): Intent = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - createOpenDocumentTreeIntent() - } else { - Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply { - val rootId = if (isEmulatedCompat) { - DocumentsContractCompat.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID - } else { - uuidCompat - } - val rootUri = DocumentsContract.buildRootUri( - DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId - ) - putExtra(DocumentsContractCompat.EXTRA_INITIAL_URI, rootUri) - putExtra(DocumentsContractCompat.EXTRA_SHOW_ADVANCED, true) - } - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/TextViewCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/TextViewCompat.kt deleted file mode 100644 index 1785d5e58..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/TextViewCompat.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.os.Build -import android.widget.TextView -import androidx.annotation.StyleRes -import androidx.core.widget.TextViewCompat -import me.zhanghai.android.files.util.lazyReflectedMethod - -private val isSingleLineMethod by lazyReflectedMethod(TextView::class.java, "isSingleLine") - -val TextView.isSingleLineCompat: Boolean - get() = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - isSingleLine - } else { - isSingleLineMethod.invoke(this) as Boolean - } - -fun TextView.setTextAppearanceCompat(@StyleRes resId: Int) { - TextViewCompat.setTextAppearance(this, resId) -} diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ThreadLocalCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ThreadLocalCompat.kt deleted file mode 100644 index 7a812218b..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ThreadLocalCompat.kt +++ /dev/null @@ -1,13 +0,0 @@ -package me.zhanghai.android.files.compat - -import android.os.Build -import kotlin.reflect.KClass - -fun KClass>.withInitial(supplier: () -> T): ThreadLocal = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ThreadLocal.withInitial(supplier) - } else { - object : ThreadLocal() { - override fun initialValue(): T = supplier.invoke() - } - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt deleted file mode 100644 index cd1d3dd81..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.util.TypedValue -import androidx.core.util.TypedValueCompat - -val TypedValue.complexUnitCompat: Int - get() = TypedValueCompat.getUnitFromComplexDimension(data) diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ViewCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ViewCompat.kt deleted file mode 100644 index 2afa754d0..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ViewCompat.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.content.res.ColorStateList -import android.graphics.PorterDuff -import android.graphics.drawable.Drawable -import android.view.View -import androidx.annotation.IdRes -import androidx.core.view.ViewCompat -import me.zhanghai.android.foregroundcompat.ForegroundCompat - -@Suppress("UNCHECKED_CAST") -fun View.requireViewByIdCompat(@IdRes id: Int): T = - ViewCompat.requireViewById(this, id) as T - -var View.scrollIndicatorsCompat: Int - get() = ViewCompat.getScrollIndicators(this) - set(value) { - ViewCompat.setScrollIndicators(this, value) - } - -fun View.setScrollIndicatorsCompat(indicators: Int, mask: Int) { - ViewCompat.setScrollIndicators(this, indicators, mask) -} - -var View.foregroundCompat: Drawable? - get() = ForegroundCompat.getForeground(this) - set(value) { - ForegroundCompat.setForeground(this, value) - } - -var View.foregroundGravityCompat: Int - get() = ForegroundCompat.getForegroundGravity(this) - set(value) { - ForegroundCompat.setForegroundGravity(this, value) - } - -var View.foregroundTintListCompat: ColorStateList? - get() = ForegroundCompat.getForegroundTintList(this) - set(value) { - ForegroundCompat.setForegroundTintList(this, value) - } - -var View.foregroundTintModeCompat: PorterDuff.Mode? - get() = ForegroundCompat.getForegroundTintMode(this) - set(value) { - ForegroundCompat.setForegroundTintMode(this, value) - } diff --git a/app/src/main/java/me/zhanghai/android/files/compat/ViewGroupCompat.kt b/app/src/main/java/me/zhanghai/android/files/compat/ViewGroupCompat.kt deleted file mode 100644 index b705b05c1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/compat/ViewGroupCompat.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.compat - -import android.graphics.PointF -import android.view.View -import android.view.ViewGroup -import me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi -import me.zhanghai.android.files.util.lazyReflectedMethod - -@RestrictedHiddenApi -private val isTransformedTouchPointInViewMethod by lazyReflectedMethod( - ViewGroup::class.java, "isTransformedTouchPointInView", Float::class.java, Float::class.java, - View::class.java, PointF::class.java -) - -fun ViewGroup.isTransformedTouchPointInViewCompat( - x: Float, - y: Float, - child: View, - outLocalPoint: PointF? -): Boolean = - isTransformedTouchPointInViewMethod.invoke(this, x, y, child, outLocalPoint) as Boolean diff --git a/app/src/main/java/me/zhanghai/android/files/file/BasicFileAttributesExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/BasicFileAttributesExtensions.kt deleted file mode 100644 index 2a31246b1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/BasicFileAttributesExtensions.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import java8.nio.file.attribute.BasicFileAttributes -import org.threeten.bp.Instant - -val BasicFileAttributes.fileSize: FileSize - get() = size().asFileSize() - -val BasicFileAttributes.lastModifiedInstant: Instant - get() = lastModifiedTime().toInstant() diff --git a/app/src/main/java/me/zhanghai/android/files/file/DocumentTreeUri.kt b/app/src/main/java/me/zhanghai/android/files/file/DocumentTreeUri.kt deleted file mode 100644 index 9937b9a81..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/DocumentTreeUri.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.content.Intent -import android.net.Uri -import android.os.Parcelable -import android.os.storage.StorageVolume -import android.provider.DocumentsContract -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.app.contentResolver -import me.zhanghai.android.files.compat.DocumentsContractCompat -import me.zhanghai.android.files.compat.createOpenDocumentTreeIntentCompat -import me.zhanghai.android.files.storage.StorageVolumeListLiveData -import me.zhanghai.android.files.util.StableUriParceler -import me.zhanghai.android.files.util.getParcelableExtraSafe -import me.zhanghai.android.files.util.releasePersistablePermission -import me.zhanghai.android.files.util.takePersistablePermission -import me.zhanghai.android.files.util.valueCompat - -@Parcelize -@JvmInline -value class DocumentTreeUri(val value: @WriteWith Uri) : Parcelable { - val documentId: String - get() = DocumentsContract.getTreeDocumentId(value) - - companion object { - val persistedUris: List - get() = - contentResolver.persistedUriPermissions - .filter { it.uri.isDocumentTreeUri } - .sortedBy { it.persistedTime } - .map { it.uri.asDocumentTreeUri() } - } -} - -fun Uri.asDocumentTreeUriOrNull(): DocumentTreeUri? = - if (isDocumentTreeUri) DocumentTreeUri(this) else null - -fun Uri.asDocumentTreeUri(): DocumentTreeUri { - check(isDocumentTreeUri) - return DocumentTreeUri(this) -} - -private val Uri.isDocumentTreeUri: Boolean - get() = DocumentsContractCompat.isTreeUri(this) - -fun DocumentTreeUri.buildDocumentUri(documentId: String): DocumentUri = - DocumentsContract.buildDocumentUriUsingTree(value, documentId).asDocumentUri() - -val DocumentTreeUri.displayName: String? - get() = buildDocumentUri(documentId).displayName - -fun DocumentTreeUri.takePersistablePermission(): Boolean = - value.takePersistablePermission( - Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - ) || value.takePersistablePermission(Intent.FLAG_GRANT_READ_URI_PERMISSION) - -fun DocumentTreeUri.releasePersistablePermission(): Boolean = - value.releasePersistablePermission( - Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION - ) - -val StorageVolume.documentTreeUri: DocumentTreeUri - get() { - val intent = createOpenDocumentTreeIntentCompat() - val rootUri = intent.getParcelableExtraSafe( - DocumentsContractCompat.EXTRA_INITIAL_URI - )!! - // @see com.android.externalstorage.ExternalStorageProvider#getDocIdForFile(File) - // @see com.android.documentsui.picker.ConfirmFragment#onCreateDialog(Bundle) - return DocumentsContract.buildTreeDocumentUri( - rootUri.authority, "${DocumentsContract.getRootId(rootUri)}:" - ).asDocumentTreeUri() - } - -val DocumentTreeUri.storageVolume: StorageVolume? - get() = StorageVolumeListLiveData.valueCompat.find { it.documentTreeUri == this } diff --git a/app/src/main/java/me/zhanghai/android/files/file/DocumentUri.kt b/app/src/main/java/me/zhanghai/android/files/file/DocumentUri.kt deleted file mode 100644 index fac458337..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/DocumentUri.kt +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.net.Uri -import android.os.Parcelable -import android.provider.DocumentsContract -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.app.contentResolver -import me.zhanghai.android.files.compat.DocumentsContractCompat -import me.zhanghai.android.files.util.StableUriParceler - -@Parcelize -@JvmInline -value class DocumentUri(val value: @WriteWith Uri) : Parcelable { - val treeDocumentId: String - get() = DocumentsContract.getTreeDocumentId(value) - - val documentId: String - get() = DocumentsContract.getDocumentId(value) -} - -fun Uri.asDocumentUriOrNull(): DocumentUri? = - if (isDocumentUri) DocumentUri(this) else null - -fun Uri.asDocumentUri(): DocumentUri { - require(isDocumentUri) - return DocumentUri(this) -} - -private val Uri.isDocumentUri: Boolean - get() = DocumentsContractCompat.isDocumentUri(this) - -val DocumentUri.displayName: String? - get() { - try { - contentResolver.query( - value, arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME), null, null, null - ).use { cursor -> - if (cursor != null && cursor.moveToFirst()) { - val displayNameIndex = cursor.getColumnIndex( - DocumentsContract.Document.COLUMN_DISPLAY_NAME - ) - if (displayNameIndex != -1) { - val displayName = cursor.getString(displayNameIndex) - if (!displayName.isNullOrEmpty()) { - return displayName - } - } - } - } - } catch (e: Exception) { - e.printStackTrace() - } - return null - } diff --git a/app/src/main/java/me/zhanghai/android/files/file/DurationExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/DurationExtensions.kt deleted file mode 100644 index b185c65d5..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/DurationExtensions.kt +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.text.format.DateUtils -import org.threeten.bp.Duration - -fun Duration.format(): String = DateUtils.formatElapsedTime(seconds) diff --git a/app/src/main/java/me/zhanghai/android/files/file/ExternalStorageUri.kt b/app/src/main/java/me/zhanghai/android/files/file/ExternalStorageUri.kt deleted file mode 100644 index 2448615c8..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/ExternalStorageUri.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2024 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.net.Uri -import android.os.Parcelable -import android.provider.DocumentsContract -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.compat.DocumentsContractCompat -import me.zhanghai.android.files.util.StableUriParceler -import me.zhanghai.android.files.util.takeIfNotEmpty - -@Parcelize -@JvmInline -value class ExternalStorageUri(val value: @WriteWith Uri) : Parcelable { - constructor( - rootId: String, - path: String - ) : this( - DocumentsContract.buildDocumentUriUsingTree( - DocumentsContract.buildTreeDocumentUri( - DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, - rootId - ), - "$rootId:$path" - ) - ) - - val rootId: String - get() = DocumentsContract.getTreeDocumentId(value) - - val path: String - get() = DocumentsContract.getDocumentId(value).removePrefix("$rootId:") -} - -fun Uri.asExternalStorageUriOrNull(): ExternalStorageUri? = - if (isExternalStorageUri) ExternalStorageUri(this) else null - -fun Uri.asExternalStorageUri(): ExternalStorageUri { - require(isExternalStorageUri) - return ExternalStorageUri(this) -} - -/** @see DocumentsContractCompat.isDocumentUri */ -private val Uri.isExternalStorageUri: Boolean - get() = - DocumentsContractCompat.isDocumentUri(this) && - authority == DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY && - pathSegments.size == 4 - -val ExternalStorageUri.displayName: String - get() = path.takeLastWhile { it != '/' }.takeIfNotEmpty() ?: "/" diff --git a/app/src/main/java/me/zhanghai/android/files/file/FileItem.kt b/app/src/main/java/me/zhanghai/android/files/file/FileItem.kt deleted file mode 100644 index d575fc949..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/FileItem.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.os.Parcelable -import androidx.annotation.WorkerThread -import java8.nio.file.LinkOption -import java8.nio.file.Path -import java8.nio.file.attribute.BasicFileAttributes -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.filelist.getCollationKeyForFileName -import me.zhanghai.android.files.filelist.name -import me.zhanghai.android.files.provider.common.AndroidFileTypeDetector -import me.zhanghai.android.files.provider.common.isHidden -import me.zhanghai.android.files.provider.common.readAttributes -import me.zhanghai.android.files.provider.common.readSymbolicLinkByteString -import me.zhanghai.android.files.util.ParcelableParceler -import java.io.IOException -import java.text.CollationKey -import java.text.Collator - -@Parcelize -data class FileItem( - val path: @WriteWith Path, - val nameCollationKey: @WriteWith CollationKey, - val attributesNoFollowLinks: @WriteWith BasicFileAttributes, - val symbolicLinkTarget: String?, - private val symbolicLinkTargetAttributes: @WriteWith BasicFileAttributes?, - val isHidden: Boolean, - val mimeType: MimeType -) : Parcelable { - val attributes: BasicFileAttributes - get() = symbolicLinkTargetAttributes ?: attributesNoFollowLinks - - val isSymbolicLinkBroken: Boolean - get() { - check(attributesNoFollowLinks.isSymbolicLink) { "Not a symbolic link" } - return symbolicLinkTargetAttributes == null - } -} - -@WorkerThread -@Throws(IOException::class) -fun Path.loadFileItem(): FileItem { - val nameCollationKey = Collator.getInstance().getCollationKeyForFileName(name) - val attributes = readAttributes(BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS) - val isHidden = isHidden - if (!attributes.isSymbolicLink) { - val mimeType = AndroidFileTypeDetector.getMimeType(this, attributes).asMimeType() - return FileItem(this, nameCollationKey, attributes, null, null, isHidden, mimeType) - } - val symbolicLinkTarget = readSymbolicLinkByteString().toString() - val symbolicLinkTargetAttributes = try { - readAttributes(BasicFileAttributes::class.java) - } catch (e: IOException) { - e.printStackTrace() - null - } - val mimeType = AndroidFileTypeDetector.getMimeType( - this, symbolicLinkTargetAttributes ?: attributes - ).asMimeType() - return FileItem( - this, nameCollationKey, attributes, symbolicLinkTarget, symbolicLinkTargetAttributes, - isHidden, mimeType - ) -} diff --git a/app/src/main/java/me/zhanghai/android/files/file/FileProvider.kt b/app/src/main/java/me/zhanghai/android/files/file/FileProvider.kt deleted file mode 100644 index e65e145d4..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/FileProvider.kt +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.content.ContentProvider -import android.content.ContentResolver -import android.content.ContentValues -import android.content.Context -import android.content.pm.ProviderInfo -import android.database.Cursor -import android.database.MatrixCursor -import android.net.Uri -import android.os.Binder -import android.os.Build -import android.os.Handler -import android.os.HandlerThread -import android.os.ParcelFileDescriptor -import android.os.Process -import android.os.StrictMode -import android.provider.DocumentsContract -import android.provider.MediaStore -import android.provider.OpenableColumns -import android.system.ErrnoException -import android.system.OsConstants -import java8.nio.channels.SeekableByteChannel -import java8.nio.file.AccessDeniedException -import java8.nio.file.FileSystemException -import java8.nio.file.FileSystemLoopException -import java8.nio.file.NoSuchFileException -import java8.nio.file.OpenOption -import java8.nio.file.Path -import java8.nio.file.Paths -import java8.nio.file.StandardOpenOption -import me.zhanghai.android.files.BuildConfig -import me.zhanghai.android.files.app.storageManager -import me.zhanghai.android.files.compat.ProxyFileDescriptorCallbackCompat -import me.zhanghai.android.files.compat.openProxyFileDescriptorCompat -import me.zhanghai.android.files.provider.common.InvalidFileNameException -import me.zhanghai.android.files.provider.common.IsDirectoryException -import me.zhanghai.android.files.provider.common.force -import me.zhanghai.android.files.provider.common.getLastModifiedTime -import me.zhanghai.android.files.provider.common.isForceable -import me.zhanghai.android.files.provider.common.newByteChannel -import me.zhanghai.android.files.provider.common.size -import me.zhanghai.android.files.provider.document.documentUri -import me.zhanghai.android.files.provider.document.isDocumentPath -import me.zhanghai.android.files.provider.linux.isLinuxPath -import me.zhanghai.android.files.provider.linux.syscall.SyscallException -import me.zhanghai.android.files.util.hasBits -import me.zhanghai.android.files.util.withoutPenaltyDeathOnNetwork -import java.io.FileNotFoundException -import java.io.IOException -import java.io.InterruptedIOException -import java.net.URI -import java.nio.ByteBuffer -import java.nio.channels.ClosedByInterruptException - -class FileProvider : ContentProvider() { - private lateinit var callbackThread: HandlerThread - private lateinit var callbackHandler: Handler - - override fun onCreate(): Boolean { - callbackThread = HandlerThread("FileProvider.CallbackThread") - callbackThread.start() - callbackHandler = Handler(callbackThread.looper) - return true - } - - override fun shutdown() { - callbackThread.quitSafely() - } - - override fun attachInfo(context: Context, info: ProviderInfo) { - super.attachInfo(context, info) - - if (info.exported) { - throw SecurityException("Provider must not be exported") - } - if (!info.grantUriPermissions) { - throw SecurityException("Provider must grant uri permissions") - } - } - - override fun query( - uri: Uri, - projection: Array?, - selection: String?, - selectionArgs: Array?, - sortOrder: String? - ): Cursor? { - // ContentProvider has already checked granted permissions - val projectionColumns = projection ?: getDefaultProjection() - val path = uri.fileProviderPath - val columns = mutableListOf() - val values = mutableListOf() - loop@ for (column in projectionColumns) { - @Suppress("DEPRECATION") - when (column) { - OpenableColumns.DISPLAY_NAME -> { - columns += column - values += path.fileName.toString() - } - OpenableColumns.SIZE -> { - val size = try { - path.size() - } catch (e: IOException) { - e.printStackTrace() - null - } - columns += column - values += size - } - MediaStore.MediaColumns.DATA -> { - val file = try { - path.toFile() - } catch (e: UnsupportedOperationException) { - continue@loop - } - columns += column - values += file.absolutePath - } - // TODO: We should actually implement a DocumentsProvider since we are handling - // ACTION_OPEN_DOCUMENT. - DocumentsContract.Document.COLUMN_MIME_TYPE -> { - columns += column - values += MimeType.guessFromPath(path.toString()).value - } - DocumentsContract.Document.COLUMN_LAST_MODIFIED -> { - val lastModified = try { - path.getLastModifiedTime().toMillis() - } catch (e: IOException) { - e.printStackTrace() - null - } - columns += column - values += lastModified - } - } - } - return MatrixCursor(columns.toTypedArray(), 1).apply { - addRow(values) - } - } - - private fun getDefaultProjection(): Array = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - && Binder.getCallingUid() == Process.SYSTEM_UID) { - // com.android.internal.app.ChooserActivity.queryResolver() in Q queries with a null - // projection (meaning all columns) on main thread but only actually needs the display - // name (and document flags). However if we do return all the columns, we may perform - // network requests and crash it due to StrictMode. So just work around by only - // returning the display name in this case. - CHOOSER_ACTIVITY_DEFAULT_PROJECTION - } else { - DEFAULT_PROJECTION - } - - override fun getType(uri: Uri): String? { - val path = uri.fileProviderPath - return MimeType.guessFromPath(path.toString()).value - } - - override fun insert(uri: Uri, values: ContentValues?): Uri? { - throw UnsupportedOperationException("No external inserts") - } - - override fun update( - uri: Uri, - values: ContentValues?, - selection: String?, - selectionArgs: Array? - ): Int { - throw UnsupportedOperationException("No external updates") - } - - override fun delete( - uri: Uri, - selection: String?, - selectionArgs: Array? - ): Int { - throw UnsupportedOperationException("No external deletes") - } - - @Throws(FileNotFoundException::class) - override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { - // ContentProvider has already checked granted permissions - val path = uri.fileProviderPath - val modeBits = ParcelFileDescriptor.parseMode(mode) - if (path.canOpenDirectly(modeBits)) { - return ParcelFileDescriptor.open(path.toFile(), modeBits) - } - val options = modeBits.toOpenOptions() - val channel = try { - // Strict mode thread policy is passed through binder, but some apps (notably music - // players) like to open file on their main thread. - StrictMode::class.withoutPenaltyDeathOnNetwork { - path.newByteChannel(options) - } - } catch (e: IOException) { - throw e.toFileNotFoundException() - } - return try { - storageManager.openProxyFileDescriptorCompat( - modeBits, ChannelCallback(channel), callbackHandler - ) - } catch (e: IOException) { - throw e.toFileNotFoundException() - } - } - - private fun Path.canOpenDirectly(mode: Int): Boolean { - if (!isLinuxPath) { - return false - } - val file = toFile() - val readOnly = mode.hasBits(ParcelFileDescriptor.MODE_READ_ONLY) - val writeOnly = mode.hasBits(ParcelFileDescriptor.MODE_WRITE_ONLY) - val readWrite = mode.hasBits(ParcelFileDescriptor.MODE_READ_WRITE) - val needRead = readOnly || readWrite - val needWrite = writeOnly || readWrite - return !((needRead && !file.canRead()) || (needWrite && !file.canWrite())) - } - - private fun Int.toOpenOptions(): Set = - mutableSetOf().apply { - // May be "r" for read-only access, "rw" for read and write access, or "rwt" for - // read and write access that truncates any existing file. - require(!hasBits(ParcelFileDescriptor.MODE_APPEND)) { "mode ${this@toOpenOptions}" } - if (hasBits(ParcelFileDescriptor.MODE_READ_ONLY) - || hasBits(ParcelFileDescriptor.MODE_READ_WRITE)) { - this += StandardOpenOption.READ - } - if (hasBits(ParcelFileDescriptor.MODE_WRITE_ONLY) - || hasBits(ParcelFileDescriptor.MODE_READ_WRITE)) { - this += StandardOpenOption.WRITE - } - if (hasBits(ParcelFileDescriptor.MODE_CREATE)) { - this += StandardOpenOption.CREATE - } - if (hasBits(ParcelFileDescriptor.MODE_TRUNCATE)) { - this += StandardOpenOption.TRUNCATE_EXISTING - } - } - - private fun IOException.toFileNotFoundException(): FileNotFoundException = - if (this is FileNotFoundException) { - this - } else { - FileNotFoundException(message).apply { initCause(this@toFileNotFoundException) } - } - - private class ChannelCallback( - private val channel: SeekableByteChannel - ) : ProxyFileDescriptorCallbackCompat() { - private var offset = 0L - private var released = false - - @Throws(ErrnoException::class) - override fun onGetSize(): Long { - ensureNotReleased() - return try { - channel.size() - } catch (e: IOException) { - throw e.toErrnoException() - } - } - - @Throws(ErrnoException::class) - override fun onRead(offset: Long, size: Int, data: ByteArray): Int { - ensureNotReleased() - if (this.offset != offset) { - try { - channel.position(offset) - } catch (e: IOException) { - throw e.toErrnoException() - } - this.offset = offset - } - val buffer = ByteBuffer.wrap(data, 0, size) - // Unlike ReadableByteChannel which may not fill the buffer and returns -1 upon - // end-of-stream, we need to read as much as we can unless end-of-stream is reached. - while (buffer.hasRemaining()) { - val channelSize = try { - channel.read(buffer) - } catch (e: IOException) { - throw e.toErrnoException() - } - if (channelSize == -1) { - break - } - this.offset += channelSize - } - return (this.offset - offset).toInt() - } - - @Throws(ErrnoException::class) - override fun onWrite(offset: Long, size: Int, data: ByteArray): Int { - ensureNotReleased() - if (this.offset != offset) { - try { - channel.position(offset) - } catch (e: IOException) { - throw e.toErrnoException() - } - this.offset = offset - } - val buffer = ByteBuffer.wrap(data, 0, size) - return try { - channel.write(buffer) - } catch (e: IOException) { - throw e.toErrnoException() - }.also { this.offset += it.toLong() } - } - - @Throws(ErrnoException::class) - override fun onFsync() { - ensureNotReleased() - if (channel.isForceable) { - try { - channel.force(true) - } catch (e: IOException) { - throw e.toErrnoException() - } - } - } - - @Throws(ErrnoException::class) - private fun ensureNotReleased() { - if (released) { - throw ErrnoException(null, OsConstants.EBADF) - } - } - - override fun onRelease() { - if (released) { - return - } - try { - channel.close() - } catch (e: IOException) { - e.printStackTrace() - } - released = true - } - - private fun IOException.toErrnoException(): ErrnoException { - val cause = cause - return if (this is FileSystemException && cause is SyscallException) { - ErrnoException(cause.functionName, cause.errno, this) - } else { - val errno = when (this) { - is AccessDeniedException -> OsConstants.EPERM - is FileSystemLoopException -> OsConstants.ELOOP - is InvalidFileNameException -> OsConstants.EINVAL - is IsDirectoryException -> OsConstants.EISDIR - is NoSuchFileException -> OsConstants.ENOENT - is ClosedByInterruptException, is InterruptedIOException -> OsConstants.EINTR - else -> OsConstants.EIO - } - ErrnoException(message, errno, this) - } - } - } - - companion object { - private val DEFAULT_PROJECTION = arrayOf( - OpenableColumns.DISPLAY_NAME, - OpenableColumns.SIZE, - MediaStore.MediaColumns.DATA, - DocumentsContract.Document.COLUMN_MIME_TYPE, - DocumentsContract.Document.COLUMN_LAST_MODIFIED - ) - - private val CHOOSER_ACTIVITY_DEFAULT_PROJECTION = arrayOf( - OpenableColumns.DISPLAY_NAME - ) - } -} - -val Path.fileProviderUri: Uri - get() { - // Try avoid going through FUSE two times, which is bad for media playback. - if (isDocumentPath) { - try { - return documentUri - } catch (e: IOException) { - e.printStackTrace() - } - } - val uriPath = Uri.encode(toUri().toString()) - return Uri.Builder() - .scheme(ContentResolver.SCHEME_CONTENT) - .authority(BuildConfig.FILE_PROVIDIER_AUTHORITY) - .path(uriPath) - .build() - } - -private val Uri.fileProviderPath: Path - get() { - // Strip the prepended slash. A slash is always prepended because our Uri path starts with - // our URI scheme, which can never start with a slash; but our Uri has an authority so its - // path must start with a slash. - val uriPath = Uri.decode(path).substring(1) - return Paths.get(URI.create(uriPath)) - } diff --git a/app/src/main/java/me/zhanghai/android/files/file/FileSize.kt b/app/src/main/java/me/zhanghai/android/files/file/FileSize.kt deleted file mode 100644 index fa529e61f..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/FileSize.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.content.Context -import android.text.format.Formatter -import me.zhanghai.android.files.R -import me.zhanghai.android.files.util.getQuantityString - -@JvmInline -value class FileSize(val value: Long) { - - /* @see android.text.format.Formatter#formatBytes(Resources, long, int) */ - val isHumanReadableInBytes: Boolean - get() = value <= 900 - - fun formatInBytes(context: Context): String = - context.getQuantityString(R.plurals.size_in_bytes_format, value.toInt(), value) - - fun formatHumanReadable(context: Context): String = - Formatter.formatFileSize(context, value) -} - -fun Long.asFileSize(): FileSize = FileSize(this) diff --git a/app/src/main/java/me/zhanghai/android/files/file/InstantExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/InstantExtensions.kt deleted file mode 100644 index 11efa2fbd..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/InstantExtensions.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.content.Context -import android.text.format.DateUtils -import android.text.format.Time -import org.threeten.bp.Instant -import org.threeten.bp.ZoneId -import org.threeten.bp.format.DateTimeFormatter -import org.threeten.bp.format.FormatStyle - -/* @see com.android.documentsui.base.Shared#formatTime(Context, long) */ -@Suppress("DEPRECATION") -fun Instant.formatShort(context: Context): String { - val time = toEpochMilli() - val then = Time().apply { set(time) } - val now = Time().apply { setToNow() } - val flags = DateUtils.FORMAT_NO_NOON or DateUtils.FORMAT_NO_MIDNIGHT or - DateUtils.FORMAT_ABBREV_ALL or when { - then.year != now.year -> DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE - then.yearDay != now.yearDay -> DateUtils.FORMAT_SHOW_DATE - else -> DateUtils.FORMAT_SHOW_TIME - } - return DateUtils.formatDateTime(context, time, flags) -} - -fun Instant.formatLong(): String = - DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM) - .withZone(ZoneId.systemDefault()) - .format(this) diff --git a/app/src/main/java/me/zhanghai/android/files/file/JavaFile.kt b/app/src/main/java/me/zhanghai/android/files/file/JavaFile.kt deleted file mode 100644 index 960e33218..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/JavaFile.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import java.io.File - -object JavaFile { - fun isDirectory(path: String): Boolean = File(path).isDirectory - - fun getFreeSpace(path: String): Long = File(path).freeSpace - - fun getTotalSpace(path: String): Long = File(path).totalSpace -} diff --git a/app/src/main/java/me/zhanghai/android/files/file/MimeType.kt b/app/src/main/java/me/zhanghai/android/files/file/MimeType.kt deleted file mode 100644 index ade3411c9..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/MimeType.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.os.Parcelable -import android.provider.DocumentsContract -import kotlinx.parcelize.Parcelize - -@Parcelize -@JvmInline -value class MimeType(val value: String) : Parcelable { - val type: String - get() = value.substring(0, value.indexOf('/')) - - val subtype: String - get() { - val indexOfSlash = value.indexOf('/') - val indexOfSemicolon = value.indexOf(';') - return value.substring( - indexOfSlash + 1, if (indexOfSemicolon != -1) indexOfSemicolon else value.length - ) - } - - val suffix: String? - get() { - val indexOfPlus = value.indexOf('+') - if (indexOfPlus == -1) { - return null - } - val indexOfSemicolon = value.indexOf(';') - if (indexOfSemicolon != -1 && indexOfPlus > indexOfSemicolon) { - return null - } - return value.substring( - indexOfPlus + 1, if (indexOfSemicolon != -1) indexOfSemicolon else value.length - ) - } - - val parameters: String? - get() { - val indexOfSemicolon = value.indexOf(';') - return if (indexOfSemicolon != -1) value.substring(indexOfSemicolon + 1) else null - } - - fun match(mimeType: MimeType): Boolean = - type.let { it == "*" || mimeType.type == it } - && subtype.let { it == "*" || mimeType.subtype == it } - && parameters.let { it == null || mimeType.parameters == it } - - companion object { - val ANY = "*/*".asMimeType() - val APK = "application/vnd.android.package-archive".asMimeType() - val DIRECTORY = DocumentsContract.Document.MIME_TYPE_DIR.asMimeType() - val IMAGE_ANY = "image/*".asMimeType() - val IMAGE_GIF = "image/gif".asMimeType() - val IMAGE_SVG_XML = "image/svg+xml".asMimeType() - val PDF = "application/pdf".asMimeType() - val TEXT_PLAIN = "text/plain".asMimeType() - val GENERIC = "application/octet-stream".asMimeType() - - fun of(type: String, subtype: String, parameters: String?): MimeType = - "$type/$subtype${if (parameters != null) ";$parameters" else ""}".asMimeType() - } -} - -fun String.asMimeTypeOrNull(): MimeType? = if (isValidMimeType) MimeType(this) else null - -fun String.asMimeType(): MimeType { - require(isValidMimeType) - return MimeType(this) -} - -private val String.isValidMimeType: Boolean - get() { - val indexOfSlash = indexOf('/') - if (indexOfSlash == -1 || indexOfSlash !in 1.. indexOfSemicolon)) { - if (indexOfPlus !in indexOfSlash + 2..< - if (indexOfSemicolon != -1) indexOfSemicolon - 1 else length) { - return false - } - } - return true - } diff --git a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeConversionExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/MimeTypeConversionExtensions.kt deleted file mode 100644 index aeea2ce6d..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeConversionExtensions.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.webkit.MimeTypeMap -import me.zhanghai.android.files.compat.getMimeTypeFromExtensionCompat -import me.zhanghai.android.files.provider.common.PosixFileType -import me.zhanghai.android.files.util.asFileName -import me.zhanghai.android.files.util.asPathName - -fun MimeType.Companion.guessFromPath(path: String): MimeType { - val fileName = path.asPathName().fileName ?: return DIRECTORY - return guessFromExtension(fileName.asFileName().singleExtension) -} - -fun MimeType.Companion.guessFromExtension(extension: String): MimeType { - val extension = extension.lowercase() - return extensionToMimeTypeOverrideMap[extension] - ?: MimeTypeMap.getSingleton().getMimeTypeFromExtensionCompat(extension)?.asMimeTypeOrNull() - ?: GENERIC -} - -// @see https://android.googlesource.com/platform/external/mime-support/+/master/mime.types -// @see https://android.googlesource.com/platform/frameworks/base/+/master/mime/java-res/android.mime.types -// @see http://www.iana.org/assignments/media-types/media-types.xhtml -// @see https://salsa.debian.org/debian/media-types/-/blob/master/mime.types -// @see /usr/share/mime/packages/freedesktop.org.xml -private val extensionToMimeTypeOverrideMap = mapOf( - // Fixes - "csv" to "text/csv", // Was "text/comma-separated-values" - "sh" to "application/x-sh", // Was "text/x-sh" - // Addition - "bz" to "application/x-bzip", - "bz2" to "application/x-bzip2", - "z" to "application/x-compress", - "lzma" to "application/x-lzma", - "p7b" to "application/x-pkcs7-certificates", - "spc" to "application/x-pkcs7-certificates", // Clashes with "chemical/x-galactic-spc" - "ts" to "application/typescript", // Clashes with "video/mp2ts" - "py3" to "text/x-python", - "py3x" to "text/x-python", - "pyx" to "text/x-python", - "wsgi" to "text/x-python", - "yml" to "application/yaml", - "asm" to "text/x-asm", - "s" to "text/x-asm", - "cs" to "text/x-csharp", - "azw" to "application/vnd.amazon.ebook", - "ibooks" to "application/x-ibooks+zip", - "msg" to "application/vnd.ms-outlook", - "mkd" to "text/markdown", - "conf" to "text/plain", - "ini" to "text/plain", - "list" to "text/plain", - "log" to "text/plain", - "prop" to "text/plain", - "properties" to "text/plain", - "rc" to "text/plain" -).mapValues { it.value.asMimeType() } - -fun MimeType.Companion.forSpecialPosixFileType(type: PosixFileType): MimeType? = - specialPosixFileTypeToMimeTypeMap[type] - -// See also https://developer.gnome.org/shared-mime-info-spec/ -/** @see specialPosixFileTypeToNameResMap */ -private val specialPosixFileTypeToMimeTypeMap = mapOf( - PosixFileType.CHARACTER_DEVICE to "inode/chardevice", - PosixFileType.BLOCK_DEVICE to "inode/blockdevice", - PosixFileType.FIFO to "inode/fifo", - PosixFileType.SYMBOLIC_LINK to "inode/symlink", - PosixFileType.SOCKET to "inode/socket" -).mapValues { it.value.asMimeType() } - -val MimeType.extension: String? - // TODO: Add compat implementation as well. - get() = MimeTypeMap.getSingleton().getExtensionFromMimeType(value) - -val MimeType.intentType: String - get() = intentMimeType.value - -private val MimeType.intentMimeType: MimeType - get() = mimeTypeToIntentMimeTypeMap[this] ?: this - -private val mimeTypeToIntentMimeTypeMap = listOf( - // Allows matching "text/*" - "application/ecmascript" to "text/ecmascript", - "application/javascript" to "text/javascript", - "application/json" to "text/json", - "application/typescript" to "text/typescript", - "application/yaml" to "text/x-yaml", - "application/x-sh" to "text/x-shellscript", - "application/x-shellscript" to "text/x-shellscript", - // Allows matching generic - MimeType.GENERIC.value to MimeType.ANY.value -).associate { it.first.asMimeType() to it.second.asMimeType() } - -val Collection.intentType: String - get() { - if (isEmpty()) { - return MimeType.ANY.value - } - val intentMimeTypes = map { it.intentMimeType } - val firstIntentMimeType = intentMimeTypes.first() - if (intentMimeTypes.all { firstIntentMimeType.match(it) }) { - return firstIntentMimeType.value - } - val wildcardIntentMimeType = MimeType.of(firstIntentMimeType.type, "*", null) - if (intentMimeTypes.all { wildcardIntentMimeType.match(it) }) { - return wildcardIntentMimeType.value - } - return MimeType.ANY.value - } diff --git a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeIcon.kt b/app/src/main/java/me/zhanghai/android/files/file/MimeTypeIcon.kt deleted file mode 100644 index 6999cf0a1..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeIcon.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import me.zhanghai.android.files.R - -enum class MimeTypeIcon(val resourceId: Int) { - APK(R.drawable.file_apk_icon), - ARCHIVE(R.drawable.file_archive_icon), - AUDIO(R.drawable.file_audio_icon), - CALENDAR(R.drawable.file_calendar_icon), - CERTIFICATE(R.drawable.file_certificate_icon), - CODE(R.drawable.file_code_icon), - CONTACT(R.drawable.file_contact_icon), - DIRECTORY(R.drawable.file_directory_icon), - DOCUMENT(R.drawable.file_document_icon), - EBOOK(R.drawable.file_ebook_icon), - EMAIL(R.drawable.file_email_icon), - FONT(R.drawable.file_font_icon), - GENERIC(R.drawable.file_generic_icon), - IMAGE(R.drawable.file_image_icon), - PDF(R.drawable.file_pdf_icon), - PRESENTATION(R.drawable.file_presentation_icon), - SPREADSHEET(R.drawable.file_spreadsheet_icon), - TEXT(R.drawable.file_text_icon), - VIDEO(R.drawable.file_video_icon), - WORD(R.drawable.file_word_icon), - EXCEL(R.drawable.file_excel_icon), - POWERPOINT(R.drawable.file_powerpoint_icon) -} - -// See also https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/com/android/internal/util/MimeIconUtils.java -// See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types -// See also http://www.iana.org/assignments/media-types/media-types.xhtml -// See also /usr/share/mime/packages/freedesktop.org.xml -val MimeType.icon: MimeTypeIcon - get() = - mimeTypeToIconMap[this] ?: typeToIconMap[type] ?: suffix?.let { suffixToIconMap[it] } - ?: MimeTypeIcon.GENERIC - -// See also https://mimesniff.spec.whatwg.org/#mime-type-groups -private val mimeTypeToIconMap = mapOf( - "application/vnd.android.package-archive" to MimeTypeIcon.APK, - "application/gzip" to MimeTypeIcon.ARCHIVE, - // Not in IANA list, but Mozilla and Wikipedia say so. - "application/java-archive" to MimeTypeIcon.ARCHIVE, - "application/mac-binhex40" to MimeTypeIcon.ARCHIVE, - // Not in IANA list, but AOSP MimeUtils used to say so. - "application/rar" to MimeTypeIcon.ARCHIVE, - "application/zip" to MimeTypeIcon.ARCHIVE, - "application/zstd" to MimeTypeIcon.ARCHIVE, - "application/vnd.debian.binary-package" to MimeTypeIcon.ARCHIVE, - "application/vnd.ms-cab-compressed" to MimeTypeIcon.ARCHIVE, - "application/vnd.rar" to MimeTypeIcon.ARCHIVE, - "application/x-7z-compressed" to MimeTypeIcon.ARCHIVE, - "application/x-apple-diskimage" to MimeTypeIcon.ARCHIVE, - "application/x-bzip" to MimeTypeIcon.ARCHIVE, - "application/x-bzip2" to MimeTypeIcon.ARCHIVE, - "application/x-compress" to MimeTypeIcon.ARCHIVE, - "application/x-cpio" to MimeTypeIcon.ARCHIVE, - "application/x-deb" to MimeTypeIcon.ARCHIVE, - "application/x-debian-package" to MimeTypeIcon.ARCHIVE, - "application/x-gtar" to MimeTypeIcon.ARCHIVE, - "application/x-gtar-compressed" to MimeTypeIcon.ARCHIVE, - "application/x-gzip" to MimeTypeIcon.ARCHIVE, - "application/x-iso9660-image" to MimeTypeIcon.ARCHIVE, - "application/x-java-archive" to MimeTypeIcon.ARCHIVE, - "application/x-lha" to MimeTypeIcon.ARCHIVE, - "application/x-lzh" to MimeTypeIcon.ARCHIVE, - "application/x-lzma" to MimeTypeIcon.ARCHIVE, - "application/x-lzx" to MimeTypeIcon.ARCHIVE, - "application/x-rar-compressed" to MimeTypeIcon.ARCHIVE, - "application/x-stuffit" to MimeTypeIcon.ARCHIVE, - "application/x-tar" to MimeTypeIcon.ARCHIVE, - "application/x-webarchive" to MimeTypeIcon.ARCHIVE, - "application/x-webarchive-xml" to MimeTypeIcon.ARCHIVE, - "application/x-xz" to MimeTypeIcon.ARCHIVE, - "application/ogg" to MimeTypeIcon.AUDIO, - "application/x-flac" to MimeTypeIcon.AUDIO, - "text/calendar" to MimeTypeIcon.CALENDAR, - "text/x-vcalendar" to MimeTypeIcon.CALENDAR, - "application/pgp-keys" to MimeTypeIcon.CERTIFICATE, - "application/pgp-signature" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs12" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs7-certificates" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs7-certreqresp" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs7-crl" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs7-mime" to MimeTypeIcon.CERTIFICATE, - "application/x-pkcs7-signature" to MimeTypeIcon.CERTIFICATE, - "application/x-x509-ca-cert" to MimeTypeIcon.CERTIFICATE, - "application/x-x509-server-cert" to MimeTypeIcon.CERTIFICATE, - "application/x-x509-user-cert" to MimeTypeIcon.CERTIFICATE, - "application/ecmascript" to MimeTypeIcon.CODE, - "application/javascript" to MimeTypeIcon.CODE, - "application/json" to MimeTypeIcon.CODE, - "application/typescript" to MimeTypeIcon.CODE, - "application/xml" to MimeTypeIcon.CODE, - "application/yaml" to MimeTypeIcon.CODE, - "application/x-csh" to MimeTypeIcon.CODE, - "application/x-ecmascript" to MimeTypeIcon.CODE, - "application/x-javascript" to MimeTypeIcon.CODE, - "application/x-latex" to MimeTypeIcon.CODE, - "application/x-perl" to MimeTypeIcon.CODE, - "application/x-python" to MimeTypeIcon.CODE, - "application/x-ruby" to MimeTypeIcon.CODE, - "application/x-sh" to MimeTypeIcon.CODE, - "application/x-shellscript" to MimeTypeIcon.CODE, - "application/x-texinfo" to MimeTypeIcon.CODE, - "application/x-yaml" to MimeTypeIcon.CODE, - "text/css" to MimeTypeIcon.CODE, - "text/html" to MimeTypeIcon.CODE, - "text/ecmascript" to MimeTypeIcon.CODE, - "text/javascript" to MimeTypeIcon.CODE, - "text/jscript" to MimeTypeIcon.CODE, - "text/livescript" to MimeTypeIcon.CODE, - "text/xml" to MimeTypeIcon.CODE, - "text/x-asm" to MimeTypeIcon.CODE, - "text/x-c++hdr" to MimeTypeIcon.CODE, - "text/x-c++src" to MimeTypeIcon.CODE, - "text/x-chdr" to MimeTypeIcon.CODE, - "text/x-csh" to MimeTypeIcon.CODE, - "text/x-csharp" to MimeTypeIcon.CODE, - "text/x-csrc" to MimeTypeIcon.CODE, - "text/x-dsrc" to MimeTypeIcon.CODE, - "text/x-ecmascript" to MimeTypeIcon.CODE, - "text/x-haskell" to MimeTypeIcon.CODE, - "text/x-java" to MimeTypeIcon.CODE, - "text/x-javascript" to MimeTypeIcon.CODE, - "text/x-literate-haskell" to MimeTypeIcon.CODE, - "text/x-pascal" to MimeTypeIcon.CODE, - "text/x-perl" to MimeTypeIcon.CODE, - "text/x-python" to MimeTypeIcon.CODE, - "text/x-ruby" to MimeTypeIcon.CODE, - "text/x-shellscript" to MimeTypeIcon.CODE, - "text/x-tcl" to MimeTypeIcon.CODE, - "text/x-tex" to MimeTypeIcon.CODE, - "text/x-yaml" to MimeTypeIcon.CODE, - "text/vcard" to MimeTypeIcon.CONTACT, - "text/x-vcard" to MimeTypeIcon.CONTACT, - "inode/directory" to MimeTypeIcon.DIRECTORY, - MimeType.DIRECTORY.value to MimeTypeIcon.DIRECTORY, - "application/rtf" to MimeTypeIcon.DOCUMENT, - "application/vnd.kde.kword" to MimeTypeIcon.DOCUMENT, - "application/vnd.oasis.opendocument.text" to MimeTypeIcon.DOCUMENT, - "application/vnd.oasis.opendocument.text-master" to MimeTypeIcon.DOCUMENT, - "application/vnd.oasis.opendocument.text-template" to MimeTypeIcon.DOCUMENT, - "application/vnd.oasis.opendocument.text-web" to MimeTypeIcon.DOCUMENT, - "application/vnd.stardivision.writer" to MimeTypeIcon.DOCUMENT, - "application/vnd.stardivision.writer-global" to MimeTypeIcon.DOCUMENT, - "application/vnd.sun.xml.writer" to MimeTypeIcon.DOCUMENT, - "application/vnd.sun.xml.writer.global" to MimeTypeIcon.DOCUMENT, - "application/vnd.sun.xml.writer.template" to MimeTypeIcon.DOCUMENT, - "application/x-abiword" to MimeTypeIcon.DOCUMENT, - "application/x-kword" to MimeTypeIcon.DOCUMENT, - "text/rtf" to MimeTypeIcon.DOCUMENT, - "application/epub+zip" to MimeTypeIcon.EBOOK, - "application/vnd.amazon.ebook" to MimeTypeIcon.EBOOK, - "application/vnd.amazon.mobi8-ebook" to MimeTypeIcon.EBOOK, - "application/x-cbr" to MimeTypeIcon.EBOOK, - "application/x-cbz" to MimeTypeIcon.EBOOK, - "application/x-ibooks+zip" to MimeTypeIcon.EBOOK, - "application/x-mobipocket-ebook" to MimeTypeIcon.EBOOK, - "application/vnd.ms-outlook" to MimeTypeIcon.EMAIL, - "message/rfc822" to MimeTypeIcon.EMAIL, - "application/font-cff" to MimeTypeIcon.FONT, - "application/font-off" to MimeTypeIcon.FONT, - "application/font-sfnt" to MimeTypeIcon.FONT, - "application/font-ttf" to MimeTypeIcon.FONT, - "application/font-woff" to MimeTypeIcon.FONT, - "application/vnd.ms-fontobject" to MimeTypeIcon.FONT, - "application/vnd.ms-opentype" to MimeTypeIcon.FONT, - "application/x-font" to MimeTypeIcon.FONT, - "application/x-font-ttf" to MimeTypeIcon.FONT, - "application/x-font-woff" to MimeTypeIcon.FONT, - "application/vnd.oasis.opendocument.graphics" to MimeTypeIcon.IMAGE, - "application/vnd.oasis.opendocument.graphics-template" to MimeTypeIcon.IMAGE, - "application/vnd.oasis.opendocument.image" to MimeTypeIcon.IMAGE, - "application/vnd.stardivision.draw" to MimeTypeIcon.IMAGE, - "application/vnd.sun.xml.draw" to MimeTypeIcon.IMAGE, - "application/vnd.sun.xml.draw.template" to MimeTypeIcon.IMAGE, - "application/vnd.visio" to MimeTypeIcon.IMAGE, - "application/pdf" to MimeTypeIcon.PDF, - "application/vnd.kde.kpresenter" to MimeTypeIcon.PRESENTATION, - "application/vnd.oasis.opendocument.presentation" to MimeTypeIcon.PRESENTATION, - "application/vnd.oasis.opendocument.presentation-template" to MimeTypeIcon.PRESENTATION, - "application/vnd.stardivision.impress" to MimeTypeIcon.PRESENTATION, - "application/vnd.sun.xml.impress" to MimeTypeIcon.PRESENTATION, - "application/vnd.sun.xml.impress.template" to MimeTypeIcon.PRESENTATION, - "application/x-kpresenter" to MimeTypeIcon.PRESENTATION, - "application/vnd.kde.kspread" to MimeTypeIcon.SPREADSHEET, - "application/vnd.oasis.opendocument.spreadsheet" to MimeTypeIcon.SPREADSHEET, - "application/vnd.oasis.opendocument.spreadsheet-template" to MimeTypeIcon.SPREADSHEET, - "application/vnd.stardivision.calc" to MimeTypeIcon.SPREADSHEET, - "application/vnd.sun.xml.calc" to MimeTypeIcon.SPREADSHEET, - "application/vnd.sun.xml.calc.template" to MimeTypeIcon.SPREADSHEET, - "application/x-kspread" to MimeTypeIcon.SPREADSHEET, - "application/vnd.adobe.flash.movie" to MimeTypeIcon.VIDEO, - "application/x-quicktimeplayer" to MimeTypeIcon.VIDEO, - "application/x-shockwave-flash" to MimeTypeIcon.VIDEO, - "application/msword" to MimeTypeIcon.WORD, - "application/vnd.openxmlformats-officedocument.wordprocessingml.document" to MimeTypeIcon.WORD, - "application/vnd.openxmlformats-officedocument.wordprocessingml.template" to MimeTypeIcon.WORD, - "application/vnd.ms-excel" to MimeTypeIcon.EXCEL, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" to MimeTypeIcon.EXCEL, - "application/vnd.openxmlformats-officedocument.spreadsheetml.template" to MimeTypeIcon.EXCEL, - "application/vnd.ms-powerpoint" to MimeTypeIcon.POWERPOINT, - "application/vnd.openxmlformats-officedocument.presentationml.presentation" - to MimeTypeIcon.POWERPOINT, - "application/vnd.openxmlformats-officedocument.presentationml.slideshow" - to MimeTypeIcon.POWERPOINT, - "application/vnd.openxmlformats-officedocument.presentationml.template" - to MimeTypeIcon.POWERPOINT -).mapKeys { it.key.asMimeType() } - -private val typeToIconMap = mapOf( - "audio" to MimeTypeIcon.AUDIO, - "font" to MimeTypeIcon.FONT, - "image" to MimeTypeIcon.IMAGE, - "text" to MimeTypeIcon.TEXT, - "video" to MimeTypeIcon.VIDEO -) - -private val suffixToIconMap = mapOf( - "json" to MimeTypeIcon.CODE, - "xml" to MimeTypeIcon.CODE, - "zip" to MimeTypeIcon.ARCHIVE -) - -val MimeType.iconRes: Int - get() = icon.resourceId diff --git a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeNameExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/MimeTypeNameExtensions.kt deleted file mode 100644 index a5a0c5965..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeNameExtensions.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -import android.content.Context -import me.zhanghai.android.files.R - -fun MimeType.getName(extension: String, context: Context): String { - val nameRes = specialPosixFileTypeToNameResMap[this] - ?: icon.getNameRes(this, extension.isNotEmpty()) - return context.getString(nameRes, extension.uppercase()) -} - -/** - * @see specialPosixFileTypeToMimeTypeMap - */ -private val specialPosixFileTypeToNameResMap = mapOf( - "inode/chardevice" to R.string.file_type_name_posix_character_device, - "inode/blockdevice" to R.string.file_type_name_posix_block_device, - "inode/fifo" to R.string.file_type_name_posix_fifo, - "inode/symlink" to R.string.file_type_name_posix_symbolic_link, - "inode/socket" to R.string.file_type_name_posix_socket -).mapKeys { it.key.asMimeType() } - -private fun MimeTypeIcon.getNameRes(mimeType: MimeType, hasExtension: Boolean): Int = - when (this) { - MimeTypeIcon.APK -> R.string.file_type_name_apk - MimeTypeIcon.ARCHIVE -> R.string.file_type_name_archive - MimeTypeIcon.AUDIO -> R.string.file_type_name_audio - MimeTypeIcon.CALENDAR -> R.string.file_type_name_calendar - MimeTypeIcon.CERTIFICATE -> R.string.file_type_name_certificate - MimeTypeIcon.CODE -> R.string.file_type_name_code - MimeTypeIcon.CONTACT -> R.string.file_type_name_contact - MimeTypeIcon.DIRECTORY -> R.string.file_type_name_directory - MimeTypeIcon.DOCUMENT -> R.string.file_type_name_document - MimeTypeIcon.EBOOK -> R.string.file_type_name_ebook - MimeTypeIcon.EMAIL -> R.string.file_type_name_email - MimeTypeIcon.FONT -> R.string.file_type_name_font - MimeTypeIcon.GENERIC -> { - if (!hasExtension) R.string.file_type_name_unknown else R.string.file_type_name_generic - } - MimeTypeIcon.IMAGE -> R.string.file_type_name_image - MimeTypeIcon.PDF -> R.string.file_type_name_pdf - MimeTypeIcon.PRESENTATION -> R.string.file_type_name_presentation - MimeTypeIcon.SPREADSHEET -> R.string.file_type_name_spreadsheet - MimeTypeIcon.TEXT -> - if (mimeType == MimeType.TEXT_PLAIN) { - R.string.file_type_name_text_plain - } else { - R.string.file_type_name_text - } - MimeTypeIcon.VIDEO -> R.string.file_type_name_video - MimeTypeIcon.WORD -> R.string.file_type_name_word - MimeTypeIcon.EXCEL -> R.string.file_type_name_excel - MimeTypeIcon.POWERPOINT -> R.string.file_type_name_powerpoint - } - -fun MimeType.Companion.getBrokenSymbolicLinkName(context: Context): String = - context.getString(R.string.file_type_name_posix_symbolic_link_broken) diff --git a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeTypeExtensions.kt b/app/src/main/java/me/zhanghai/android/files/file/MimeTypeTypeExtensions.kt deleted file mode 100644 index 90e95f247..000000000 --- a/app/src/main/java/me/zhanghai/android/files/file/MimeTypeTypeExtensions.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.file - -val MimeType.isApk: Boolean - get() = this == MimeType.APK - -val MimeType.isSupportedArchive: Boolean - get() = this in supportedArchiveMimeTypes - -private val supportedArchiveMimeTypes = mutableListOf( - "application/gzip", - "application/java-archive", - "application/rar", - "application/zip", - "application/zstd", - "application/vnd.android.package-archive", - "application/vnd.debian.binary-package", - "application/vnd.ms-cab-compressed", - "application/vnd.rar", - "application/x-7z-compressed", - "application/x-bzip2", - "application/x-cab", - "application/x-compress", - "application/x-cpio", - "application/x-deb", - "application/x-debian-package", - "application/x-gtar", - "application/x-gtar-compressed", - "application/x-iso9660-image", - "application/x-java-archive", - "application/x-lha", - "application/x-lzma", - "application/x-redhat-package-manager", - "application/x-tar", - "application/x-ustar", - "application/x-xz" -).map { it.asMimeType() }.toSet() - -val MimeType.isImage: Boolean - get() = icon == MimeTypeIcon.IMAGE - -val MimeType.isAudio: Boolean - get() = icon == MimeTypeIcon.AUDIO - -val MimeType.isVideo: Boolean - get() = icon == MimeTypeIcon.VIDEO - -val MimeType.isMedia: Boolean - get() = isAudio || isVideo - -val MimeType.isPdf: Boolean - get() = this == MimeType.PDF diff --git a/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogActivity.kt b/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogActivity.kt deleted file mode 100644 index 9cd8aa1a7..000000000 --- a/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogActivity.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.fileaction - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.commit -import me.zhanghai.android.files.app.AppActivity -import me.zhanghai.android.files.util.args -import me.zhanghai.android.files.util.putArgs - -class ArchivePasswordDialogActivity : AppActivity() { - private val args by args() - - private lateinit var fragment: ArchivePasswordDialogFragment - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // Calls ensureSubDecor(). - findViewById(android.R.id.content) - if (savedInstanceState == null) { - fragment = ArchivePasswordDialogFragment().putArgs(args) - supportFragmentManager.commit { - add(fragment, ArchivePasswordDialogFragment::class.java.name) - } - } else { - fragment = supportFragmentManager.findFragmentByTag( - ArchivePasswordDialogFragment::class.java.name - ) as ArchivePasswordDialogFragment - } - } - - override fun onDestroy() { - super.onDestroy() - - if (isFinishing) { - fragment.onFinish() - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogFragment.kt b/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogFragment.kt deleted file mode 100644 index 7acfda892..000000000 --- a/app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogFragment.kt +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2023 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.fileaction - -import android.app.Dialog -import android.content.Context -import android.content.DialogInterface -import android.os.Bundle -import android.os.Parcel -import android.os.Parcelable -import android.util.SparseArray -import android.view.WindowManager -import android.widget.LinearLayout -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatDialogFragment -import androidx.core.widget.NestedScrollView -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import java8.nio.file.Path -import kotlinx.parcelize.Parceler -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.R -import me.zhanghai.android.files.compat.requireViewByIdCompat -import me.zhanghai.android.files.databinding.ArchivePasswordDialogBinding -import me.zhanghai.android.files.provider.archive.archiveAddPassword -import me.zhanghai.android.files.provider.archive.archiveFile -import me.zhanghai.android.files.ui.AllowSoftInputHackAlertDialogCustomView -import me.zhanghai.android.files.util.ParcelableArgs -import me.zhanghai.android.files.util.ParcelableParceler -import me.zhanghai.android.files.util.ParcelableState -import me.zhanghai.android.files.util.RemoteCallback -import me.zhanghai.android.files.util.args -import me.zhanghai.android.files.util.finish -import me.zhanghai.android.files.util.getArgs -import me.zhanghai.android.files.util.getState -import me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange -import me.zhanghai.android.files.util.layoutInflater -import me.zhanghai.android.files.util.putArgs -import me.zhanghai.android.files.util.putState -import me.zhanghai.android.files.util.readParcelable -import me.zhanghai.android.files.util.setOnEditorConfirmActionListener - -class ArchivePasswordDialogFragment : AppCompatDialogFragment() { - private val args by args() - - private lateinit var binding: ArchivePasswordDialogBinding - - private var isListenerNotified = false - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - - val hierarchyState = SparseArray() - .apply { binding.root.saveHierarchyState(this) } - outState.putState(State(hierarchyState)) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val context = requireContext() - return MaterialAlertDialogBuilder(requireContext(), theme) - .setTitle(getTitle(context)) - .setMessage(getMessage(args.path.archiveFile.fileName, context)) - .apply { - binding = ArchivePasswordDialogBinding.inflate(context.layoutInflater) - binding.passwordEdit.hideTextInputLayoutErrorOnTextChange(binding.passwordLayout) - binding.passwordEdit.setOnEditorConfirmActionListener { onOk() } - if (savedInstanceState != null) { - val state = savedInstanceState.getState() - binding.root.restoreHierarchyState(state.hierarchyState) - } - setView(AllowSoftInputHackAlertDialogCustomView(context)) - } - .setPositiveButton(android.R.string.ok, null) - .setNegativeButton(android.R.string.cancel) { _, _ -> finish() } - .create() - .apply { - setCanceledOnTouchOutside(false) - // Override the listener here so that we have control over when to close the dialog. - setOnShowListener { - getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { onOk() } - } - window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) - } - } - - override fun onStart() { - super.onStart() - - val dialog = requireDialog() as AlertDialog - if (binding.root.parent == null) { - val scrollView = dialog.requireViewByIdCompat(R.id.scrollView) - val linearLayout = scrollView.getChildAt(0) as LinearLayout - linearLayout.addView(binding.root) - binding.passwordEdit.requestFocus() - } - } - - private fun onOk() { - val password = binding.passwordEdit.text!!.toString() - if (password.isEmpty()) { - binding.passwordLayout.error = - getString(R.string.file_action_archive_password_error_empty) - return - } - args.path.archiveAddPassword(password) - notifyListenerOnce(true) - finish() - } - - override fun onCancel(dialog: DialogInterface) { - super.onCancel(dialog) - - notifyListenerOnce(false) - finish() - } - - fun onFinish() { - notifyListenerOnce(false) - } - - private fun notifyListenerOnce(successful: Boolean) { - if (isListenerNotified) { - return - } - args.listener(successful) - isListenerNotified = true - } - - companion object { - fun getTitle(context: Context): String = - context.getString(R.string.file_action_archive_password_title) - - fun getMessage(archiveFile: Path, context: Context): String = - context.getString( - R.string.file_action_archive_password_message_format, archiveFile.fileName - ) - } - - @Parcelize - class Args( - val path: @WriteWith Path, - val listener: @WriteWith() - (Boolean) -> Unit - ) : ParcelableArgs { - object ListenerParceler : Parceler<(Boolean) -> Unit> { - override fun create(parcel: Parcel): (Boolean) -> Unit = - parcel.readParcelable()!!.let { - { successful -> - it.sendResult(Bundle().putArgs(ListenerArgs(successful))) - } - } - - override fun ((Boolean) -> Unit).write(parcel: Parcel, flags: Int) { - parcel.writeParcelable( - RemoteCallback { - val args = it.getArgs() - this(args.successful) - }, flags - ) - } - - @Parcelize - private class ListenerArgs( - val successful: Boolean - ) : ParcelableArgs - } - } - - @Parcelize - private class State( - val hierarchyState: SparseArray - ) : ParcelableState -} diff --git a/app/src/main/java/me/zhanghai/android/files/filejob/FileJob.kt b/app/src/main/java/me/zhanghai/android/files/filejob/FileJob.kt deleted file mode 100644 index 98e0dd381..000000000 --- a/app/src/main/java/me/zhanghai/android/files/filejob/FileJob.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.filejob - -import me.zhanghai.android.files.util.showToast -import java.io.IOException -import java.io.InterruptedIOException -import java.util.Random - -abstract class FileJob { - val id = Random().nextInt() - - internal lateinit var service: FileJobService - private set - - fun runOn(service: FileJobService) { - this.service = service - try { - run() - // TODO: Toast - } catch (e: InterruptedIOException) { - // TODO - e.printStackTrace() - } catch (e: Exception) { - e.printStackTrace() - service.showToast(e.toString()) - } finally { - service.notificationManager.cancel(id) - } - } - - @Throws(IOException::class) - protected abstract fun run() -} diff --git a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictAction.kt b/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictAction.kt deleted file mode 100644 index 8e6452b72..000000000 --- a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictAction.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (c) 2020 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.filejob - -enum class FileJobConflictAction { - MERGE_OR_REPLACE, - RENAME, - SKIP, - CANCEL, - CANCELED -} diff --git a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogActivity.kt b/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogActivity.kt deleted file mode 100644 index 334aa36c0..000000000 --- a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogActivity.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.filejob - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.commit -import me.zhanghai.android.files.app.AppActivity -import me.zhanghai.android.files.util.args -import me.zhanghai.android.files.util.putArgs - -class FileJobConflictDialogActivity : AppActivity() { - private val args by args() - - private lateinit var fragment: FileJobConflictDialogFragment - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // Calls ensureSubDecor(). - findViewById(android.R.id.content) - if (savedInstanceState == null) { - fragment = FileJobConflictDialogFragment().putArgs(args) - supportFragmentManager.commit { - add(fragment, FileJobConflictDialogFragment::class.java.name) - } - } else { - fragment = supportFragmentManager.findFragmentByTag( - FileJobConflictDialogFragment::class.java.name - ) as FileJobConflictDialogFragment - } - } - - override fun onDestroy() { - super.onDestroy() - - if (isFinishing) { - fragment.onFinish() - } - } -} diff --git a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogFragment.kt b/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogFragment.kt deleted file mode 100644 index 76fbe2285..000000000 --- a/app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogFragment.kt +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2019 Hai Zhang - * All Rights Reserved. - */ - -package me.zhanghai.android.files.filejob - -import android.app.Dialog -import android.content.Context -import android.content.DialogInterface -import android.os.Bundle -import android.os.Parcel -import android.view.WindowManager -import android.widget.Button -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.appcompat.app.AlertDialog -import androidx.appcompat.app.AppCompatDialogFragment -import androidx.core.view.isVisible -import androidx.core.widget.NestedScrollView -import androidx.core.widget.doAfterTextChanged -import androidx.interpolator.view.animation.FastOutSlowInInterpolator -import coil.dispose -import coil.load -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import kotlinx.parcelize.Parceler -import kotlinx.parcelize.Parcelize -import kotlinx.parcelize.WriteWith -import me.zhanghai.android.files.R -import me.zhanghai.android.files.coil.AppIconPackageName -import me.zhanghai.android.files.compat.requireViewByIdCompat -import me.zhanghai.android.files.databinding.FileJobConflictDialogViewBinding -import me.zhanghai.android.files.file.FileItem -import me.zhanghai.android.files.file.fileSize -import me.zhanghai.android.files.file.formatShort -import me.zhanghai.android.files.file.iconRes -import me.zhanghai.android.files.file.lastModifiedInstant -import me.zhanghai.android.files.filelist.appDirectoryPackageName -import me.zhanghai.android.files.filelist.supportsThumbnail -import me.zhanghai.android.files.provider.common.isEncrypted -import me.zhanghai.android.files.util.ParcelableArgs -import me.zhanghai.android.files.util.ParcelableState -import me.zhanghai.android.files.util.RemoteCallback -import me.zhanghai.android.files.util.args -import me.zhanghai.android.files.util.finish -import me.zhanghai.android.files.util.getArgs -import me.zhanghai.android.files.util.getState -import me.zhanghai.android.files.util.layoutInflater -import me.zhanghai.android.files.util.putArgs -import me.zhanghai.android.files.util.putState -import me.zhanghai.android.files.util.readParcelable -import me.zhanghai.android.files.util.setTextWithSelection -import me.zhanghai.android.files.util.shortAnimTime -import me.zhanghai.android.files.util.showSoftInput - -class FileJobConflictDialogFragment : AppCompatDialogFragment() { - private val args by args() - - private lateinit var binding: FileJobConflictDialogViewBinding - - private var isListenerNotified = false - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - - outState.putState(State(binding.allCheck.isChecked)) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val sourceFile = args.sourceFile - val targetFile = args.targetFile - val title = getTitle(sourceFile, targetFile, requireContext()) - val message = getMessage(sourceFile, targetFile, args.type, requireContext()) - val isMerge = isMerge(sourceFile, targetFile) - val positiveButtonRes = if (isMerge) R.string.merge else R.string.replace - return MaterialAlertDialogBuilder(requireContext(), theme) - .setTitle(title) - .setMessage(message) - .apply { - binding = FileJobConflictDialogViewBinding.inflate(context.layoutInflater) - binding.targetNameText.setText( - if (isMerge) { - R.string.file_job_merge_target_name - } else { - R.string.file_job_replace_target_name - } - ) - bindFileItem( - targetFile, binding.targetIconImage, binding.targetThumbnailImage, - binding.targetAppIconBadgeImage, binding.targetBadgeImage, - binding.targetDescriptionText - ) - binding.sourceNameText.setText( - if (isMerge) { - R.string.file_job_merge_source_name - } else { - R.string.file_job_replace_source_name - } - ) - bindFileItem( - sourceFile, binding.sourceIconImage, binding.sourceThumbnailImage, - binding.sourceAppIconBadgeImage, binding.sourceBadgeImage, - binding.sourceDescriptionText - ) - binding.showNameLayout.setOnClickListener { - val visible = !binding.nameLayout.isVisible - binding.showNameArrowImage.animate() - .rotation(if (visible) 90f else 0f) - .setDuration(shortAnimTime.toLong()) - .setInterpolator(FastOutSlowInInterpolator()) - .start() - binding.nameLayout.isVisible = visible - if (visible) { - binding.nameEdit.requestFocus() - binding.nameEdit.showSoftInput() - } - } - val targetFileName = targetFile.path.fileName.toString() - binding.nameEdit.setTextWithSelection(targetFileName) - binding.nameEdit.doAfterTextChanged { - val hasNewName = hasNewName() - binding.allCheck.isEnabled = !hasNewName - if (hasNewName) { - binding.allCheck.isChecked = false - } - val positiveButton = requireDialog() - .requireViewByIdCompat