├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml └── workflows │ ├── DeleteWorkflowRuns.yml │ └── android.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── .replit ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── app_icon-playstore.png │ ├── assets │ ├── font │ │ └── JetBrainsMono-Regular.ttf │ └── textmate │ │ ├── README.md │ │ ├── dark.json │ │ ├── json │ │ ├── language-configuration.json │ │ └── syntax │ │ │ └── json.tmLanguage.json │ │ ├── kotlin │ │ ├── language-configuration.json │ │ └── syntax │ │ │ └── kotlin.tmLanguage │ │ ├── light.tmTheme │ │ └── xml │ │ ├── language-configuration.json │ │ └── syntax │ │ └── xml.tmLanguage.json │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── raival │ │ └── fileexplorer │ │ ├── App.kt │ │ ├── activity │ │ ├── BaseActivity.kt │ │ ├── MainActivity.kt │ │ ├── SettingsActivity.kt │ │ ├── TextEditorActivity.kt │ │ ├── adapter │ │ │ └── BookmarksAdapter.kt │ │ ├── editor │ │ │ ├── autocomplete │ │ │ │ ├── CustomCompletionItemAdapter.kt │ │ │ │ └── CustomCompletionLayout.kt │ │ │ ├── language │ │ │ │ ├── java │ │ │ │ │ ├── JavaCodeLanguage.kt │ │ │ │ │ └── JavaFormatter.kt │ │ │ │ ├── json │ │ │ │ │ ├── JsonFormatter.kt │ │ │ │ │ └── JsonLanguage.kt │ │ │ │ ├── kotlin │ │ │ │ │ └── KotlinCodeLanguage.kt │ │ │ │ └── xml │ │ │ │ │ └── XmlLanguage.kt │ │ │ ├── scheme │ │ │ │ ├── DarkScheme.kt │ │ │ │ └── LightScheme.kt │ │ │ └── view │ │ │ │ └── SymbolInputView.kt │ │ └── model │ │ │ ├── MainViewModel.kt │ │ │ └── TextEditorViewModel.kt │ │ ├── common │ │ ├── BackgroundTask.kt │ │ ├── dialog │ │ │ ├── CustomDialog.kt │ │ │ └── OptionsDialog.kt │ │ └── view │ │ │ ├── BottomBarView.kt │ │ │ └── TabView.kt │ │ ├── extension │ │ ├── File.kt │ │ ├── Int.kt │ │ ├── Long.kt │ │ └── String.kt │ │ ├── glide │ │ ├── FileExplorerGlideModule.java │ │ ├── apk │ │ │ ├── ApkIconDataFetcher.java │ │ │ ├── ApkIconModelLoader.java │ │ │ └── ApkIconModelLoaderFactory.java │ │ ├── icon │ │ │ ├── IconDataFetcher.java │ │ │ ├── IconModelLoader.java │ │ │ └── IconModelLoaderFactory.java │ │ └── model │ │ │ └── IconRes.kt │ │ ├── tab │ │ ├── BaseDataHolder.kt │ │ ├── BaseTabFragment.kt │ │ ├── apps │ │ │ ├── AppsTabDataHolder.kt │ │ │ ├── AppsTabFragment.kt │ │ │ ├── adapter │ │ │ │ └── AppListAdapter.kt │ │ │ ├── model │ │ │ │ └── Apk.kt │ │ │ └── resolver │ │ │ │ └── ApkResolver.kt │ │ └── file │ │ │ ├── FileExplorerTabDataHolder.kt │ │ │ ├── FileExplorerTabFragment.kt │ │ │ ├── adapter │ │ │ ├── FileListAdapter.kt │ │ │ └── PathHistoryAdapter.kt │ │ │ ├── dialog │ │ │ ├── FileInfoDialog.kt │ │ │ ├── SearchDialog.kt │ │ │ └── TasksDialog.kt │ │ │ ├── misc │ │ │ ├── APKSignerUtils.kt │ │ │ ├── BuildUtils.kt │ │ │ ├── FileMimeTypes.kt │ │ │ ├── FileOpener.kt │ │ │ ├── FileUtils.kt │ │ │ ├── IconHelper.kt │ │ │ ├── ZipUtils.kt │ │ │ └── md5 │ │ │ │ ├── HashUtils.kt │ │ │ │ ├── MessageDigestAlgorithm.kt │ │ │ │ └── StringUtils.kt │ │ │ ├── model │ │ │ ├── FileItem.kt │ │ │ └── Task.kt │ │ │ ├── observer │ │ │ └── FileListObserver.kt │ │ │ ├── options │ │ │ └── FileOptionsHandler.kt │ │ │ └── task │ │ │ ├── CompressTask.kt │ │ │ ├── CopyTask.kt │ │ │ ├── CutTask.kt │ │ │ └── ExtractTask.kt │ │ └── util │ │ ├── Log.kt │ │ ├── PrefsUtils.kt │ │ └── Utils.kt │ └── res │ ├── drawable-v24 │ └── apk_placeholder.webp │ ├── drawable │ ├── app_icon_foreground.xml │ ├── archive_file_extension.png │ ├── code_file_extension.png │ ├── doc_file_extension.png │ ├── fastscroll_thumb.xml │ ├── font_file_extension.png │ ├── ic_baseline_add_24.xml │ ├── ic_baseline_arrow_back_24.xml │ ├── ic_baseline_assignment_24.xml │ ├── ic_baseline_bookmark_add_24.xml │ ├── ic_baseline_bookmark_remove_24.xml │ ├── ic_baseline_bug_report_24.xml │ ├── ic_baseline_chevron_right_24.xml │ ├── ic_baseline_delete_sweep_24.xml │ ├── ic_baseline_file_copy_24.xml │ ├── ic_baseline_folder_24.xml │ ├── ic_baseline_folder_open_24.xml │ ├── ic_baseline_info_24.xml │ ├── ic_baseline_layers_24.xml │ ├── ic_baseline_logout_24.xml │ ├── ic_baseline_more_vert_24.xml │ ├── ic_baseline_open_in_browser_24.xml │ ├── ic_baseline_open_in_new_24.xml │ ├── ic_baseline_restart_alt_24.xml │ ├── ic_baseline_save_24.xml │ ├── ic_baseline_select_all_24.xml │ ├── ic_baseline_sort_24.xml │ ├── ic_launcher_foreground.xml │ ├── ic_round_code_24.xml │ ├── ic_round_compress_24.xml │ ├── ic_round_content_cut_24.xml │ ├── ic_round_delete_forever_24.xml │ ├── ic_round_edit_24.xml │ ├── ic_round_edit_note_24.xml │ ├── ic_round_exit_to_app_24.xml │ ├── ic_round_home_24.xml │ ├── ic_round_insert_drive_file_24.xml │ ├── ic_round_key_24.xml │ ├── ic_round_manage_search_24.xml │ ├── ic_round_menu_24.xml │ ├── ic_round_play_arrow_24.xml │ ├── ic_round_redo_24.xml │ ├── ic_round_search_24.xml │ ├── ic_round_settings_24.xml │ ├── ic_round_share_24.xml │ ├── ic_round_tab_24.xml │ ├── ic_round_timelapse_24.xml │ ├── ic_round_undo_24.xml │ ├── image_file_extension.png │ ├── java_file_extension.png │ ├── kt_file_extension.png │ ├── music_file_extension.png │ ├── pdf_file_extension.png │ ├── powerpoint_file_extension.png │ ├── sql_file_extension.png │ ├── svg_file_extension.png │ ├── txt_file_extension.png │ ├── unknown_file_extension.png │ ├── vector_file_extension.png │ ├── video_file_extension.png │ ├── xls_file_extension.png │ └── xml_file_extension.png │ ├── layout │ ├── activity_main.xml │ ├── activity_main_drawer.xml │ ├── activity_main_drawer_bookmark_item.xml │ ├── apps_tab_app_item.xml │ ├── apps_tab_fragment.xml │ ├── bottom_bar_menu_item.xml │ ├── common_custom_dialog.xml │ ├── common_options_dialog.xml │ ├── common_options_dialog_item.xml │ ├── file_explorer_tab_file_item.xml │ ├── file_explorer_tab_fragment.xml │ ├── file_explorer_tab_info_dialog.xml │ ├── file_explorer_tab_info_dialog_item.xml │ ├── file_explorer_tab_path_history_view.xml │ ├── file_explorer_tab_placeholder.xml │ ├── file_explorer_tab_task_dialog.xml │ ├── file_explorer_tab_task_dialog_item.xml │ ├── input.xml │ ├── progress_view.xml │ ├── search_fragment.xml │ ├── settings_activity.xml │ ├── text_editor_activity.xml │ └── text_editor_completion_item.xml │ ├── menu │ ├── main_menu.xml │ ├── tab_menu.xml │ └── text_editor_menu.xml │ ├── mipmap-anydpi-v26 │ ├── app_icon.xml │ └── app_icon_round.xml │ ├── mipmap-hdpi │ ├── app_icon.png │ └── app_icon_round.png │ ├── mipmap-mdpi │ ├── app_icon.png │ └── app_icon_round.png │ ├── mipmap-xhdpi │ ├── app_icon.png │ └── app_icon_round.png │ ├── mipmap-xxhdpi │ ├── app_icon.png │ └── app_icon_round.png │ ├── mipmap-xxxhdpi │ ├── app_icon.png │ └── app_icon_round.png │ ├── values-night │ ├── colors.xml │ └── themes.xml │ ├── values │ ├── app_icon_background.xml │ ├── colors.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ └── provider_paths.xml ├── assets ├── screenshot1.png ├── screenshot2.png └── screenshot3.png ├── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── full_description.txt │ ├── images │ ├── icon.png │ └── phoneScreenshots │ │ ├── img1.jpg │ │ ├── img2.jpg │ │ └── img3.jpg │ └── short_description.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── testkey.keystore /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | # Original source: https://github.com/AndroidIDEOfficial/AndroidIDE/blob/main/.github/ISSUE_TEMPLATE/BUG.yml 2 | name: Bug Report 3 | description: File a bug report 4 | title: "[Bug]: " 5 | labels: ["bug"] 6 | body: 7 | - type: markdown 8 | attributes: 9 | value: | 10 | Thanks for taking the time to fill out this bug report! Please provide a proper title and clear description to this issue. 11 | 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: What happened? 16 | description: Describe the issue properly. 17 | placeholder: Describe the error 18 | validations: 19 | required: true 20 | - type: textarea 21 | id: expected-behavior 22 | attributes: 23 | label: What's the expected behavior? 24 | description: Tell us what is the expected behavior. 25 | placeholder: Describe the expected behavior. 26 | validations: 27 | required: true 28 | - type: dropdown 29 | id: version 30 | attributes: 31 | label: What version of File Explorer you are using? 32 | multiple: false 33 | options: 34 | - latest GitHub action 35 | - latest release (v1.1.0) 36 | - from IzzyOnDroid 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: logs 41 | attributes: 42 | label: Relevant log output 43 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 44 | render: shell 45 | - type: checkboxes 46 | id: not-a-duplicate 47 | attributes: 48 | label: Duplicate issues 49 | description: Please make sure that there are no similar issues opened. Duplicate issues will be closed directly. If there are any similar looking issues, leave a comment there. 50 | options: 51 | - label: This issue has not been reported yet. 52 | required: true 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Requesting a new feature 3 | title: "[Feature]: " 4 | labels: ["feature request"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this feature request! Please provide a proper title and clear description to this feature. You must request one feature at a time, if you want to request multiple features, create a new request for each one. 10 | 11 | - type: textarea 12 | id: feature-details 13 | attributes: 14 | label: Feature Description 15 | description: Describe the feature properly. 16 | placeholder: Describe the feature 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: implementation 22 | attributes: 23 | label: How can this feature be implemented? 24 | description: Tell us any idea on how can we implement this feature 25 | placeholder: Describe the possible implementation 26 | validations: 27 | required: false 28 | 29 | - type: checkboxes 30 | id: not-a-duplicate 31 | attributes: 32 | label: Duplicate feature 33 | description: Please make sure that there are no similar feature opened. Duplicate feature will be closed directly. If there are any similar looking features, leave a comment there. 34 | options: 35 | - label: This feature has not been requested yet. 36 | required: true 37 | -------------------------------------------------------------------------------- /.github/workflows/DeleteWorkflowRuns.yml: -------------------------------------------------------------------------------- 1 | # Original at https://github.com/Sketchware-Pro/Sketchware-Pro/blob/main/.github/workflows/DeleteWorkflowRuns.yml 2 | 3 | name: 'Delete old workflow runs' 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | days: 8 | description: 'Number of retains days.' 9 | required: true 10 | default: '20' 11 | 12 | minimum_runs: 13 | description: 'The minimum runs to keep for each workflow.' 14 | required: true 15 | default: '6' 16 | 17 | jobs: 18 | deleteWorkflowRuns: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Delete workflow runs 22 | uses: Mattraks/delete-workflow-runs@v2.0.3 23 | with: 24 | token: ${{ github.token }} 25 | repository: ${{ github.repository }} 26 | retain_days: ${{ github.event.inputs.days }} 27 | keep_minimum_runs: ${{ github.event.inputs.minimum_runs }} 28 | -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | # Original at https://github.com/Sketchware-Pro/Sketchware-Pro/blob/main/.github/workflows/android.yml 2 | 3 | name: Android CI 4 | 5 | on: 6 | push: 7 | paths: 8 | - '.github/workflows/android.yml' 9 | - 'app/**' 10 | - 'build-logic/**' 11 | - 'kotlinc/**' 12 | - 'gradle/**' 13 | - 'build.gradle' 14 | - 'gradle.properties' 15 | - 'gradlew' 16 | - 'gradlew.bat' 17 | - 'public-stable-ids.txt' 18 | - 'settings.gradle' 19 | pull_request: 20 | paths: 21 | - '.github/workflows/android.yml' 22 | - 'app/**' 23 | - 'build-logic/**' 24 | - 'kotlinc/**' 25 | - 'gradle/**' 26 | - 'build.gradle' 27 | - 'gradle.properties' 28 | - 'gradlew' 29 | - 'gradlew.bat' 30 | - 'public-stable-ids.txt' 31 | - 'settings.gradle' 32 | workflow_dispatch: 33 | 34 | jobs: 35 | build: 36 | name: Build release APK 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | 41 | - name: Set up JDK 18 42 | uses: actions/setup-java@v3 43 | with: 44 | java-version: 18 45 | distribution: temurin 46 | cache: gradle 47 | 48 | - name: Grant execute permissions for gradlew 49 | run: chmod +x gradlew 50 | 51 | - name: Build release apk 52 | uses: gradle/gradle-build-action@v2 53 | with: 54 | arguments: assembleRelease 55 | 56 | - name: Upload APK 57 | uses: actions/upload-artifact@v3 58 | with: 59 | name: apk-release 60 | path: app/build/outputs/apk/release 61 | 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | File Explorer -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 119 | 120 | 122 | 123 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "bash" 2 | run = "" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI](https://github.com/Raival-e/File-Explorer/actions/workflows/android.yml/badge.svg) 2 | [![License](https://img.shields.io/github/license/Raival-e/File-Explorer)](https://github.com/Raival-e/File-Explorer/blob/master/LICENSE) 3 | ![Commit Activity](https://img.shields.io/github/commit-activity/m/Raival-e/File-Explorer) 4 | [![Total downloads](https://img.shields.io/github/downloads/Raival-e/File-Explorer/total)](https://github.com/Raival-e/File-Explorer/releases) 5 | ![Repository Size](https://img.shields.io/github/repo-size/Raival-e/File-Explorer) 6 | 7 | > [!WARNING] 8 | > This project is no longer maintained. Check out the new version [here](https://github.com/Raival-e/File-Explorer-Compose). 9 | 10 | # File Explorer 11 | 12 | A full-featured and lightweight file manager with Material 3 Dynamic colors 13 | 14 | # Screenshots 15 | 16 |
17 | 18 |
19 | 20 | # Features 21 | 22 | - Open source and simple. 23 | - All basic file management functionality (e.g. copy, paste,.. etc) are supported. 24 | - Support for multiple tabs, and Tasks which make managing files much easier. 25 | - Powerful Code Editor ([Sora Editor](https://github.com/Rosemoe/sora-editor)). 26 | - Deep search that allows you to search in files contents. 27 | 28 | # Upcoming features 29 | - [ ] Built-in audio/video player and PDF/image viewer 30 | - [ ] Archive viewer 31 | - [ ] Support for exploring root/external storage 32 | 33 | # Download 34 | 35 | [Get it on IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.raival.fileexplorer) 36 | 37 | - Latest release from [here](https://github.com/Raival-e/File-Explorer/releases/tag/v1.1.0). 38 | - Latest debug build from [Github Actions](https://github.com/Raival-e/File-Explorer/actions). 39 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 32 8 | 9 | defaultConfig { 10 | applicationId "com.raival.fileexplorer" 11 | minSdk 26 12 | targetSdk 32 13 | versionCode 2 14 | versionName "1.1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | packagingOptions { 20 | resources.excludes.add("license/*") 21 | } 22 | 23 | signingConfigs { 24 | debug { 25 | storeFile file('../testkey.keystore') 26 | storePassword 'testkey' 27 | keyAlias 'testkey' 28 | keyPassword 'testkey' 29 | } 30 | release { 31 | storeFile file("../testkey.keystore") 32 | storePassword "testkey" 33 | keyAlias "testkey" 34 | keyPassword "testkey" 35 | } 36 | } 37 | 38 | buildTypes { 39 | release { 40 | minifyEnabled false 41 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 42 | signingConfig signingConfigs.release 43 | } 44 | } 45 | compileOptions { 46 | coreLibraryDesugaringEnabled true 47 | 48 | sourceCompatibility JavaVersion.VERSION_11 49 | targetCompatibility JavaVersion.VERSION_11 50 | } 51 | 52 | kotlinOptions { 53 | jvmTarget = '1.8' 54 | } 55 | } 56 | 57 | dependencies { 58 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9' 59 | 60 | implementation fileTree(dir: "libs", include: ["*.jar"]) 61 | 62 | implementation "androidx.appcompat:appcompat:1.5.1" 63 | implementation "com.google.android.material:material:1.7.0" 64 | implementation "com.google.code.gson:gson:2.9.1" 65 | implementation 'commons-io:commons-io:2.11.0' 66 | 67 | implementation 'com.github.bumptech.glide:glide:4.14.0' 68 | annotationProcessor 'com.github.bumptech.glide:compiler:4.14.0' 69 | 70 | implementation "com.pixplicity.easyprefs:EasyPrefs:1.10.0" 71 | implementation 'net.lingala.zip4j:zip4j:2.11.2' 72 | 73 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' 74 | implementation "io.github.Rosemoe.sora-editor:editor:0.17.1", { 75 | exclude group: "androidx.annotation", module: "annotation" 76 | } 77 | implementation "io.github.Rosemoe.sora-editor:language-textmate:0.17.1" 78 | implementation "io.github.Rosemoe.sora-editor:language-java:0.17.1" 79 | 80 | testImplementation "junit:junit:4.13.2" 81 | androidTestImplementation "androidx.test.ext:junit:1.1.3" 82 | androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" 83 | } 84 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 15 | 16 | 25 | 29 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 49 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/app_icon-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/app_icon-playstore.png -------------------------------------------------------------------------------- /app/src/main/assets/font/JetBrainsMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/assets/font/JetBrainsMono-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/assets/textmate/README.md: -------------------------------------------------------------------------------- 1 | # Disclaimer 2 | 3 | These files were copied from [CodeAssist](https://github.com/tyron12233/CodeAssist), and minor 4 | modifications were applied to some of them. Original 5 | source: https://github.com/tyron12233/CodeAssist/tree/main/app/src/main/assets/textmate -------------------------------------------------------------------------------- /app/src/main/assets/textmate/json/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": [ 5 | "/*", 6 | "*/" 7 | ] 8 | }, 9 | "brackets": [ 10 | [ 11 | "{", 12 | "}" 13 | ], 14 | [ 15 | "[", 16 | "]" 17 | ] 18 | ], 19 | "autoClosingPairs": [ 20 | [ 21 | "{", 22 | "}" 23 | ], 24 | [ 25 | "[", 26 | "]" 27 | ], 28 | { 29 | "open": "\"", 30 | "close": "\"", 31 | "notIn": [ 32 | "string" 33 | ] 34 | }, 35 | { 36 | "open": "'", 37 | "close": "'", 38 | "notIn": [ 39 | "string" 40 | ] 41 | }, 42 | { 43 | "open": "/**", 44 | "close": " */", 45 | "notIn": [ 46 | "string" 47 | ] 48 | } 49 | ], 50 | "surroundingPairs": [ 51 | [ 52 | "{", 53 | "}" 54 | ], 55 | [ 56 | "[", 57 | "]" 58 | ], 59 | [ 60 | "\"", 61 | "\"" 62 | ] 63 | ], 64 | "folding": { 65 | "offSide": false, 66 | "markers": { 67 | "start": "^\\s*//\\s*#region", 68 | "end": "^\\s*//\\s*#endregion" 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /app/src/main/assets/textmate/kotlin/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "blockComment": [ 5 | "/*", 6 | "*/" 7 | ] 8 | }, 9 | "brackets": [ 10 | [ 11 | "{", 12 | "}" 13 | ], 14 | [ 15 | "[", 16 | "]" 17 | ], 18 | [ 19 | "(", 20 | ")" 21 | ], 22 | [ 23 | "<", 24 | ">" 25 | ] 26 | ], 27 | "autoClosingPairs": [ 28 | { 29 | "open": "{", 30 | "close": "}" 31 | }, 32 | { 33 | "open": "[", 34 | "close": "]" 35 | }, 36 | { 37 | "open": "(", 38 | "close": ")" 39 | }, 40 | { 41 | "open": "'", 42 | "close": "'", 43 | "notIn": [ 44 | "string", 45 | "comment" 46 | ] 47 | }, 48 | { 49 | "open": "\"", 50 | "close": "\"", 51 | "notIn": [ 52 | "string" 53 | ] 54 | }, 55 | { 56 | "open": "/*", 57 | "close": " */", 58 | "notIn": [ 59 | "string" 60 | ] 61 | } 62 | ], 63 | "surroundingPairs": [ 64 | [ 65 | "{", 66 | "}" 67 | ], 68 | [ 69 | "[", 70 | "]" 71 | ], 72 | [ 73 | "(", 74 | ")" 75 | ], 76 | [ 77 | "<", 78 | ">" 79 | ], 80 | [ 81 | "'", 82 | "'" 83 | ], 84 | [ 85 | "\"", 86 | "\"" 87 | ] 88 | ], 89 | "folding": { 90 | "offSide": false, 91 | "markers": { 92 | "start": "^\\s*//\\s*#region", 93 | "end": "^\\s*//\\s*#endregion" 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /app/src/main/assets/textmate/xml/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": [ 4 | "" 6 | ] 7 | }, 8 | "brackets": [ 9 | [ 10 | "<", 11 | "/>" 12 | ], 13 | [ 14 | "[", 15 | "]" 16 | ], 17 | [ 18 | "(", 19 | ")" 20 | ] 21 | ], 22 | "autoClosingPairs": [ 23 | [ 24 | "{", 25 | "}" 26 | ], 27 | [ 28 | "[", 29 | "]" 30 | ], 31 | [ 32 | "(", 33 | ")" 34 | ], 35 | { 36 | "open": "\"", 37 | "close": "\"", 38 | "notIn": [ 39 | "string" 40 | ] 41 | }, 42 | { 43 | "open": "'", 44 | "close": "'", 45 | "notIn": [ 46 | "string" 47 | ] 48 | }, 49 | { 50 | "open": "/**", 51 | "close": " */", 52 | "notIn": [ 53 | "string" 54 | ] 55 | } 56 | ], 57 | "surroundingPairs": [ 58 | [ 59 | "{", 60 | "}" 61 | ], 62 | [ 63 | "[", 64 | "]" 65 | ], 66 | [ 67 | "(", 68 | ")" 69 | ], 70 | [ 71 | "\"", 72 | "\"" 73 | ], 74 | [ 75 | "'", 76 | "'" 77 | ], 78 | [ 79 | "<", 80 | ">" 81 | ] 82 | ], 83 | "folding": { 84 | "offSide": false, 85 | "markers": { 86 | "start": "^\\s*//\\s*#region", 87 | "end": "^\\s*//\\s*#endregion" 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/App.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer 2 | 3 | import android.app.Application 4 | import android.content.ClipData 5 | import android.content.ClipboardManager 6 | import android.content.Context 7 | import android.os.Process 8 | import android.widget.Toast 9 | import com.pixplicity.easyprefs.library.Prefs 10 | import com.raival.fileexplorer.util.Log 11 | import kotlin.system.exitProcess 12 | 13 | class App : Application() { 14 | override fun onCreate() { 15 | Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable? -> 16 | Log.e("AppCrash", "", throwable) 17 | Process.killProcess(Process.myPid()) 18 | exitProcess(2) 19 | } 20 | 21 | super.onCreate() 22 | appContext = this 23 | 24 | Prefs.Builder() 25 | .setContext(applicationContext) 26 | .setPrefsName("Prefs") 27 | .setMode(MODE_PRIVATE) 28 | .build() 29 | Log.start(appContext) 30 | } 31 | 32 | companion object { 33 | lateinit var appContext: Context 34 | 35 | @JvmStatic 36 | fun showMsg(message: String?) { 37 | Toast.makeText(appContext, message, Toast.LENGTH_SHORT).show() 38 | } 39 | 40 | @JvmStatic 41 | fun copyString(string: String?) { 42 | (appContext.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip( 43 | ClipData.newPlainText("clipboard", string) 44 | ) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/adapter/BookmarksAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.graphics.Color 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import androidx.recyclerview.widget.RecyclerView 10 | import com.raival.fileexplorer.App.Companion.showMsg 11 | import com.raival.fileexplorer.R 12 | import com.raival.fileexplorer.activity.MainActivity 13 | import com.raival.fileexplorer.tab.file.misc.IconHelper 14 | import com.raival.fileexplorer.util.PrefsUtils 15 | import com.raival.fileexplorer.util.Utils 16 | import java.io.File 17 | 18 | class BookmarksAdapter(private val activity: MainActivity) : 19 | RecyclerView.Adapter() { 20 | private var list = arrayListOf() 21 | 22 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 23 | @SuppressLint("InflateParams") val v = 24 | activity.layoutInflater.inflate(R.layout.activity_main_drawer_bookmark_item, null) 25 | v.layoutParams = RecyclerView.LayoutParams( 26 | ViewGroup.LayoutParams.MATCH_PARENT, 27 | ViewGroup.LayoutParams.WRAP_CONTENT 28 | ) 29 | return ViewHolder(v) 30 | } 31 | 32 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 33 | holder.bind() 34 | } 35 | 36 | override fun getItemCount(): Int { 37 | return PrefsUtils.TextEditor.fileExplorerTabBookmarks.also { list = it }.size 38 | } 39 | 40 | inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { 41 | var name: TextView 42 | var details: TextView 43 | var icon: ImageView 44 | var background: View 45 | 46 | init { 47 | name = v.findViewById(R.id.name) 48 | details = v.findViewById(R.id.details) 49 | icon = v.findViewById(R.id.icon) 50 | background = v.findViewById(R.id.background) 51 | } 52 | 53 | @SuppressLint("NotifyDataSetChanged") 54 | fun bind() { 55 | val position = adapterPosition 56 | val file = File(list[position]) 57 | if (file.isFile && file.name.endsWith(".extension")) { 58 | name.text = file.name.substring(0, file.name.length - 10) 59 | } else { 60 | name.text = file.name 61 | } 62 | if (!file.exists()) { 63 | name.setTextColor(Color.RED) 64 | details.setTextColor(Color.RED) 65 | background.setOnClickListener { 66 | showMsg("This file doesn't exist anymore") 67 | list.remove(file.absolutePath) 68 | PrefsUtils.General.setFileExplorerTabBookmarks(list) 69 | list.clear() 70 | notifyDataSetChanged() 71 | } 72 | } else { 73 | name.setTextColor(Utils.getColorAttribute(R.attr.colorOnSurface, activity)) 74 | details.setTextColor(Utils.getColorAttribute(R.attr.colorOnSurface, activity)) 75 | background.setOnClickListener { activity.onBookmarkSelected(file) } 76 | } 77 | details.text = file.absolutePath 78 | IconHelper.setFileIcon(icon, file) 79 | background.setOnLongClickListener { 80 | list.remove(file.absolutePath) 81 | PrefsUtils.General.setFileExplorerTabBookmarks(list) 82 | list.clear() 83 | notifyDataSetChanged() 84 | true 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/autocomplete/CustomCompletionItemAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.autocomplete 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.TextView 7 | import com.raival.fileexplorer.R 8 | import com.raival.fileexplorer.extension.toDp 9 | import io.github.rosemoe.sora.widget.component.EditorCompletionAdapter 10 | 11 | class CustomCompletionItemAdapter : EditorCompletionAdapter() { 12 | override fun getItemHeight(): Int = 45.toDp() 13 | 14 | public override fun getView( 15 | pos: Int, 16 | view: View?, 17 | parent: ViewGroup, 18 | isCurrentCursorPosition: Boolean 19 | ): View { 20 | val v: View = view ?: LayoutInflater.from(context) 21 | .inflate(R.layout.text_editor_completion_item, parent, false) 22 | 23 | val item = getItem(pos) 24 | var tv = v.findViewById(R.id.result_item_label) 25 | val iv = v.findViewById(R.id.result_item_image) 26 | 27 | tv.text = item.label 28 | tv = v.findViewById(R.id.result_item_desc) 29 | tv.text = item.desc 30 | v.tag = pos 31 | iv.text = item.desc.subSequence(0, 1) 32 | 33 | return v 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/autocomplete/CustomCompletionLayout.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.autocomplete 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.GradientDrawable 5 | import android.os.SystemClock 6 | import android.view.MotionEvent 7 | import android.view.View 8 | import android.widget.* 9 | import android.widget.AdapterView.OnItemClickListener 10 | import com.raival.fileexplorer.App.Companion.showMsg 11 | import com.raival.fileexplorer.extension.toDp 12 | import io.github.rosemoe.sora.widget.component.CompletionLayout 13 | import io.github.rosemoe.sora.widget.component.EditorAutoCompletion 14 | import io.github.rosemoe.sora.widget.schemes.EditorColorScheme 15 | 16 | class CustomCompletionLayout : CompletionLayout { 17 | private lateinit var mListView: ListView 18 | private lateinit var mProgressBar: ProgressBar 19 | private lateinit var mBackground: GradientDrawable 20 | private lateinit var mEditorAutoCompletion: EditorAutoCompletion 21 | 22 | override fun onApplyColorScheme(colorScheme: EditorColorScheme) { 23 | mBackground.setStroke( 24 | 1, 25 | colorScheme.getColor(EditorColorScheme.COMPLETION_WND_BACKGROUND) 26 | ) 27 | mBackground.setColor(colorScheme.getColor(EditorColorScheme.COMPLETION_WND_BACKGROUND)) 28 | } 29 | 30 | override fun setEditorCompletion(completion: EditorAutoCompletion) { 31 | mEditorAutoCompletion = completion 32 | } 33 | 34 | override fun inflate(context: Context): View { 35 | val layout = RelativeLayout(context) 36 | mProgressBar = ProgressBar(context) 37 | layout.addView(mProgressBar) 38 | 39 | val params = mProgressBar.layoutParams as RelativeLayout.LayoutParams 40 | params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT) 41 | params.height = 30.toDp() 42 | params.width = params.height 43 | 44 | mBackground = GradientDrawable() 45 | mBackground.cornerRadius = 8f.toDp() 46 | layout.background = mBackground 47 | 48 | mListView = ListView(context) 49 | mListView.dividerHeight = 0 50 | layout.addView(mListView, LinearLayout.LayoutParams(-1, -1)) 51 | mListView.onItemClickListener = 52 | OnItemClickListener { _: AdapterView<*>?, _: View?, position: Int, _: Long -> 53 | try { 54 | mEditorAutoCompletion.select(position) 55 | } catch (e: Exception) { 56 | showMsg(e.toString()) 57 | } 58 | } 59 | setLoading(true) 60 | return layout 61 | } 62 | 63 | override fun getCompletionList(): AdapterView<*> { 64 | return mListView 65 | } 66 | 67 | override fun setLoading(loading: Boolean) { 68 | mProgressBar.visibility = if (loading) View.VISIBLE else View.INVISIBLE 69 | } 70 | 71 | override fun ensureListPositionVisible(position: Int, incrementPixels: Int) { 72 | mListView.post { 73 | while (mListView.firstVisiblePosition + 1 > position && mListView.canScrollList(-1)) { 74 | performScrollList(incrementPixels / 2) 75 | } 76 | while (mListView.lastVisiblePosition - 1 < position && mListView.canScrollList(1)) { 77 | performScrollList(-incrementPixels / 2) 78 | } 79 | } 80 | } 81 | 82 | private fun performScrollList(offset: Int) { 83 | val adpView = completionList 84 | val down = SystemClock.uptimeMillis() 85 | var ev = MotionEvent.obtain(down, down, MotionEvent.ACTION_DOWN, 0f, 0f, 0) 86 | 87 | adpView.onTouchEvent(ev) 88 | ev.recycle() 89 | 90 | ev = MotionEvent.obtain(down, down, MotionEvent.ACTION_MOVE, 0f, offset.toFloat(), 0) 91 | adpView.onTouchEvent(ev) 92 | ev.recycle() 93 | 94 | ev = MotionEvent.obtain(down, down, MotionEvent.ACTION_CANCEL, 0f, offset.toFloat(), 0) 95 | adpView.onTouchEvent(ev) 96 | ev.recycle() 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/language/java/JavaCodeLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.language.java 2 | 3 | import io.github.rosemoe.sora.lang.format.Formatter 4 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandleResult 5 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler 6 | import io.github.rosemoe.sora.langs.java.JavaLanguage 7 | import io.github.rosemoe.sora.langs.java.JavaTextTokenizer 8 | import io.github.rosemoe.sora.langs.java.Tokens 9 | import io.github.rosemoe.sora.text.ContentReference 10 | import io.github.rosemoe.sora.text.TextUtils 11 | 12 | open class JavaCodeLanguage : JavaLanguage() { 13 | 14 | private val javaFormatter = JavaFormatter() 15 | private val newlineHandlers = arrayOf(BraceHandler()) 16 | 17 | override fun getFormatter(): Formatter { 18 | return javaFormatter 19 | } 20 | 21 | override fun getIndentAdvance(text: ContentReference, line: Int, column: Int): Int { 22 | val content = text.getLine(line).substring(0, column) 23 | return getIndentAdvance(content) 24 | } 25 | 26 | override fun useTab(): Boolean { 27 | return false 28 | } 29 | 30 | 31 | private fun getIndentAdvance(content: String): Int { 32 | val t = JavaTextTokenizer(content) 33 | var token: Tokens 34 | var advance = 0 35 | 36 | while (t.nextToken().also { token = it } !== Tokens.EOF) { 37 | if (token === Tokens.LBRACE) { 38 | advance++ 39 | } 40 | if (token === Tokens.LPAREN) { 41 | advance++ 42 | } 43 | 44 | if (advance > 0) { 45 | if (token === Tokens.RBRACE) { 46 | advance-- 47 | } 48 | if (token === Tokens.RPAREN) { 49 | advance-- 50 | } 51 | } 52 | } 53 | 54 | advance = 0.coerceAtLeast(advance) 55 | 56 | if (advance > 0) return 4 57 | return 0 58 | } 59 | 60 | override fun getNewlineHandlers(): Array { 61 | return newlineHandlers 62 | } 63 | 64 | inner class BraceHandler : NewlineHandler { 65 | override fun matchesRequirement(beforeText: String, afterText: String): Boolean { 66 | return (beforeText.endsWith("{") && afterText.startsWith("}")) 67 | || (beforeText.endsWith("(") && afterText.startsWith(")")) 68 | } 69 | 70 | override fun handleNewline( 71 | beforeText: String, 72 | afterText: String, 73 | tabSize: Int 74 | ): NewlineHandleResult { 75 | val count: Int = TextUtils.countLeadingSpaceCount(beforeText, tabSize) 76 | val advanceBefore: Int = getIndentAdvance(beforeText) 77 | val advanceAfter: Int = getIndentAdvance(afterText) 78 | var text: String 79 | val sb: StringBuilder = StringBuilder("\n") 80 | .append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab())) 81 | .append('\n') 82 | .append( 83 | TextUtils.createIndent(count + advanceAfter, tabSize, useTab()) 84 | .also { text = it }) 85 | val shiftLeft = text.length + 1 86 | return NewlineHandleResult(sb, shiftLeft) 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/language/json/JsonFormatter.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.language.json 2 | 3 | import io.github.rosemoe.sora.lang.format.Formatter 4 | import io.github.rosemoe.sora.text.Content 5 | import io.github.rosemoe.sora.text.TextRange 6 | import org.json.JSONArray 7 | import org.json.JSONObject 8 | 9 | class JsonFormatter : Formatter { 10 | private var receiver: Formatter.FormatResultReceiver? = null 11 | private var isRunning = false 12 | 13 | override fun format(text: Content, cursorRange: TextRange) { 14 | isRunning = true 15 | receiver?.onFormatSucceed(format(text.toString()), cursorRange) 16 | isRunning = false 17 | } 18 | 19 | override fun formatRegion(text: Content, rangeToFormat: TextRange, cursorRange: TextRange) { 20 | } 21 | 22 | private fun format(txt: String): String { 23 | if (txt.isEmpty()) return txt 24 | 25 | val isObject = txt.trim()[0] == '{' 26 | 27 | return try { 28 | if (isObject) JSONObject(txt).toString(2) 29 | else JSONArray(txt).toString(2) 30 | } catch (e: Exception) { 31 | txt 32 | } 33 | } 34 | 35 | override fun setReceiver(receiver: Formatter.FormatResultReceiver?) { 36 | this.receiver = receiver 37 | } 38 | 39 | override fun isRunning(): Boolean { 40 | return isRunning 41 | } 42 | 43 | override fun destroy() { 44 | receiver = null 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/language/json/JsonLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.language.json 2 | 3 | import com.raival.fileexplorer.App 4 | import io.github.rosemoe.sora.lang.format.Formatter 5 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandleResult 6 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler 7 | import io.github.rosemoe.sora.langs.java.JavaTextTokenizer 8 | import io.github.rosemoe.sora.langs.java.Tokens 9 | import io.github.rosemoe.sora.langs.textmate.TextMateLanguage 10 | import io.github.rosemoe.sora.text.ContentReference 11 | import io.github.rosemoe.sora.text.TextUtils 12 | import org.eclipse.tm4e.core.registry.IGrammarSource 13 | import org.eclipse.tm4e.core.registry.IThemeSource 14 | import java.io.InputStreamReader 15 | import java.io.Reader 16 | 17 | class JsonLanguage( 18 | iThemeSource: IThemeSource, 19 | iGrammarSource: IGrammarSource = IGrammarSource.fromInputStream( 20 | App.appContext.assets.open("textmate/json/syntax/json.tmLanguage.json"), 21 | "json.tmLanguage.json", 22 | null 23 | ), 24 | languageConfiguration: Reader = InputStreamReader(App.appContext.assets.open("textmate/json/language-configuration.json")), 25 | createIdentifiers: Boolean = true 26 | ) : TextMateLanguage(iGrammarSource, languageConfiguration, iThemeSource, createIdentifiers) { 27 | 28 | private val jsonFormatter = JsonFormatter() 29 | private val newlineHandlers = arrayOf(BraceHandler()) 30 | 31 | 32 | override fun getFormatter(): Formatter { 33 | return jsonFormatter 34 | } 35 | 36 | override fun getIndentAdvance(text: ContentReference, line: Int, column: Int): Int { 37 | val content = text.getLine(line).substring(0, column) 38 | return getIndentAdvance(content) 39 | } 40 | 41 | override fun useTab(): Boolean { 42 | return false 43 | } 44 | 45 | 46 | private fun getIndentAdvance(content: String): Int { 47 | val t = JavaTextTokenizer(content) 48 | var token: Tokens 49 | var advance = 0 50 | 51 | while (t.nextToken().also { token = it } !== Tokens.EOF) { 52 | if (token === Tokens.LBRACE) { 53 | advance++ 54 | } 55 | if (token === Tokens.LBRACK) { 56 | advance++ 57 | } 58 | 59 | if (advance > 0) { 60 | if (token === Tokens.RBRACE) { 61 | advance-- 62 | } 63 | if (token === Tokens.RBRACE) { 64 | advance-- 65 | } 66 | } 67 | } 68 | 69 | advance = 0.coerceAtLeast(advance) 70 | 71 | if (advance > 0) return 4 72 | return 0 73 | } 74 | 75 | override fun getNewlineHandlers(): Array { 76 | return newlineHandlers 77 | } 78 | 79 | inner class BraceHandler : NewlineHandler { 80 | override fun matchesRequirement(beforeText: String, afterText: String): Boolean { 81 | return (beforeText.endsWith("{") && afterText.startsWith("}")) 82 | || (beforeText.endsWith("[") && afterText.startsWith("]")) 83 | } 84 | 85 | override fun handleNewline( 86 | beforeText: String, 87 | afterText: String, 88 | tabSize: Int 89 | ): NewlineHandleResult { 90 | val count: Int = TextUtils.countLeadingSpaceCount(beforeText, tabSize) 91 | val advanceBefore: Int = getIndentAdvance(beforeText) 92 | val advanceAfter: Int = getIndentAdvance(afterText) 93 | var text: String 94 | val sb: StringBuilder = StringBuilder("\n") 95 | .append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab())) 96 | .append('\n') 97 | .append( 98 | TextUtils.createIndent(count + advanceAfter, tabSize, useTab()) 99 | .also { text = it }) 100 | val shiftLeft = text.length + 1 101 | return NewlineHandleResult(sb, shiftLeft) 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/language/kotlin/KotlinCodeLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.language.kotlin 2 | 3 | import com.raival.fileexplorer.App 4 | import com.raival.fileexplorer.activity.editor.language.java.JavaFormatter 5 | import io.github.rosemoe.sora.lang.format.Formatter 6 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandleResult 7 | import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler 8 | import io.github.rosemoe.sora.langs.java.JavaTextTokenizer 9 | import io.github.rosemoe.sora.langs.java.Tokens 10 | import io.github.rosemoe.sora.langs.textmate.TextMateLanguage 11 | import io.github.rosemoe.sora.text.ContentReference 12 | import io.github.rosemoe.sora.text.TextUtils 13 | import org.eclipse.tm4e.core.registry.IGrammarSource 14 | import org.eclipse.tm4e.core.registry.IThemeSource 15 | import java.io.InputStreamReader 16 | import java.io.Reader 17 | 18 | 19 | class KotlinCodeLanguage( 20 | iThemeSource: IThemeSource, 21 | iGrammarSource: IGrammarSource = IGrammarSource.fromInputStream( 22 | App.appContext.assets.open("textmate/kotlin/syntax/kotlin.tmLanguage"), 23 | "kotlin.tmLanguage", 24 | null 25 | ), 26 | languageConfiguration: Reader = InputStreamReader(App.appContext.assets.open("textmate/kotlin/language-configuration.json")), 27 | createIdentifiers: Boolean = true 28 | ) : TextMateLanguage(iGrammarSource, languageConfiguration, iThemeSource, createIdentifiers) { 29 | 30 | private val javaFormatter = JavaFormatter() 31 | private val newlineHandlers = arrayOf(BraceHandler()) 32 | 33 | override fun getFormatter(): Formatter { 34 | return javaFormatter 35 | } 36 | 37 | override fun getIndentAdvance(text: ContentReference, line: Int, column: Int): Int { 38 | val content = text.getLine(line).substring(0, column) 39 | return getIndentAdvance(content) 40 | } 41 | 42 | override fun useTab(): Boolean { 43 | return false 44 | } 45 | 46 | 47 | private fun getIndentAdvance(content: String): Int { 48 | val t = JavaTextTokenizer(content) 49 | var token: Tokens 50 | var advance = 0 51 | 52 | while (t.nextToken().also { token = it } !== Tokens.EOF) { 53 | if (token === Tokens.LBRACE) { 54 | advance++ 55 | } 56 | if (token === Tokens.LPAREN) { 57 | advance++ 58 | } 59 | 60 | if (advance > 0) { 61 | if (token === Tokens.RBRACE) { 62 | advance-- 63 | } 64 | if (token === Tokens.RPAREN) { 65 | advance-- 66 | } 67 | } 68 | } 69 | 70 | advance = 0.coerceAtLeast(advance) 71 | 72 | if (advance > 0) return 4 73 | return 0 74 | } 75 | 76 | override fun getNewlineHandlers(): Array { 77 | return newlineHandlers 78 | } 79 | 80 | inner class BraceHandler : NewlineHandler { 81 | override fun matchesRequirement(beforeText: String, afterText: String): Boolean { 82 | return (beforeText.endsWith("{") && afterText.startsWith("}")) 83 | || (beforeText.endsWith("(") && afterText.startsWith(")")) 84 | } 85 | 86 | override fun handleNewline( 87 | beforeText: String, 88 | afterText: String, 89 | tabSize: Int 90 | ): NewlineHandleResult { 91 | val count: Int = TextUtils.countLeadingSpaceCount(beforeText, tabSize) 92 | val advanceBefore: Int = getIndentAdvance(beforeText) 93 | val advanceAfter: Int = getIndentAdvance(afterText) 94 | var text: String 95 | val sb: StringBuilder = StringBuilder("\n") 96 | .append(TextUtils.createIndent(count + advanceBefore, tabSize, useTab())) 97 | .append('\n') 98 | .append( 99 | TextUtils.createIndent(count + advanceAfter, tabSize, useTab()) 100 | .also { text = it }) 101 | val shiftLeft = text.length + 1 102 | return NewlineHandleResult(sb, shiftLeft) 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/language/xml/XmlLanguage.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.language.xml 2 | 3 | import com.raival.fileexplorer.App 4 | import io.github.rosemoe.sora.langs.textmate.TextMateLanguage 5 | import org.eclipse.tm4e.core.registry.IGrammarSource 6 | import org.eclipse.tm4e.core.registry.IThemeSource 7 | import java.io.InputStreamReader 8 | import java.io.Reader 9 | 10 | class XmlLanguage( 11 | iThemeSource: IThemeSource, 12 | iGrammarSource: IGrammarSource = IGrammarSource.fromInputStream( 13 | App.appContext.assets.open("textmate/xml/syntax/xml.tmLanguage.json"), 14 | "xml.tmLanguage.json", 15 | null 16 | ), 17 | languageConfiguration: Reader = InputStreamReader(App.appContext.assets.open("textmate/xml/language-configuration.json")), 18 | createIdentifiers: Boolean = true 19 | ) : TextMateLanguage(iGrammarSource, languageConfiguration, iThemeSource, createIdentifiers) { 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/scheme/DarkScheme.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.scheme 2 | 3 | import io.github.rosemoe.sora.widget.schemes.EditorColorScheme 4 | 5 | class DarkScheme : EditorColorScheme() { 6 | override fun applyDefault() { 7 | super.applyDefault() 8 | setColor(ANNOTATION, -0x444ad7) 9 | setColor(FUNCTION_NAME, -0x332f27) 10 | setColor(IDENTIFIER_NAME, -0x332f27) 11 | setColor(IDENTIFIER_VAR, -0x678956) 12 | setColor(LITERAL, -0x9578a7) 13 | setColor(OPERATOR, -0x332f27) 14 | setColor(COMMENT, -0x7f7f80) 15 | setColor(KEYWORD, -0x3387ce) 16 | setColor(WHOLE_BACKGROUND, -0xd4d4d5) 17 | setColor(TEXT_NORMAL, -0x332f27) 18 | setColor(LINE_NUMBER_BACKGROUND, -0xcecccb) 19 | setColor(LINE_NUMBER, -0x9f9c9a) 20 | setColor(LINE_DIVIDER, -0x9f9c9a) 21 | setColor(SCROLL_BAR_THUMB, -0x59595a) 22 | setColor(SCROLL_BAR_THUMB_PRESSED, -0xa9a9aa) 23 | setColor(SELECTED_TEXT_BACKGROUND, -0xc98948) 24 | setColor(MATCHED_TEXT_BACKGROUND, -0xcda6c3) 25 | setColor(CURRENT_LINE, -0xcdcdce) 26 | setColor(SELECTION_INSERT, -0x332f27) 27 | setColor(SELECTION_HANDLE, -0x332f27) 28 | setColor(BLOCK_LINE, -0xa8a8a9) 29 | setColor(BLOCK_LINE_CURRENT, -0x22a8a8a9) 30 | setColor(NON_PRINTABLE_CHAR, -0x222223) 31 | setColor(TEXT_SELECTED, -0x332f27) 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/scheme/LightScheme.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.scheme 2 | 3 | import io.github.rosemoe.sora.widget.schemes.EditorColorScheme 4 | 5 | class LightScheme : EditorColorScheme() { 6 | override fun applyDefault() { 7 | super.applyDefault() 8 | setColor(ANNOTATION, -0x9b9b9c) 9 | setColor(FUNCTION_NAME, -0x1000000) 10 | setColor(IDENTIFIER_NAME, -0x1000000) 11 | setColor(IDENTIFIER_VAR, -0x479cc2) 12 | setColor(LITERAL, -0xd5ff01) 13 | setColor(OPERATOR, -0xc60000) 14 | setColor(COMMENT, -0xc080a1) 15 | setColor(KEYWORD, -0x80ff8c) 16 | setColor(WHOLE_BACKGROUND, -0x1) 17 | setColor(TEXT_NORMAL, -0x1000000) 18 | setColor(LINE_NUMBER_BACKGROUND, -0x1) 19 | setColor(LINE_NUMBER, -0x878788) 20 | setColor(SELECTED_TEXT_BACKGROUND, -0xcc6601) 21 | setColor(MATCHED_TEXT_BACKGROUND, -0x2b2b2c) 22 | setColor(CURRENT_LINE, -0x170d02) 23 | setColor(SELECTION_INSERT, -0xfc1415) 24 | setColor(SELECTION_HANDLE, -0xfc1415) 25 | setColor(BLOCK_LINE, -0x272728) 26 | setColor(BLOCK_LINE_CURRENT, 0) 27 | setColor(TEXT_SELECTED, -0x1) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/editor/view/SymbolInputView.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.editor.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.util.TypedValue 6 | import android.widget.Button 7 | import android.widget.LinearLayout 8 | import com.raival.fileexplorer.R 9 | import com.raival.fileexplorer.util.Utils 10 | import io.github.rosemoe.sora.widget.CodeEditor 11 | 12 | class SymbolInputView : LinearLayout { 13 | private var textColor = 0 14 | private lateinit var editor: CodeEditor 15 | 16 | constructor(context: Context?) : super(context) 17 | 18 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) 19 | 20 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( 21 | context, 22 | attrs, 23 | defStyleAttr 24 | ) 25 | 26 | init { 27 | setBackgroundColor(Utils.getColorAttribute(R.attr.backgroundColor, context)) 28 | setTextColor(Utils.getColorAttribute(R.attr.colorOnSurface, context)) 29 | orientation = HORIZONTAL 30 | } 31 | 32 | fun bindEditor(editor: CodeEditor): SymbolInputView { 33 | this.editor = editor 34 | return this 35 | } 36 | 37 | fun setTextColor(color: Int): SymbolInputView { 38 | for (i in 0 until childCount) { 39 | (getChildAt(i) as Button).setTextColor(color) 40 | } 41 | textColor = color 42 | return this 43 | } 44 | 45 | fun addSymbols(symbols: Array): SymbolInputView { 46 | for (symbol in symbols) addSymbol(symbol) 47 | return this 48 | } 49 | 50 | fun addSymbol( 51 | display: String, 52 | content: String = display, 53 | cursorPos: Int = content.length 54 | ): SymbolInputView { 55 | val btn = Button(context, null, android.R.attr.buttonStyleSmall) 56 | btn.text = display 57 | val out = TypedValue() 58 | context.theme.resolveAttribute(android.R.attr.selectableItemBackground, out, true) 59 | btn.setBackgroundResource(out.resourceId) 60 | btn.setTextColor(textColor) 61 | addView(btn, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)) 62 | 63 | btn.setOnClickListener { 64 | if (this::editor.isInitialized) editor.insertText(content, cursorPos) 65 | } 66 | return this 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/model/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.model 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.raival.fileexplorer.tab.BaseDataHolder 5 | import com.raival.fileexplorer.tab.file.model.Task 6 | 7 | class MainViewModel : ViewModel() { 8 | @JvmField 9 | val tasks = arrayListOf() 10 | private val dataHolders: MutableList = arrayListOf() 11 | 12 | fun addDataHolder(dataHolder: BaseDataHolder) { 13 | dataHolders.add(dataHolder) 14 | } 15 | 16 | fun getDataHolders(): ArrayList { 17 | return dataHolders as ArrayList 18 | } 19 | 20 | fun getDataHolder(tag: String): BaseDataHolder? { 21 | for (dataHolder in dataHolders) { 22 | if (dataHolder.tag == tag) return dataHolder 23 | } 24 | return null 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/activity/model/TextEditorViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.activity.model 2 | 3 | import androidx.lifecycle.ViewModel 4 | import io.github.rosemoe.sora.text.Content 5 | import java.io.File 6 | 7 | class TextEditorViewModel : ViewModel() { 8 | var file: File? = null 9 | var content: Content? = null 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/common/BackgroundTask.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.common 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Activity 5 | import android.os.Handler 6 | import android.os.Looper 7 | import android.view.View 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AlertDialog 10 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 11 | import com.raival.fileexplorer.R 12 | import kotlinx.coroutines.CoroutineScope 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.launch 15 | 16 | class BackgroundTask { 17 | private lateinit var preTask: Runnable 18 | private lateinit var task: Runnable 19 | private lateinit var postTask: Runnable 20 | private lateinit var alertDialog: AlertDialog 21 | 22 | fun setTasks(preTask: Runnable, task: Runnable, postTask: Runnable) { 23 | this.preTask = preTask 24 | this.task = task 25 | this.postTask = postTask 26 | } 27 | 28 | fun run() { 29 | CoroutineScope(Dispatchers.IO).launch { 30 | Handler(Looper.getMainLooper()).post(preTask) 31 | task.run() 32 | Handler(Looper.getMainLooper()).post(postTask) 33 | }.start() 34 | } 35 | 36 | fun dismiss() { 37 | if (this::alertDialog.isInitialized) alertDialog.dismiss() 38 | } 39 | 40 | @SuppressLint("ResourceType") 41 | fun showProgressDialog(msg: String, activity: Activity) { 42 | alertDialog = MaterialAlertDialogBuilder(activity) 43 | .setCancelable(false) 44 | .setView(getProgressView(msg, activity)) 45 | .show() 46 | } 47 | 48 | private fun getProgressView(msg: String, activity: Activity): View { 49 | @SuppressLint("InflateParams") val v = 50 | activity.layoutInflater.inflate(R.layout.progress_view, null) 51 | (v.findViewById(R.id.msg) as TextView).text = msg 52 | return v 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/common/dialog/OptionsDialog.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.common.dialog 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import android.widget.LinearLayout 9 | import android.widget.TextView 10 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 11 | import com.raival.fileexplorer.R 12 | 13 | class OptionsDialog(var title: String) : BottomSheetDialogFragment() { 14 | private val options = ArrayList() 15 | private var container: LinearLayout? = null 16 | 17 | fun addOption( 18 | label: String?, 19 | listener: View.OnClickListener?, 20 | dismissOnClick: Boolean 21 | ): OptionsDialog { 22 | return addOption(label, 0, listener, dismissOnClick) 23 | } 24 | 25 | fun addOption( 26 | label: String?, 27 | resId: Int, 28 | listener: View.OnClickListener?, 29 | dismissOnClick: Boolean 30 | ): OptionsDialog { 31 | val optionHolder = OptionHolder() 32 | optionHolder.dismissOnClick = dismissOnClick 33 | optionHolder.label = label 34 | optionHolder.listener = listener 35 | optionHolder.res = resId 36 | options.add(optionHolder) 37 | return this 38 | } 39 | 40 | override fun onCreateView( 41 | inflater: LayoutInflater, 42 | container: ViewGroup?, 43 | savedInstanceState: Bundle? 44 | ): View? { 45 | return inflater.inflate(R.layout.common_options_dialog, container, false) 46 | } 47 | 48 | override fun getTheme(): Int { 49 | return R.style.ThemeOverlay_Material3_BottomSheetDialog 50 | } 51 | 52 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 53 | super.onViewCreated(view, savedInstanceState) 54 | container = view.findViewById(R.id.container) 55 | (view.findViewById(R.id.title) as TextView).text = title 56 | view.findViewById(R.id.msg).visibility = View.GONE 57 | addOptions() 58 | } 59 | 60 | private fun addOptions() { 61 | for (optionHolder in options) { 62 | val v = layoutInflater.inflate(R.layout.common_options_dialog_item, container, false) 63 | if (optionHolder.res != 0) { 64 | val icon = v.findViewById(R.id.icon) 65 | icon.visibility = View.VISIBLE 66 | icon.setImageResource(optionHolder.res) 67 | } 68 | (v.findViewById(R.id.label) as TextView).text = optionHolder.label 69 | v.findViewById(R.id.background).setOnClickListener { view: View? -> 70 | if (optionHolder.listener != null) optionHolder.listener!!.onClick(view) 71 | if (optionHolder.dismissOnClick) v.post { dismiss() } 72 | } 73 | container!!.addView(v) 74 | } 75 | } 76 | 77 | private class OptionHolder { 78 | var label: String? = null 79 | var res = 0 80 | var listener: View.OnClickListener? = null 81 | var dismissOnClick = false 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/common/view/BottomBarView.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.common.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.widget.ImageView 8 | import android.widget.LinearLayout 9 | import android.widget.TextView 10 | import com.raival.fileexplorer.R 11 | import com.raival.fileexplorer.util.PrefsUtils 12 | 13 | class BottomBarView : LinearLayout { 14 | constructor(context: Context?) : super(context) 15 | constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) 16 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( 17 | context, 18 | attrs, 19 | defStyleAttr 20 | ) 21 | 22 | fun addItem(tag: String?, icon: Int, clickListener: OnClickListener?) { 23 | val view = (context 24 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater) 25 | .inflate(R.layout.bottom_bar_menu_item, this, false) 26 | view.setOnClickListener(clickListener) 27 | view.tooltipText = tag 28 | val label = view.findViewById(R.id.label) 29 | if (PrefsUtils.Settings.showBottomToolbarLabels) { 30 | label.text = tag 31 | } else { 32 | label.visibility = GONE 33 | } 34 | val image = view.findViewById(R.id.icon) 35 | image.setImageResource(icon) 36 | addView(view) 37 | } 38 | 39 | fun addItem(tag: String?, view: View) { 40 | view.tooltipText = tag 41 | addView(view) 42 | } 43 | 44 | fun clear() { 45 | removeAllViews() 46 | } 47 | 48 | fun onUpdatePrefs() { 49 | for (i in 0 until childCount) { 50 | val view = getChildAt(i) 51 | val label = view.findViewById(R.id.label) 52 | if (label != null) { 53 | label.visibility = 54 | if (PrefsUtils.Settings.showBottomToolbarLabels) VISIBLE else GONE 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/extension/Int.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.extension 2 | 3 | import android.util.TypedValue 4 | import com.raival.fileexplorer.App 5 | 6 | fun Int.toDp(): Int = TypedValue.applyDimension( 7 | TypedValue.COMPLEX_UNIT_DIP, 8 | this.toFloat(), 9 | App.appContext.resources.displayMetrics 10 | ).toInt() 11 | 12 | fun Float.toDp(): Float = TypedValue.applyDimension( 13 | TypedValue.COMPLEX_UNIT_DIP, 14 | this, 15 | App.appContext.resources.displayMetrics 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/extension/Long.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.extension 2 | 3 | import java.text.DecimalFormat 4 | import kotlin.math.log10 5 | import kotlin.math.pow 6 | 7 | fun Long.toFormattedSize(): String { 8 | if (this <= 0) return "0 B" 9 | val units = arrayOf("B", "kB", "MB", "GB", "TB") 10 | val digitGroups = (log10(this.toDouble()) / log10(1024.0)).toInt() 11 | return DecimalFormat("#,##0.#").format( 12 | this / 1024.0.pow(digitGroups.toDouble()) 13 | ) + " " + units[digitGroups] 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/extension/String.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.extension 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.reflect.TypeToken 5 | 6 | fun String.getStringList(): ArrayList { 7 | return Gson().fromJson(this, object : TypeToken>() {}.type) 8 | } 9 | 10 | fun String.surroundWithBrackets(): String { 11 | return "[$this]" 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/FileExplorerGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bumptech.glide.Glide; 9 | import com.bumptech.glide.Registry; 10 | import com.bumptech.glide.annotation.GlideModule; 11 | import com.bumptech.glide.module.AppGlideModule; 12 | import com.raival.fileexplorer.glide.apk.ApkIconModelLoaderFactory; 13 | import com.raival.fileexplorer.glide.icon.IconModelLoaderFactory; 14 | import com.raival.fileexplorer.glide.model.IconRes; 15 | 16 | @GlideModule 17 | public class FileExplorerGlideModule extends AppGlideModule { 18 | @Override 19 | public void registerComponents(@NonNull Context context, @NonNull Glide glide, Registry registry) { 20 | registry.prepend(String.class, Drawable.class, new ApkIconModelLoaderFactory(context)); 21 | registry.prepend(IconRes.class, Drawable.class, new IconModelLoaderFactory(context)); 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/apk/ApkIconDataFetcher.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.apk; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.content.ContextCompat; 8 | 9 | import com.bumptech.glide.Priority; 10 | import com.bumptech.glide.load.DataSource; 11 | import com.bumptech.glide.load.data.DataFetcher; 12 | import com.raival.fileexplorer.R; 13 | import com.raival.fileexplorer.tab.file.misc.FileUtils; 14 | 15 | import java.io.File; 16 | 17 | public class ApkIconDataFetcher implements DataFetcher { 18 | 19 | private final Context context; 20 | private final String model; 21 | 22 | public ApkIconDataFetcher(Context context, String model) { 23 | this.context = context; 24 | this.model = model; 25 | } 26 | 27 | @Override 28 | public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { 29 | Drawable apkIcon = FileUtils.INSTANCE.getApkIcon(new File(model)); 30 | if (apkIcon == null) 31 | apkIcon = ContextCompat.getDrawable(context, R.drawable.unknown_file_extension); 32 | callback.onDataReady(apkIcon); 33 | } 34 | 35 | @Override 36 | public void cleanup() { 37 | // Intentionally empty only because we're not opening an InputStream or another I/O resource! 38 | } 39 | 40 | @Override 41 | public void cancel() { 42 | // No cancellation procedure 43 | } 44 | 45 | @NonNull 46 | @Override 47 | public Class getDataClass() { 48 | return Drawable.class; 49 | } 50 | 51 | @NonNull 52 | @Override 53 | public DataSource getDataSource() { 54 | return DataSource.LOCAL; 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/apk/ApkIconModelLoader.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.apk; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import com.bumptech.glide.load.Options; 10 | import com.bumptech.glide.load.model.ModelLoader; 11 | import com.bumptech.glide.signature.ObjectKey; 12 | import com.raival.fileexplorer.tab.file.misc.FileMimeTypes; 13 | 14 | import java.util.Locale; 15 | 16 | public class ApkIconModelLoader implements ModelLoader { 17 | 18 | private final Context context; 19 | 20 | public ApkIconModelLoader(Context context) { 21 | this.context = context; 22 | } 23 | 24 | @Nullable 25 | @Override 26 | public LoadData buildLoadData(@NonNull String s, int width, int height, @NonNull Options options) { 27 | return new LoadData<>(new ObjectKey(s), new ApkIconDataFetcher(context, s)); 28 | } 29 | 30 | @Override 31 | public boolean handles(String s) { 32 | return s.toLowerCase(Locale.ROOT).endsWith(FileMimeTypes.apkType); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/apk/ApkIconModelLoaderFactory.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.apk; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bumptech.glide.load.model.ModelLoader; 9 | import com.bumptech.glide.load.model.ModelLoaderFactory; 10 | import com.bumptech.glide.load.model.MultiModelLoaderFactory; 11 | 12 | public class ApkIconModelLoaderFactory implements ModelLoaderFactory { 13 | 14 | private final Context context; 15 | 16 | public ApkIconModelLoaderFactory(Context context) { 17 | this.context = context; 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { 23 | return new ApkIconModelLoader(context); 24 | } 25 | 26 | @Override 27 | public void teardown() { 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/icon/IconDataFetcher.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.icon; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.core.content.ContextCompat; 8 | 9 | import com.bumptech.glide.Priority; 10 | import com.bumptech.glide.load.DataSource; 11 | import com.bumptech.glide.load.data.DataFetcher; 12 | import com.raival.fileexplorer.glide.model.IconRes; 13 | 14 | public class IconDataFetcher implements DataFetcher { 15 | 16 | private final Context context; 17 | private final IconRes model; 18 | 19 | public IconDataFetcher(Context context, IconRes model) { 20 | this.context = context; 21 | this.model = model; 22 | } 23 | 24 | @Override 25 | public void loadData(@NonNull Priority priority, @NonNull DataCallback callback) { 26 | Context ctx = (model.getContext() != null) ? model.getContext() : context; 27 | Drawable drawable = ContextCompat.getDrawable(ctx, model.getResId()); 28 | callback.onDataReady(drawable); 29 | } 30 | 31 | @Override 32 | public void cleanup() { 33 | // Intentionally empty only because we're not opening an InputStream or another I/O resource! 34 | } 35 | 36 | @Override 37 | public void cancel() { 38 | // No cancellation procedure 39 | } 40 | 41 | @NonNull 42 | @Override 43 | public Class getDataClass() { 44 | return Drawable.class; 45 | } 46 | 47 | @NonNull 48 | @Override 49 | public DataSource getDataSource() { 50 | return DataSource.LOCAL; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/icon/IconModelLoader.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.icon; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | 9 | import com.bumptech.glide.load.Options; 10 | import com.bumptech.glide.load.model.ModelLoader; 11 | import com.bumptech.glide.signature.ObjectKey; 12 | import com.raival.fileexplorer.glide.model.IconRes; 13 | 14 | public class IconModelLoader implements ModelLoader { 15 | 16 | private final Context context; 17 | 18 | public IconModelLoader(Context context) { 19 | this.context = context; 20 | } 21 | 22 | @Nullable 23 | @Override 24 | public LoadData buildLoadData(@NonNull IconRes s, int width, int height, @NonNull Options options) { 25 | return new LoadData<>(new ObjectKey(s), new IconDataFetcher(context, s)); 26 | } 27 | 28 | @Override 29 | public boolean handles(@NonNull IconRes s) { 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/icon/IconModelLoaderFactory.java: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.icon; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bumptech.glide.load.model.ModelLoader; 9 | import com.bumptech.glide.load.model.ModelLoaderFactory; 10 | import com.bumptech.glide.load.model.MultiModelLoaderFactory; 11 | import com.raival.fileexplorer.glide.model.IconRes; 12 | 13 | public class IconModelLoaderFactory implements ModelLoaderFactory { 14 | 15 | private final Context context; 16 | 17 | public IconModelLoaderFactory(Context context) { 18 | this.context = context; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | public ModelLoader build(@NonNull MultiModelLoaderFactory multiFactory) { 24 | return new IconModelLoader(context); 25 | } 26 | 27 | @Override 28 | public void teardown() { 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/glide/model/IconRes.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.glide.model 2 | 3 | import android.content.Context 4 | 5 | class IconRes(val resId: Int, val context: Context? = null) -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/BaseDataHolder.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab 2 | 3 | abstract class BaseDataHolder { 4 | abstract val tag: String 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/BaseTabFragment.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.lifecycle.ViewModelProvider 5 | import com.google.android.material.appbar.MaterialToolbar 6 | import com.raival.fileexplorer.activity.MainActivity 7 | import com.raival.fileexplorer.activity.model.MainViewModel 8 | import com.raival.fileexplorer.common.view.BottomBarView 9 | import com.raival.fileexplorer.common.view.TabView 10 | 11 | /** 12 | * Each TabFragment must handle the creation of its DataHolder and the related TabView using 13 | * the provided APIs or custom ones. 14 | */ 15 | abstract class BaseTabFragment : Fragment() { 16 | var mainViewModel: MainViewModel? = null 17 | get() { 18 | if (field == null) { 19 | field = ViewModelProvider(requireActivity()).get(MainViewModel::class.java) 20 | } 21 | return field 22 | } 23 | private set 24 | 25 | var bottomBarView: BottomBarView? = null 26 | get() { 27 | if (field == null) field = (requireActivity() as MainActivity).bottomBarView 28 | return field 29 | } 30 | private set 31 | 32 | var toolbar: MaterialToolbar? = null 33 | get() { 34 | if (field == null) field = (requireActivity() as MainActivity).toolbar 35 | return field 36 | } 37 | private set 38 | 39 | private var tabView: TabView.Tab? = null 40 | 41 | var dataHolder: BaseDataHolder? = null 42 | get() { 43 | if (field == null && mainViewModel!!.getDataHolder(tag!!).also { field = it } == null) { 44 | field = createNewDataHolder() 45 | mainViewModel!!.addDataHolder(field!!) 46 | } 47 | return field 48 | } 49 | private set 50 | 51 | abstract fun onBackPressed(): Boolean 52 | abstract fun createNewDataHolder(): BaseDataHolder 53 | 54 | fun getTabView(): TabView.Tab? { 55 | if (tabView == null && (requireActivity() as MainActivity).tabView.getTabByTag(tag) 56 | .also { tabView = it } == null 57 | ) { 58 | tabView = (requireActivity() as MainActivity).tabView.addNewTab(tag!!) 59 | // set Default name 60 | tabView!!.setName("Untitled") 61 | } 62 | return tabView 63 | } 64 | 65 | open fun closeTab() { 66 | mainViewModel!!.getDataHolders() 67 | .removeIf { dataHolder1: BaseDataHolder -> dataHolder1.tag == tag } 68 | (requireActivity() as MainActivity).closeTab(tag!!) 69 | } 70 | 71 | override fun onResume() { 72 | super.onResume() 73 | // create TabView if necessary (important when a tab fragment doesn't update its TabView) 74 | getTabView() 75 | } 76 | 77 | companion object { 78 | const val DEFAULT_TAB_FRAGMENT_PREFIX = "0_FileExplorerTabFragment_" 79 | const val FILE_EXPLORER_TAB_FRAGMENT_PREFIX = "FileExplorerTabFragment_" 80 | const val APPS_TAB_FRAGMENT_PREFIX = "AppsTabFragment_" 81 | const val ARCHIVE_TAB_FRAGMENT_PREFIX = "ArchiveTabFragment_" 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/apps/AppsTabDataHolder.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.apps 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.raival.fileexplorer.tab.BaseDataHolder 6 | import com.raival.fileexplorer.tab.apps.model.Apk 7 | import com.raival.fileexplorer.tab.apps.resolver.ApkResolver 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import kotlinx.coroutines.withContext 12 | 13 | class AppsTabDataHolder(override val tag: String) : BaseDataHolder() { 14 | private val apps = MutableLiveData>() 15 | 16 | fun getApps(showSystemApps: Boolean, sortNewerFirst: Boolean): LiveData> { 17 | if (apps.value == null) { 18 | loadApps(showSystemApps, sortNewerFirst) 19 | } 20 | return apps 21 | } 22 | 23 | fun updateAppsList(showSystemApps: Boolean, sortNewerFirst: Boolean) { 24 | loadApps(showSystemApps, sortNewerFirst) 25 | } 26 | 27 | private fun loadApps(showSystemApps: Boolean, sortNewerFirst: Boolean) { 28 | CoroutineScope(Dispatchers.IO).launch { 29 | val apks = ApkResolver().load(showSystemApps, sortNewerFirst).get() 30 | withContext(Dispatchers.Main) { apps.setValue(apks) } 31 | }.start() 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/apps/model/Apk.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.apps.model 2 | 3 | import android.graphics.drawable.Drawable 4 | import java.io.File 5 | 6 | class Apk( 7 | val name: String, 8 | val pkg: String, 9 | val size: String, 10 | val icon: Drawable, 11 | val lastModified: Long, 12 | val source: File 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/apps/resolver/ApkResolver.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.apps.resolver 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.pm.ApplicationInfo 5 | import android.content.pm.PackageInfo 6 | import android.content.pm.PackageManager 7 | import com.raival.fileexplorer.App 8 | import com.raival.fileexplorer.extension.toFormattedSize 9 | import com.raival.fileexplorer.tab.apps.model.Apk 10 | import java.io.File 11 | 12 | class ApkResolver { 13 | private val list = ArrayList() 14 | private var sortApps = false 15 | 16 | @SuppressLint("PackageManagerGetSignatures") 17 | fun load(showSystemApps: Boolean, sortNewerFirst: Boolean): ApkResolver { 18 | list.clear() 19 | sortApps = sortNewerFirst 20 | val pm = App.appContext.packageManager 21 | val apps = pm.getInstalledApplications( 22 | PackageManager.MATCH_UNINSTALLED_PACKAGES 23 | or PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS 24 | ) 25 | 26 | var androidInfo: PackageInfo? = null 27 | try { 28 | androidInfo = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES) 29 | } catch (ignored: PackageManager.NameNotFoundException) { 30 | } 31 | for (info in apps) { 32 | if (info.sourceDir == null) { 33 | continue 34 | } 35 | val pkgInfo: PackageInfo? = try { 36 | pm.getPackageInfo(info.packageName, PackageManager.GET_SIGNATURES) 37 | } catch (e: PackageManager.NameNotFoundException) { 38 | null 39 | } 40 | 41 | val isSystemApp = isAppInSystemPartition(info) || isSignedBySystem(pkgInfo, androidInfo) 42 | if (isSystemApp && !showSystemApps) continue 43 | 44 | val apk = Apk( 45 | pm.getApplicationLabel(info).toString(), 46 | info.packageName, 47 | File(info.publicSourceDir).length().toFormattedSize(), 48 | info.loadIcon(pm), 49 | File(info.publicSourceDir).lastModified(), 50 | File(info.publicSourceDir) 51 | ) 52 | list.add(apk) 53 | } 54 | return this 55 | } 56 | 57 | fun get(): ArrayList { 58 | if (sortApps) list.sortWith { apk1: Apk, apk2: Apk -> 59 | apk2.lastModified.compareTo(apk1.lastModified) 60 | } 61 | return list 62 | } 63 | 64 | private fun isSignedBySystem(piApp: PackageInfo?, piSys: PackageInfo?): Boolean { 65 | return piApp != null && piSys != null && piApp.signatures != null && piSys.signatures[0] == piApp.signatures[0] 66 | } 67 | 68 | companion object { 69 | fun isAppInSystemPartition(applicationInfo: ApplicationInfo): Boolean { 70 | return ((applicationInfo.flags 71 | and (ApplicationInfo.FLAG_SYSTEM or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) 72 | != 0) 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/FileExplorerTabDataHolder.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file 2 | 3 | import android.os.Parcelable 4 | import com.raival.fileexplorer.tab.BaseDataHolder 5 | import com.raival.fileexplorer.tab.file.model.FileItem 6 | import java.io.File 7 | 8 | class FileExplorerTabDataHolder(override val tag: String) : BaseDataHolder() { 9 | @JvmField 10 | var activeDirectory: File? = null 11 | 12 | @JvmField 13 | var recyclerViewStates: HashMap = HashMap() 14 | var searchList = ArrayList() 15 | 16 | @JvmField 17 | var selectedFiles = ArrayList() 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/adapter/PathHistoryAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.adapter 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.raival.fileexplorer.R 10 | import com.raival.fileexplorer.extension.isExternalStorageFolder 11 | import com.raival.fileexplorer.tab.file.FileExplorerTabFragment 12 | import com.raival.fileexplorer.tab.file.dialog.FileInfoDialog 13 | import com.raival.fileexplorer.tab.file.misc.FileUtils 14 | import com.raival.fileexplorer.util.Utils 15 | 16 | class PathHistoryAdapter(private val parentFragment: FileExplorerTabFragment) : 17 | RecyclerView.Adapter() { 18 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 19 | @SuppressLint("InflateParams") val v = 20 | LayoutInflater.from(parent.context).inflate( 21 | R.layout.file_explorer_tab_path_history_view, 22 | null 23 | ) 24 | v.layoutParams = ViewGroup.LayoutParams( 25 | ViewGroup.LayoutParams.WRAP_CONTENT, 26 | ViewGroup.LayoutParams.MATCH_PARENT 27 | ) 28 | return ViewHolder(v) 29 | } 30 | 31 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 32 | holder.bind() 33 | } 34 | 35 | override fun getItemCount(): Int { 36 | return parentFragment.pathHistory.size 37 | } 38 | 39 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 40 | var label: TextView 41 | fun bind() { 42 | val position = adapterPosition 43 | val file = parentFragment.pathHistory[position] 44 | label.text = 45 | if (file.isExternalStorageFolder()) FileUtils.INTERNAL_STORAGE else file.name 46 | label.setTextColor( 47 | if (position == itemCount - 1) Utils.getColorAttribute( 48 | R.attr.colorPrimary, 49 | parentFragment.requireActivity() 50 | ) else Utils.getColorAttribute( 51 | R.attr.colorOutline, 52 | parentFragment.requireActivity() 53 | ) 54 | ) 55 | itemView.setOnClickListener { 56 | parentFragment.setCurrentDirectory(file) 57 | // Restore RecyclerView state 58 | parentFragment.restoreRecyclerViewState() 59 | } 60 | itemView.setOnLongClickListener { 61 | FileInfoDialog(file).setUseDefaultFileInfo(true).show( 62 | parentFragment.parentFragmentManager, "" 63 | ) 64 | true 65 | } 66 | } 67 | 68 | init { 69 | label = itemView.findViewById(R.id.text) 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/APKSignerUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc 2 | 3 | import com.raival.fileexplorer.App 4 | import com.raival.fileexplorer.tab.file.misc.BuildUtils.unzipFromAssets 5 | import java.io.File 6 | 7 | object APKSignerUtils { 8 | val pk8: File 9 | get() { 10 | val check = File(App.appContext.filesDir.toString() + "/build/testkey.pk8") 11 | if (check.exists()) { 12 | return check 13 | } 14 | unzipFromAssets( 15 | App.appContext, 16 | "build/testkey.pk8.zip", 17 | check.parentFile?.absolutePath 18 | ) 19 | return check 20 | } 21 | val pem: File 22 | get() { 23 | val check = File(App.appContext.filesDir.toString() + "/build/testkey.x509.pem") 24 | if (check.exists()) { 25 | return check 26 | } 27 | unzipFromAssets( 28 | App.appContext, 29 | "build/testkey.x509.pem.zip", 30 | check.parentFile?.absolutePath 31 | ) 32 | return check 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/BuildUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import com.raival.fileexplorer.App 6 | import java.io.File 7 | import java.io.FileOutputStream 8 | import java.io.IOException 9 | import java.io.InputStream 10 | import java.util.zip.ZipEntry 11 | import java.util.zip.ZipInputStream 12 | 13 | object BuildUtils { 14 | private const val BUFFER_SIZE = 1024 * 10 15 | private const val TAG = "Decompress" 16 | val lambdaStubsJarFile: File 17 | get() { 18 | val check = File(App.appContext.filesDir.toString() + "/build/core-lambda-stubs.jar") 19 | if (check.exists()) { 20 | return check 21 | } 22 | unzipFromAssets( 23 | App.appContext, 24 | "build/lambda-stubs.zip", 25 | check.parentFile?.absolutePath 26 | ) 27 | return check 28 | } 29 | val rtJarFile: File 30 | get() { 31 | val customRt = File(App.appContext.getExternalFilesDir(null), "build/rt.jar") 32 | if (customRt.exists() && customRt.isFile) { 33 | return customRt 34 | } 35 | val check = File(App.appContext.filesDir.toString() + "/build/rt.jar") 36 | if (check.exists()) { 37 | return check 38 | } 39 | unzipFromAssets( 40 | App.appContext, 41 | "build/rt.zip", 42 | check.parentFile?.absolutePath 43 | ) 44 | return check 45 | } 46 | 47 | @JvmStatic 48 | fun unzipFromAssets(context: Context, zipFile: String, destination: String?) { 49 | var des = destination 50 | try { 51 | if (des == null || des.isEmpty()) des = 52 | context.filesDir.absolutePath 53 | val stream = context.assets.open(zipFile) 54 | unzip(stream, des) 55 | } catch (e: IOException) { 56 | e.printStackTrace() 57 | } 58 | } 59 | 60 | private fun dirChecker(destination: String?, dir: String) { 61 | val f = File(destination, dir) 62 | if (!f.isDirectory) { 63 | val success = f.mkdirs() 64 | if (!success) { 65 | Log.w(TAG, "Failed to create folder " + f.name) 66 | } 67 | } 68 | } 69 | 70 | private fun unzip(stream: InputStream, destination: String?) { 71 | dirChecker(destination, "") 72 | val buffer = ByteArray(BUFFER_SIZE) 73 | try { 74 | val zin = ZipInputStream(stream) 75 | var ze: ZipEntry 76 | while (zin.nextEntry.also { ze = it } != null) { 77 | Log.v(TAG, "Unzipping " + ze.name) 78 | if (ze.isDirectory) { 79 | dirChecker(destination, ze.name) 80 | } else { 81 | val f = File(destination, ze.name) 82 | if (!f.exists()) { 83 | val success = f.createNewFile() 84 | if (!success) { 85 | Log.w( 86 | TAG, 87 | com.raival.fileexplorer.util.Log.UNABLE_TO + " " + FileUtils.CREATE_FILE + " " + f.name 88 | ) 89 | continue 90 | } 91 | val fileOutputStream = FileOutputStream(f) 92 | var count: Int 93 | while (zin.read(buffer).also { count = it } != -1) { 94 | fileOutputStream.write(buffer, 0, count) 95 | } 96 | zin.closeEntry() 97 | fileOutputStream.close() 98 | } 99 | } 100 | } 101 | zin.close() 102 | } catch (e: Exception) { 103 | Log.e(TAG, "unzip", e) 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/FileOpener.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc 2 | 3 | import android.content.Intent 4 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 5 | import com.raival.fileexplorer.activity.MainActivity 6 | import com.raival.fileexplorer.activity.TextEditorActivity 7 | import com.raival.fileexplorer.extension.openFileWith 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | import java.io.File 12 | 13 | class FileOpener(private val mainActivity: MainActivity) { 14 | fun openFile(file: File) { 15 | if (!handleKnownFileExtensions(file)) { 16 | file.openFileWith(false) 17 | } 18 | } 19 | 20 | private fun handleKnownFileExtensions(file: File): Boolean { 21 | if (FileMimeTypes.textType.contains(file.extension.lowercase()) 22 | || FileMimeTypes.codeType.contains(file.extension.lowercase()) 23 | ) { 24 | val intent = Intent() 25 | intent.setClass(mainActivity, TextEditorActivity::class.java) 26 | intent.putExtra("file", file.absolutePath) 27 | mainActivity.startActivity(intent) 28 | return true 29 | } 30 | if (file.extension == FileMimeTypes.apkType) { 31 | val dialog = MaterialAlertDialogBuilder(mainActivity) 32 | .setMessage("Do you want to install this app?") 33 | .setPositiveButton("Install") { _, _ -> file.openFileWith(false) } 34 | CoroutineScope(Dispatchers.Main).launch { 35 | dialog.setTitle(FileUtils.getApkName(file)) 36 | dialog.setIcon(FileUtils.getApkIcon(file)) 37 | dialog.show() 38 | } 39 | return true 40 | } 41 | return false 42 | } 43 | 44 | 45 | companion object { 46 | private val TAG = FileOpener::class.java.simpleName 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/ZipUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc 2 | 3 | import net.lingala.zip4j.ZipFile 4 | import java.io.File 5 | 6 | fun archive(filesToCompress: ArrayList, zipFile: File?) { 7 | val zip = ZipFile(zipFile) 8 | for (file in filesToCompress) { 9 | if (file.isFile) { 10 | zip.addFile(file) 11 | } else { 12 | zip.addFolder(file) 13 | } 14 | } 15 | } 16 | 17 | fun extract(filesToExtract: ArrayList, directory: File) { 18 | for (file in filesToExtract) { 19 | if (file.isFile) { 20 | val output = File(directory, file.name.substring(0, file.name.lastIndexOf("."))) 21 | if (output.mkdir()) { 22 | ZipFile(file).extractAll(output.absolutePath) 23 | } else { 24 | ZipFile(file).extractAll(directory.absolutePath) 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/md5/HashUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc.md5 2 | 3 | import java.io.File 4 | import java.io.FileInputStream 5 | import java.io.InputStream 6 | import java.security.MessageDigest 7 | 8 | object HashUtils { 9 | 10 | private const val STREAM_BUFFER_LENGTH = 1024 11 | 12 | fun getCheckSumFromFile(digest: MessageDigest, filePath: String): String { 13 | val file = File(filePath) 14 | return getCheckSumFromFile(digest, file) 15 | } 16 | 17 | fun getCheckSumFromFile(digest: MessageDigest, file: File): String { 18 | val fis = FileInputStream(file) 19 | val byteArray = updateDigest(digest, fis).digest() 20 | fis.close() 21 | val hexCode = StringUtils.encodeHex(byteArray, true) 22 | return String(hexCode) 23 | } 24 | 25 | private fun updateDigest(digest: MessageDigest, data: InputStream): MessageDigest { 26 | val buffer = ByteArray(STREAM_BUFFER_LENGTH) 27 | var read = data.read(buffer, 0, STREAM_BUFFER_LENGTH) 28 | while (read > -1) { 29 | digest.update(buffer, 0, read) 30 | read = data.read(buffer, 0, STREAM_BUFFER_LENGTH) 31 | } 32 | return digest 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/md5/MessageDigestAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc.md5 2 | 3 | object MessageDigestAlgorithm { 4 | const val MD2 = "MD2" 5 | const val MD5 = "MD5" 6 | const val SHA_1 = "SHA-1" 7 | const val SHA_224 = "SHA-224" 8 | const val SHA_256 = "SHA-256" 9 | const val SHA_384 = "SHA-384" 10 | const val SHA_512 = "SHA-512" 11 | const val SHA_512_224 = "SHA-512/224" 12 | const val SHA_512_256 = "SHA-512/256" 13 | const val SHA3_224 = "SHA3-224" 14 | const val SHA3_256 = "SHA3-256" 15 | const val SHA3_384 = "SHA3-384" 16 | const val SHA3_512 = "SHA3-512" 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/misc/md5/StringUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.misc.md5 2 | 3 | object StringUtils { 4 | 5 | private val DIGITS_LOWER = 6 | charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') 7 | 8 | private val DIGITS_UPPER = 9 | charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F') 10 | 11 | fun encodeHex(data: ByteArray, toLowerCase: Boolean): CharArray { 12 | return encodeHex(data, if (toLowerCase) DIGITS_LOWER else DIGITS_UPPER) 13 | } 14 | 15 | fun encodeHex(data: ByteArray, toDigits: CharArray): CharArray { 16 | val l = data.size 17 | val out = CharArray(l shl 1) 18 | // two characters form the hex value. 19 | var i = 0 20 | var j = 0 21 | while (i < l) { 22 | out[j++] = toDigits[0xF0 and data[i].toInt() ushr 4] 23 | out[j++] = toDigits[0x0F and data[i].toInt()] 24 | i++ 25 | } 26 | return out 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/model/FileItem.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.model 2 | 3 | import java.io.File 4 | 5 | class FileItem(var f: File) { 6 | @JvmField 7 | var isSelected = false 8 | 9 | @JvmField 10 | var file: File = f 11 | var details = "" 12 | var name = "" 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/model/Task.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.model 2 | 3 | import java.io.File 4 | 5 | abstract class Task { 6 | abstract val name: String? 7 | abstract val details: String? 8 | abstract val isValid: Boolean 9 | 10 | abstract fun setActiveDirectory(directory: File) 11 | abstract fun start(onUpdate: OnUpdateListener, onFinish: OnFinishListener) 12 | 13 | interface OnUpdateListener { 14 | fun onUpdate(progress: String) 15 | } 16 | 17 | interface OnFinishListener { 18 | fun onFinish(result: String) 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/observer/FileListObserver.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.observer 2 | 3 | import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver 4 | import com.raival.fileexplorer.tab.file.FileExplorerTabFragment 5 | import com.raival.fileexplorer.tab.file.adapter.FileListAdapter 6 | 7 | class FileListObserver( 8 | private val parentFragment: FileExplorerTabFragment, 9 | private val fileListAdapter: FileListAdapter? 10 | ) : AdapterDataObserver() { 11 | 12 | override fun onChanged() { 13 | super.onChanged() 14 | checkIfEmpty() 15 | } 16 | 17 | private fun checkIfEmpty() { 18 | parentFragment.showPlaceholder(fileListAdapter != null && fileListAdapter.itemCount == 0) 19 | } 20 | 21 | init { 22 | checkIfEmpty() 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/task/CompressTask.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.task 2 | 3 | import com.raival.fileexplorer.tab.file.misc.archive 4 | import com.raival.fileexplorer.tab.file.model.Task 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.withContext 9 | import java.io.File 10 | 11 | class CompressTask(private val filesToCompress: ArrayList) : Task() { 12 | private var zipFile: File? = null 13 | override val name: String 14 | get() = "Compress" 15 | override val details: String 16 | get() { 17 | val sb = StringBuilder() 18 | var first = true 19 | for (file in filesToCompress) { 20 | if (!first) { 21 | sb.append(", ") 22 | } 23 | sb.append(file.name) 24 | first = false 25 | } 26 | return sb.toString() 27 | } 28 | override val isValid: Boolean 29 | get() { 30 | for (file in filesToCompress) { 31 | if (!file.exists()) return false 32 | } 33 | return true 34 | } 35 | 36 | override fun setActiveDirectory(directory: File) { 37 | zipFile = directory 38 | } 39 | 40 | override fun start(onUpdate: OnUpdateListener, onFinish: OnFinishListener) { 41 | CoroutineScope(Dispatchers.IO).launch { 42 | withContext(Dispatchers.Main) { 43 | onUpdate.onUpdate("Compressing....") 44 | } 45 | try { 46 | archive(filesToCompress, zipFile) 47 | withContext(Dispatchers.Main) { 48 | onFinish.onFinish("Files have been compressed successfully") 49 | } 50 | } catch (e: Exception) { 51 | e.printStackTrace() 52 | withContext(Dispatchers.Main) { 53 | onFinish.onFinish(e.toString()) 54 | } 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/task/CopyTask.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.task 2 | 3 | import com.raival.fileexplorer.tab.file.misc.FileUtils 4 | import com.raival.fileexplorer.tab.file.model.Task 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.withContext 9 | import java.io.File 10 | 11 | class CopyTask(private val filesToCopy: ArrayList) : Task() { 12 | private var activeDirectory: File? = null 13 | override val name: String 14 | get() = "Copy" 15 | override val details: String 16 | get() { 17 | val sb = StringBuilder() 18 | var first = true 19 | for (file in filesToCopy) { 20 | if (!first) { 21 | sb.append(", ") 22 | } 23 | sb.append(file.name) 24 | first = false 25 | } 26 | return sb.toString() 27 | } 28 | override val isValid: Boolean 29 | get() { 30 | for (file in filesToCopy) { 31 | if (!file.exists()) return false 32 | } 33 | return true 34 | } 35 | 36 | override fun setActiveDirectory(directory: File) { 37 | activeDirectory = directory 38 | } 39 | 40 | override fun start(onUpdate: OnUpdateListener, onFinish: OnFinishListener) { 41 | CoroutineScope(Dispatchers.IO).launch { 42 | var error = false 43 | try { 44 | var progress = 1 45 | for (file in filesToCopy) { 46 | try { 47 | val finalProgress = progress 48 | withContext(Dispatchers.Main) { 49 | onUpdate.onUpdate( 50 | "[" 51 | + finalProgress 52 | + "/" 53 | + filesToCopy.size 54 | + "]" 55 | + "Copying " 56 | + file.name 57 | ) 58 | } 59 | FileUtils.copy(file, activeDirectory!!, true) 60 | } catch (exception: Exception) { 61 | error = true 62 | } 63 | ++progress 64 | } 65 | val finalError = error 66 | withContext(Dispatchers.Main) { onFinish.onFinish(if (finalError) "An error occurred, some files didn't get copied" else "Files copied successfully") } 67 | } catch (e: Exception) { 68 | e.printStackTrace() 69 | withContext(Dispatchers.Main) { onFinish.onFinish(e.toString()) } 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/task/CutTask.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.task 2 | 3 | import com.raival.fileexplorer.tab.file.misc.FileUtils 4 | import com.raival.fileexplorer.tab.file.model.Task 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.withContext 9 | import java.io.File 10 | 11 | class CutTask(private val filesToCut: ArrayList) : Task() { 12 | private var activeDirectory: File? = null 13 | override val name: String 14 | get() = "Cut" 15 | override val details: String 16 | get() { 17 | val sb = StringBuilder() 18 | var first = true 19 | for (file in filesToCut) { 20 | if (!first) { 21 | sb.append(", ") 22 | } 23 | sb.append(file.name) 24 | first = false 25 | } 26 | return sb.toString() 27 | } 28 | override val isValid: Boolean 29 | get() { 30 | for (file in filesToCut) { 31 | if (!file.exists()) return false 32 | } 33 | return true 34 | } 35 | 36 | override fun setActiveDirectory(directory: File) { 37 | activeDirectory = directory 38 | } 39 | 40 | override fun start(onUpdate: OnUpdateListener, onFinish: OnFinishListener) { 41 | CoroutineScope(Dispatchers.IO).launch { 42 | var error = false 43 | try { 44 | var progress = 1 45 | for (file in filesToCut) { 46 | try { 47 | val finalProgress = progress 48 | withContext(Dispatchers.Main) { 49 | onUpdate.onUpdate( 50 | "[" 51 | + finalProgress 52 | + "/" 53 | + filesToCut.size 54 | + "]" 55 | + "Moving " 56 | + file.name 57 | ) 58 | } 59 | FileUtils.move(file, activeDirectory) 60 | } catch (exception: Exception) { 61 | error = true 62 | } 63 | ++progress 64 | } 65 | val finalError = error 66 | withContext(Dispatchers.Main) { onFinish.onFinish(if (finalError) "An error occurred, some files haven't been moved" else "Files have been moved successfully") } 67 | } catch (e: Exception) { 68 | e.printStackTrace() 69 | withContext(Dispatchers.Main) { onFinish.onFinish(e.toString()) } 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/tab/file/task/ExtractTask.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.tab.file.task 2 | 3 | import android.os.Handler 4 | import android.os.Looper 5 | import com.raival.fileexplorer.tab.file.misc.extract 6 | import com.raival.fileexplorer.tab.file.model.Task 7 | import kotlinx.coroutines.CoroutineScope 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.launch 10 | import kotlinx.coroutines.withContext 11 | import java.io.File 12 | 13 | class ExtractTask(private val filesToExtract: ArrayList) : Task() { 14 | private var activeDirectory: File? = null 15 | override val name: String 16 | get() = "Extract" 17 | override val details: String 18 | get() { 19 | val sb = StringBuilder() 20 | var first = true 21 | for (file in filesToExtract) { 22 | if (!first) { 23 | sb.append(", ") 24 | } 25 | sb.append(file.name) 26 | first = false 27 | } 28 | return sb.toString() 29 | } 30 | override val isValid: Boolean 31 | get() { 32 | for (file in filesToExtract) { 33 | if (!file.exists()) return false 34 | } 35 | return true 36 | } 37 | 38 | override fun setActiveDirectory(directory: File) { 39 | activeDirectory = directory 40 | } 41 | 42 | override fun start(onUpdate: OnUpdateListener, onFinish: OnFinishListener) { 43 | CoroutineScope(Dispatchers.IO).launch { 44 | Handler(Looper.getMainLooper()).post { onUpdate.onUpdate("Extracting....") } 45 | try { 46 | extract(filesToExtract, activeDirectory!!) 47 | withContext(Dispatchers.Main) { onFinish.onFinish("Files have been extracted successfully") } 48 | } catch (e: Exception) { 49 | e.printStackTrace() 50 | withContext(Dispatchers.Main) { onFinish.onFinish(e.toString()) } 51 | } 52 | }.start() 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/util/PrefsUtils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.util 2 | 3 | import com.google.gson.Gson 4 | import com.pixplicity.easyprefs.library.Prefs 5 | import com.raival.fileexplorer.activity.SettingsActivity 6 | import com.raival.fileexplorer.extension.getStringList 7 | 8 | object PrefsUtils { 9 | const val SORT_NAME_A2Z = 1 10 | const val SORT_NAME_Z2A = 2 11 | const val SORT_SIZE_SMALLER = 3 12 | const val SORT_SIZE_BIGGER = 4 13 | const val SORT_DATE_OLDER = 5 14 | const val SORT_DATE_NEWER = 6 15 | 16 | object TextEditor { 17 | var textEditorWordwrap: Boolean 18 | get() = Prefs.getBoolean("text_editor_wordwrap", false) 19 | set(wordwrap) { 20 | Prefs.putBoolean("text_editor_wordwrap", wordwrap) 21 | } 22 | var textEditorShowLineNumber: Boolean 23 | get() = Prefs.getBoolean("text_editor_show_line_number", true) 24 | set(showLineNumber) { 25 | Prefs.putBoolean("text_editor_show_line_number", showLineNumber) 26 | } 27 | var textEditorPinLineNumber: Boolean 28 | get() = Prefs.getBoolean("text_editor_pin_line_number", true) 29 | set(pinLineNumber) { 30 | Prefs.putBoolean("text_editor_pin_line_number", pinLineNumber) 31 | } 32 | var textEditorMagnifier: Boolean 33 | get() = Prefs.getBoolean("text_editor_magnifier", true) 34 | set(magnifier) { 35 | Prefs.putBoolean("text_editor_magnifier", magnifier) 36 | } 37 | var textEditorReadOnly: Boolean 38 | get() = Prefs.getBoolean("text_editor_read_only", false) 39 | set(readOnly) { 40 | Prefs.putBoolean("text_editor_read_only", readOnly) 41 | } 42 | var textEditorAutocomplete: Boolean 43 | get() = Prefs.getBoolean("text_editor_autocomplete", false) 44 | set(autocomplete) { 45 | Prefs.putBoolean("text_editor_autocomplete", autocomplete) 46 | } 47 | val fileExplorerTabBookmarks: ArrayList 48 | get() = Prefs.getString("file_explorer_tab_bookmarks", "[]").getStringList() 49 | } 50 | 51 | object FileExplorerTab { 52 | @JvmStatic 53 | var sortingMethod: Int 54 | get() = Prefs.getInt("sorting_method", SORT_NAME_A2Z) 55 | set(method) { 56 | Prefs.putInt("sorting_method", method) 57 | } 58 | 59 | fun setListFoldersFirst(b: Boolean) { 60 | Prefs.putBoolean("list_folders_first", b) 61 | } 62 | 63 | @JvmStatic 64 | fun listFoldersFirst(): Boolean { 65 | return Prefs.getBoolean("list_folders_first", true) 66 | } 67 | } 68 | 69 | object Settings { 70 | var deepSearchFileSizeLimit: Long 71 | get() = Prefs.getLong( 72 | "settings_deep_search_file_size_limit", 73 | (6 * 1024 * 1024).toLong() 74 | ) 75 | set(limit) { 76 | Prefs.putLong("settings_deep_search_file_size_limit", limit) 77 | } 78 | 79 | @JvmStatic 80 | var themeMode: String 81 | get() = Prefs.getString("settings_theme_mode", SettingsActivity.THEME_MODE_AUTO) 82 | set(themeMode) { 83 | Prefs.putString("settings_theme_mode", themeMode) 84 | } 85 | var logMode: String 86 | get() = Prefs.getString("settings_log_mode", SettingsActivity.LOG_MODE_ERRORS_ONLY) 87 | set(logMode) { 88 | Prefs.putString("settings_log_mode", logMode) 89 | } 90 | var showBottomToolbarLabels: Boolean 91 | get() = Prefs.getBoolean("settings_show_bottom_toolbar_labels", true) 92 | set(showBottomToolbarLabels) { 93 | Prefs.putBoolean("settings_show_bottom_toolbar_labels", showBottomToolbarLabels) 94 | } 95 | } 96 | 97 | object General { 98 | fun setFileExplorerTabBookmarks(list: ArrayList) { 99 | Prefs.putString("file_explorer_tab_bookmarks", Gson().toJson(list)) 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/com/raival/fileexplorer/util/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.raival.fileexplorer.util 2 | 3 | import android.content.Context 4 | import android.content.res.Configuration 5 | import android.util.TypedValue 6 | import androidx.annotation.ColorInt 7 | import com.raival.fileexplorer.App 8 | import com.raival.fileexplorer.activity.SettingsActivity 9 | import com.raival.fileexplorer.util.PrefsUtils.Settings.themeMode 10 | import java.util.* 11 | 12 | object Utils { 13 | private const val ALLOWED_CHARACTERS = "0123456789qwertyuiopasdfghjklzxcvbnm_" 14 | 15 | @ColorInt 16 | fun getColorAttribute(id: Int, context: Context): Int { 17 | val out = TypedValue() 18 | context.theme.resolveAttribute(id, out, true) 19 | return out.data 20 | } 21 | 22 | val isDarkMode: Boolean 23 | get() = when (themeMode) { 24 | SettingsActivity.THEME_MODE_DARK -> { 25 | true 26 | } 27 | SettingsActivity.THEME_MODE_LIGHT -> { 28 | false 29 | } 30 | else -> { 31 | (App.appContext.resources.configuration.uiMode 32 | and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES 33 | } 34 | } 35 | 36 | fun getRandomString(sizeOfRandomString: Int): String { 37 | val random = Random() 38 | val sb = StringBuilder(sizeOfRandomString) 39 | for (i in 0 until sizeOfRandomString) { 40 | sb.append(ALLOWED_CHARACTERS[random.nextInt(ALLOWED_CHARACTERS.length)]) 41 | } 42 | return sb.toString() 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/apk_placeholder.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable-v24/apk_placeholder.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_icon_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/archive_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/archive_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/code_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/code_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/doc_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/doc_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/fastscroll_thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/font_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/font_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_add_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_assignment_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_bookmark_add_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_bookmark_remove_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_bug_report_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_chevron_right_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_delete_sweep_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_file_copy_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_folder_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_folder_open_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_info_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_layers_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_logout_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_more_vert_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_open_in_browser_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_open_in_new_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_restart_alt_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_save_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_select_all_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_sort_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_code_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_compress_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_content_cut_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_delete_forever_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_edit_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_edit_note_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_exit_to_app_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_home_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_insert_drive_file_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_key_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_manage_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_menu_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_redo_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_search_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_settings_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_share_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_tab_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_timelapse_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_undo_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/image_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/image_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/java_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/java_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/kt_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/kt_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/music_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/music_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pdf_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/pdf_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/powerpoint_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/powerpoint_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/sql_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/sql_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/svg_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/svg_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/txt_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/txt_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/unknown_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/unknown_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/vector_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/vector_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/video_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/video_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/xls_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/xls_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/xml_file_extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/drawable/xml_file_extension.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 23 | 24 | 30 | 31 | 36 | 37 | 45 | 46 | 50 | 51 | 52 | 53 | 58 | 59 | 65 | 66 | 70 | 71 | 72 | 73 | 74 | 77 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main_drawer_bookmark_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 26 | 27 | 32 | 33 | 40 | 41 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/apps_tab_app_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 22 | 23 | 30 | 31 | 32 | 33 | 38 | 39 | 47 | 48 | 55 | 56 | 63 | 64 | 65 | 66 | 72 | -------------------------------------------------------------------------------- /app/src/main/res/layout/apps_tab_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bottom_bar_menu_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_custom_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 20 | 21 | 30 | 31 | 32 | 36 | 37 | 41 | 42 | 52 | 53 | 60 | 61 | 62 | 63 | 64 | 70 | 71 | 77 | 78 | 82 | 83 | 90 | 91 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_options_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 31 | 32 | 37 | 38 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/common_options_dialog_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 23 | 24 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_file_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 16 | 17 | 23 | 24 | 31 | 32 | 33 | 34 | 39 | 40 | 48 | 49 | 56 | 57 | 58 | 59 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 44 | 45 | 58 | 59 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_info_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 21 | 22 | 30 | 31 | 32 | 37 | 38 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_info_dialog_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_path_history_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_placeholder.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_task_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | 27 | 28 | 32 | 33 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/file_explorer_tab_task_dialog_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 23 | 24 | 29 | 30 | 31 | 40 | 41 | 50 | 51 | 60 | 61 | 62 | 63 | 64 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/input.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/progress_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/search_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 19 | 20 | 21 | 25 | 26 | 32 | 33 | 40 | 41 | 42 | 46 | 47 | 53 | 54 | 60 | 61 | 67 | 68 | 69 | 75 | 76 | 81 | 82 | 88 | 89 | 93 | -------------------------------------------------------------------------------- /app/src/main/res/layout/text_editor_completion_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 24 | 25 | 31 | 32 | 38 | 39 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/menu/tab_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/text_editor_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 12 | 16 | 17 | 20 | 21 | 22 | 26 | 29 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 44 | 49 | 53 | 58 | 63 | 68 | 69 | 70 | 71 | 75 | 80 | 81 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/app_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/app_icon_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-hdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/app_icon_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-hdpi/app_icon_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-mdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/app_icon_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-mdpi/app_icon_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xhdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/app_icon_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xhdpi/app_icon_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xxhdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/app_icon_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xxhdpi/app_icon_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xxxhdpi/app_icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/app_icon_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/app/src/main/res/mipmap-xxxhdpi/app_icon_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2F0066FF 4 | #0FFDFDFD 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/app_icon_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #262A30 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2F0066FF 4 | #0A000000 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #1B2221 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | File Explorer 3 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | 15 | 20 | 21 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | -------------------------------------------------------------------------------- /assets/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/assets/screenshot1.png -------------------------------------------------------------------------------- /assets/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/assets/screenshot2.png -------------------------------------------------------------------------------- /assets/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/assets/screenshot3.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = '1.7.20-RC' 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.2.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | task clean(type: Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | File Explorer is a full-featured and lightweight file manager with Material 3 Dynamic colors. 2 | 3 |
Features: 4 | 5 | - All basic file management functionality (e.g. copy, paste,.. etc) are supported. 6 | - Support for multiple tabs, and Tasks which make managing files much easier. 7 | - Powerful Code Editor (Sora Editor). 8 | - Deep search that allows you to search in files contents. 9 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/fastlane/metadata/android/en-US/images/phoneScreenshots/img1.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/fastlane/metadata/android/en-US/images/phoneScreenshots/img2.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/fastlane/metadata/android/en-US/images/phoneScreenshots/img3.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | a full-featured file manager for android 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jul 14 16:17:27 UTC 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "File Explorer" 10 | include ':app' 11 | -------------------------------------------------------------------------------- /testkey.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Raival-e/File-Explorer/3ee7490d759ab0fad7e16335c6347d88ae5de5d3/testkey.keystore --------------------------------------------------------------------------------