├── .github └── workflows │ └── android.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_EN.md ├── app ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── videoyou │ │ └── x │ │ ├── GuideActivity.kt │ │ ├── MainActivity.kt │ │ ├── PlayerActivity.kt │ │ ├── player │ │ ├── Media3PlayerUtils.kt │ │ ├── Play.kt │ │ ├── PlayerSliderV2ViewModel.kt │ │ ├── VideoInfo.kt │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ ├── storage │ │ ├── AndroidMediaStore.kt │ │ ├── MediaStorage.kt │ │ └── Statistics.kt │ │ ├── ui │ │ └── fragment │ │ │ ├── functions │ │ │ ├── FunctionsFragment.kt │ │ │ └── model │ │ │ │ └── OverViewModel.kt │ │ │ ├── guide │ │ │ ├── GuidePermissionFragment.kt │ │ │ ├── GuideWelcomeFragment.kt │ │ │ └── model │ │ │ │ ├── LanguageListModel.kt │ │ │ │ └── PermissionListModel.kt │ │ │ └── home │ │ │ ├── HomeFragment.kt │ │ │ ├── ModalBottomSheet.kt │ │ │ └── model │ │ │ ├── CarouselModel.kt │ │ │ ├── FoldersModel.kt │ │ │ └── VideosModel.kt │ │ └── utils │ │ ├── MediaUtils.kt │ │ ├── System.kt │ │ └── base │ │ ├── BaseActivity.kt │ │ └── BaseFragment.kt │ └── res │ ├── anim │ ├── fade_in.xml │ ├── fade_out.xml │ ├── guide_back_in.xml │ ├── guide_back_out.xml │ ├── guide_next_in.xml │ └── guide_next_out.xml │ ├── drawable │ ├── baseline_center_focus_weak_24.xml │ ├── baseline_data_usage_24.xml │ ├── baseline_folder_24.xml │ ├── baseline_home_24.xml │ ├── baseline_language_24.xml │ ├── baseline_more_vert_24.xml │ ├── baseline_picture_in_picture_alt_24.xml │ ├── baseline_refresh_24.xml │ ├── baseline_search_24.xml │ ├── baseline_settings_24.xml │ ├── color_dark_system_mask.xml │ ├── curved_circle.xml │ ├── ic_launcher_foreground.xml │ ├── icon_foreground.xml │ ├── icon_home.xml │ ├── outline_folder_24.xml │ ├── outline_functions_24.xml │ ├── outline_home_24.xml │ ├── outline_info_24.xml │ ├── outline_layers_24.xml │ ├── outline_list_24.xml │ ├── outline_settings_24.xml │ ├── outline_topic_24.xml │ ├── outline_verified_user_24.xml │ ├── outline_video_file_24.xml │ ├── round_arrow_back_24.xml │ ├── round_close_24.xml │ ├── round_delete_outline_24.xml │ ├── round_done_24.xml │ ├── round_format_list_bulleted_24.xml │ ├── round_history_24.xml │ ├── round_menu_open_24.xml │ ├── round_navigate_before_24.xml │ ├── round_navigate_next_24.xml │ ├── round_pause_24.xml │ ├── round_picture_in_picture_alt_24.xml │ ├── round_play_arrow_24.xml │ ├── round_playlist_play_24.xml │ ├── round_question_mark_24.xml │ ├── round_sort_by_alpha_24.xml │ ├── round_view_in_ar_24.xml │ ├── strange_shape.xml │ └── system_light_ripple.xml │ ├── layout │ ├── activity_guide.xml │ ├── activity_main.xml │ ├── activity_player.xml │ ├── folders_sheet.xml │ ├── fragment_guide_permission.xml │ ├── fragment_guide_welcome.xml │ ├── fragment_main_functions.xml │ ├── fragment_main_home.xml │ ├── fragment_main_list.xml │ ├── fragment_main_search.xml │ ├── fragment_main_settings.xml │ ├── item_functions_overview.xml │ ├── item_language_list.xml │ ├── item_main_carousel.xml │ ├── item_main_folders.xml │ ├── item_permission_list.xml │ ├── item_videos_item.xml │ └── module_page_footer.xml │ ├── menu │ ├── home_topbar.xml │ └── nav_main.xml │ ├── mipmap-anydpi │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── navigation │ └── guide_navigation.xml │ ├── raw │ ├── bc_welcome_bg.json │ └── bc_welcome_bg_land.json │ ├── values-de │ └── strings.xml │ ├── values-en │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-night │ ├── colors.xml │ └── themes.xml │ ├── values-ru │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ ├── data_extraction_rules.xml │ └── locales_config.xml ├── build.gradle.kts ├── crowdin.yml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── imgs └── ic_launcher_round.webp ├── renovate.json └── settings.gradle.kts /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Vyx CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | paths-ignore: 7 | - '*.md' 8 | - 'LICENSE' 9 | - 'renovate.json' 10 | - 'crowdin.yml' 11 | - 'imgs/**' 12 | - '.gitignore' 13 | pull_request: 14 | branches: [ "main" ] 15 | paths-ignore: 16 | - '*.md' 17 | - 'LICENSE' 18 | - 'renovate.json' 19 | - 'crowdin.yml' 20 | - 'imgs/**' 21 | - '.gitignore' 22 | workflow_dispatch: 23 | 24 | jobs: 25 | build: 26 | name: Build App 27 | if: contains(github.event.head_commit.message, '[skip_ci]') == false 28 | runs-on: ubuntu-latest 29 | env: 30 | GITHUB_RETENTION_DAYS: 30 31 | 32 | steps: 33 | - name: Checkout Git Repository 34 | uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Set up JDK 17 39 | uses: actions/setup-java@v4 40 | with: 41 | java-version: '17' 42 | distribution: 'corretto' 43 | # cache: 'gradle' 44 | # check-latest: true 45 | 46 | - name: Setup Gradle 47 | uses: gradle/actions/setup-gradle@v4 48 | 49 | - name: Build with Gradle 50 | run: | 51 | echo ${{ secrets.SIGNING_KEY }} | base64 -d > keystore.jks 52 | bash ./gradlew assemble 53 | env: 54 | KEYSTORE_PATH: "../keystore.jks" 55 | KEYSTORE_PASS: ${{ secrets.KEY_STORE_PASSWORD }} 56 | KEY_ALIAS: ${{ secrets.ALIAS }} 57 | KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} 58 | 59 | - name: Find Apks 60 | run: | 61 | echo "RELEASE_APK=$(find 'app/build/outputs/apk/release' -name '*.apk')" >> $GITHUB_ENV 62 | echo "DEBUG_APK=$(find 'app/build/outputs/apk/debug' -name '*.apk')" >> $GITHUB_ENV 63 | 64 | - name: Show Apks SHA256 65 | if: success() 66 | run: | 67 | echo "### Build Success" >> $GITHUB_STEP_SUMMARY 68 | echo "|Artifact|SHA256|" >> $GITHUB_STEP_SUMMARY 69 | echo "|:--------:|:----------|" >> $GITHUB_STEP_SUMMARY 70 | release=($(sha256sum ${{ env.RELEASE_APK }})) 71 | echo "|Release|$release" >> $GITHUB_STEP_SUMMARY 72 | debug=($(sha256sum ${{ env.DEBUG_APK }})) 73 | echo "|Debug|$debug" >> $GITHUB_STEP_SUMMARY 74 | 75 | - name: Upload Release Apk 76 | uses: actions/upload-artifact@v4 77 | with: 78 | name: VideoYouX-Release-${{ github.event.head_commit.id }} 79 | path: ${{ env.RELEASE_APK }} 80 | compression-level: 9 81 | 82 | - name: Upload Debug Apk 83 | uses: actions/upload-artifact@v4 84 | with: 85 | name: VideoYouX-Debug-${{ github.event.head_commit.id }} 86 | path: ${{ env.DEBUG_APK }} 87 | compression-level: 9 88 | 89 | - name: Post to Channel 90 | if: github.ref == 'refs/heads/main' && contains(github.event.head_commit.message, '[skip_post]') == false 91 | env: 92 | CHANNEL_ID: ${{ secrets.TELEGRAM_TO }} 93 | BOT_TOKEN: ${{ secrets.TELEGRAM_TOKEN }} 94 | RELEASE: ${{ env.RELEASE_APK }} 95 | DEBUG: ${{ env.DEBUG_APK }} 96 | COMMIT_MESSAGE: |+ 97 | GitHub New CI\ 98 | 99 | `${{ github.event.head_commit.message }}` 100 | 101 | by `${{ github.event.head_commit.author.name }}` 102 | 103 | See commit detail [here](${{ github.event.head_commit.url }}) 104 | run: | 105 | ESCAPED=`python3 -c 'import json,os,urllib.parse; print(urllib.parse.quote(json.dumps(os.environ["COMMIT_MESSAGE"])))'` 106 | curl -v "https://api.telegram.org/bot${BOT_TOKEN}/sendMediaGroup?chat_id=${CHANNEL_ID}&media=%5B%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Frelease%22%7D%2C%7B%22type%22%3A%22document%22%2C%20%22media%22%3A%22attach%3A%2F%2Fdebug%22%2C%22parse_mode%22%3A%22MarkdownV2%22%2C%22caption%22%3A${ESCAPED}%7D%5D" -F release="@$RELEASE" -F debug="@$DEBUG" 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.aab 2 | *.apk 3 | *.iml 4 | *.cxx 5 | *.log 6 | .gradle/ 7 | local.properties 8 | .idea/ 9 | .vscode/ 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | /app/build 15 | /app/release/* 16 | /app/*/release/* 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | # VideoYouX 6 | ### Vyx 是一款基于 Material You 设计语言的视频播放器。 7 | 8 | 简体中文  |  [English](/README_EN.md) 9 | 10 |
11 | 12 | ## 展望未来 13 | 14 | #### 软件处于内测阶段,遇到任何 Bug 欢迎提 Issues。 15 | 16 | 未来可能实现的功能: 17 | * 通过哔哩哔哩等软件直接分享链接到 Vyx 播放 18 | * 解析 Twitter、哔哩哔哩等平台的视频链接并下载 19 | * And more... 20 | 21 | ## 网络平台 22 | 23 | | 下载渠道 | 社交平台 | 全能社区 | 24 | | :---- | :---- | :---- | 25 | | [Telegram 频道](https://t.me/VyxNotice) |[Telegram 群组](https://t.me/VyxChatting)|[QQ 频道](https://pd.qq.com/s/7w9nfu9d) | 26 | | [Telegram CI 频道](https://t.me/VyxCiBuild) |[QQ 群组](http://qm.qq.com/cgi-bin/qm/qr?k=kuEgTkhx0YOeQVfCHpVCJvJRiZ2zvxlr)| 27 | 28 | ## 为 Vyx 贡献翻译 29 | 30 | 如果您所使用的语言还没有被翻译,或是部分翻译不太准确,我们欢迎您为 Vyx 贡献翻译,帮助我们完成 Vyx 的国际化翻译工作。 31 | 32 | 我们已在 Crowdin 上创建了翻译项目,您可以在[这里](https://crowdin.com/project/videoyoux/invite?h=511ace92d5ca0ccd00a9d6f598c9fc6d1790079)贡献翻译。 33 | 34 | 我们会将您对 Vyx 所做出的贡献记录在关于页面,以感谢您对翻译工作的支持。 35 | 36 | ## LICENSE 37 | Copyright 2023 Clearpole 38 | 39 | Licensed under the Apache License, Version 2.0 (the "License"); 40 | you may not use this file except in compliance with the License. 41 | You may obtain a copy of the License at 42 | 43 | http://www.apache.org/licenses/LICENSE-2.0 44 | 45 | Unless required by applicable law or agreed to in writing, software 46 | distributed under the License is distributed on an "AS IS" BASIS, 47 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 48 | See the License for the specific language governing permissions and 49 | limitations under the License. 50 | 51 | ## Disclaimer 52 | The developer of this application has no association with the content played by the user. 53 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | # VideoYouX 6 | ### Vyx is a Video Player based on the Material You design. 7 | 8 | English  |  [简体中文](/README.md) 9 | 10 |
11 | 12 | ## TODO: 13 | 14 | #### The software is in the internal testing stage, any bugs encountered are welcome to submit Issues. 15 | 16 | Possible future features: 17 | 18 | * Directly share the link to Vyx in order to playback Bilibili videos or other apps 19 | * Parse and download video links from platforms such as Twitter and Bilibili 20 | * And more... 21 | 22 | ## Online Platform 23 | 24 | | Download Channels | Social Platform | All-round community | 25 | |:------------------|:----------------|:--------------------| 26 | | [Telegram Channel](https://t.me/VyxNotice) | [Telegram Group](https://t.me/VyxChatting) | [QQ Channel](https://pd.qq.com/s/7w9nfu9d) | 27 | | [Telegram CI Channel](https://t.me/VyxCiBuild) | [QQ Group](http://qm.qq.com/cgi-bin/qm/qr?k=kuEgTkhx0YOeQVfCHpVCJvJRiZ2zvxlr) | | 28 | 29 | ## Contribute translations for Vyx 30 | 31 | Contributions are welcome! 32 | 33 | You can help translate VideoYouX on [Crowdin](https://crowdin.com/project/videoyoux/invite?h=511ace92d5ca0ccd00a9d6f598c9fc6d1790079). 34 | 35 | We will record your contributions to Vyx on the About page to express our appreciation for your support in the translation work. 36 | 37 | ## LICENSE 38 | 39 | ``` 40 | Copyright 2023 Clearpole 41 | 42 | Licensed under the Apache License, Version 2.0 (the "License"); 43 | you may not use this file except in compliance with the License. 44 | You may obtain a copy of the License at 45 | 46 | http://www.apache.org/licenses/LICENSE-2.0 47 | 48 | Unless required by applicable law or agreed to in writing, software 49 | distributed under the License is distributed on an "AS IS" BASIS, 50 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 51 | See the License for the specific language governing permissions and 52 | limitations under the License. 53 | ``` 54 | 55 | ## Disclaimer 56 | 57 | The developer of this application has no association with the content played by the user. 58 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | import java.util.Properties 4 | 5 | plugins { 6 | alias(libs.plugins.android.application) 7 | alias(libs.plugins.kotlin.android) 8 | alias(libs.plugins.kotlin.compose.compiler) 9 | id("kotlin-kapt") 10 | } 11 | 12 | fun buildInfo(type: String): Any? { 13 | when (type) { 14 | "name" -> { 15 | return "VideoYouX" 16 | } 17 | 18 | "shortCommitId" -> { 19 | return exec("git rev-parse --short HEAD") 20 | } 21 | 22 | "numberOfCommits" -> { 23 | return exec("git rev-list --count HEAD") 24 | } 25 | 26 | "isCanary" -> { 27 | val isCanaryBuild = Properties().getProperty("GITHUB_ACTIONS") ?: System.getenv("GITHUB_ACTIONS") 28 | return isCanaryBuild == "true" 29 | } 30 | 31 | "version" -> { 32 | return "0.1.0" 33 | } 34 | 35 | else -> { 36 | return null 37 | } 38 | } 39 | } 40 | 41 | fun Project.exec(command: String): String = providers.exec { 42 | commandLine(command.split(" ")) 43 | }.standardOutput.asText.get().trim() 44 | 45 | android { 46 | namespace = "com.videoyou.x" 47 | compileSdk = 34 48 | buildToolsVersion = "34.0.0" 49 | 50 | defaultConfig { 51 | applicationId = namespace 52 | minSdk = 26 53 | targetSdk = 34 54 | versionCode = 1 55 | versionName = if (buildInfo("isCanary") == true) { 56 | "${buildInfo("version")}.r${buildInfo("numberOfCommits")}.canary.${buildInfo("shortCommitId")}" 57 | } else { 58 | "${buildInfo("version")}.r${buildInfo("numberOfCommits")}.release" 59 | } 60 | 61 | resourceConfigurations += arrayListOf("en", "zh-rCN", "de", "fr", "ja", "zh-rTW") 62 | 63 | ndk { 64 | //noinspection ChromeOsAbiSupport 65 | abiFilters += "arm64-v8a" 66 | } 67 | vectorDrawables { 68 | useSupportLibrary = true 69 | } 70 | } 71 | 72 | val properties = Properties() 73 | runCatching { properties.load(project.rootProject.file("local.properties").inputStream()) } 74 | val keystorePath = properties.getProperty("KEYSTORE_PATH") ?: System.getenv("KEYSTORE_PATH") 75 | val keystorePwd = properties.getProperty("KEYSTORE_PASS") ?: System.getenv("KEYSTORE_PASS") 76 | val alias = properties.getProperty("KEY_ALIAS") ?: System.getenv("KEY_ALIAS") 77 | val pwd = properties.getProperty("KEY_PASSWORD") ?: System.getenv("KEY_PASSWORD") 78 | if (keystorePath != null) { 79 | signingConfigs { 80 | create("Vyx") { 81 | storeFile = file(keystorePath) 82 | storePassword = keystorePwd 83 | keyAlias = alias 84 | keyPassword = pwd 85 | enableV3Signing = true 86 | } 87 | } 88 | } 89 | 90 | buildTypes { 91 | release { 92 | isMinifyEnabled = true 93 | isShrinkResources = true 94 | proguardFiles( 95 | getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" 96 | ) 97 | if (keystorePath != null) { 98 | signingConfig = signingConfigs.getByName("Vyx") 99 | } 100 | } 101 | debug { 102 | if (keystorePath != null) { 103 | signingConfig = signingConfigs.getByName("Vyx") 104 | } 105 | } 106 | } 107 | 108 | java { 109 | toolchain { 110 | languageVersion = JavaLanguageVersion.of(17) 111 | } 112 | } 113 | 114 | kotlin { 115 | jvmToolchain(17) 116 | compilerOptions { 117 | freeCompilerArgs = listOf( 118 | "-Xno-param-assertions", 119 | "-Xno-call-assertions", 120 | "-Xno-receiver-assertions" 121 | ) 122 | } 123 | } 124 | 125 | buildFeatures { 126 | dataBinding = true 127 | viewBinding = true 128 | compose = true 129 | } 130 | 131 | packaging { 132 | resources { 133 | excludes += "/META-INF/**" 134 | excludes += "/kotlin/**" 135 | excludes += "/*.bin" 136 | excludes += "/*.json" 137 | excludes += "/*.txt" 138 | } 139 | } 140 | 141 | dependenciesInfo { 142 | includeInApk = false 143 | includeInBundle = false 144 | } 145 | 146 | android.applicationVariants.all { 147 | outputs.all { 148 | if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) { 149 | this.outputFileName = if (buildInfo("isCanary") == false) { 150 | buildInfo("name").toString() + "-" + "v" + buildInfo("version") + "-" + "r" + buildInfo( 151 | "numberOfCommits" 152 | ).toString() + "-" + "${buildType.name}.apk" 153 | } else { 154 | buildInfo("name").toString() + "-" + "v" + buildInfo("version") + "-" + "r" + buildInfo( 155 | "numberOfCommits" 156 | ).toString() + "-" + buildInfo("shortCommitId").toString() + "-" + "${buildType.name}.apk" 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | dependencies { 164 | implementation(libs.androidx.media3.session) 165 | implementation(libs.androidx.media3.exoplayer) 166 | implementation(libs.androidx.media3.exoplayer.dash) 167 | implementation(libs.androidx.media3.ui) 168 | 169 | implementation(libs.androidx.activity.compose) 170 | implementation(libs.androidx.compose.animation) 171 | implementation(libs.androidx.compose.material3) 172 | implementation(libs.androidx.compose.ui.viewbinding) 173 | 174 | implementation(libs.androidx.appcompat) 175 | implementation(libs.androidx.appcompat.resources) 176 | implementation(libs.androidx.activity) 177 | implementation(libs.androidx.constraintlayout) 178 | implementation(libs.androidx.recyclerview) 179 | implementation(libs.androidx.lifecycle.livedata.ktx) 180 | implementation(libs.androidx.lifecycle.viewmodel.ktx) 181 | implementation(libs.androidx.navigation.fragment) 182 | 183 | implementation(libs.google.material) 184 | implementation(libs.collapsingtoolbarlayout.subtitle) 185 | implementation(libs.utilcodex) 186 | implementation(libs.serialize) 187 | implementation(libs.brv) 188 | implementation(libs.glide) 189 | implementation(libs.lottie) 190 | implementation(libs.xx.permissions) 191 | implementation(libs.immersionbar) 192 | } 193 | -------------------------------------------------------------------------------- /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 22 | -keep class com.google.android.material.carousel.* -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | 15 | 16 | 28 | 31 | 35 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/GuideActivity.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x 2 | 3 | import com.videoyou.x.utils.base.BaseActivity 4 | import com.videoyou.x.databinding.ActivityGuideBinding 5 | 6 | class GuideActivity : BaseActivity() { 7 | 8 | override fun getLayout(): Int { 9 | return R.layout.activity_guide 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x 2 | 3 | import android.os.Bundle 4 | import androidx.activity.enableEdgeToEdge 5 | import androidx.fragment.app.commit 6 | import com.drake.serialize.intent.openActivity 7 | import com.videoyou.x.storage.AndroidMediaStore 8 | import com.videoyou.x.utils.base.BaseActivity 9 | import com.videoyou.x.databinding.ActivityMainBinding 10 | import com.videoyou.x.ui.fragment.functions.FunctionsFragment 11 | import com.videoyou.x.ui.fragment.home.HomeFragment 12 | import kotlinx.coroutines.CoroutineScope 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.launch 15 | 16 | 17 | class MainActivity : BaseActivity( 18 | ) { 19 | private var firstLoad = false 20 | private val homeFragment = HomeFragment() 21 | private val functionsFragment = FunctionsFragment() 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | enableEdgeToEdge() 24 | super.onCreate(savedInstanceState) 25 | val sharedPreferences = getSharedPreferences("values", MODE_PRIVATE) 26 | val isFirstEnter = sharedPreferences.getBoolean("first-enter", true) 27 | if (isFirstEnter) { 28 | openActivity() 29 | } 30 | CoroutineScope(Dispatchers.IO).launch { 31 | AndroidMediaStore.writeData(this@MainActivity, false) 32 | } 33 | 34 | binding.navView.setOnItemSelectedListener { 35 | when (it.itemId) { 36 | R.id.menu_screen_page1 -> { 37 | supportFragmentManager.commit { 38 | setCustomAnimations(R.anim.fade_in,R.anim.fade_out) 39 | show(homeFragment) 40 | hide(functionsFragment) 41 | } 42 | true 43 | } 44 | 45 | R.id.menu_screen_page2 -> { 46 | supportFragmentManager.commit { 47 | setCustomAnimations(R.anim.fade_in,R.anim.fade_out) 48 | show(functionsFragment) 49 | hide(homeFragment) 50 | } 51 | true 52 | } 53 | 54 | else -> { 55 | true 56 | } 57 | } 58 | } 59 | } 60 | 61 | override fun onWindowFocusChanged(hasFocus: Boolean) { 62 | super.onWindowFocusChanged(hasFocus) 63 | if (!firstLoad) { 64 | supportFragmentManager.commit { 65 | add(R.id.nav_host_fragment, homeFragment) 66 | add(R.id.nav_host_fragment, functionsFragment) 67 | hide(functionsFragment) 68 | } 69 | firstLoad = true 70 | } 71 | } 72 | 73 | override fun getLayout(): Int { 74 | return R.layout.activity_main 75 | } 76 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/Media3PlayerUtils.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player 2 | 3 | import androidx.media3.exoplayer.ExoPlayer 4 | 5 | class Media3PlayerUtils { 6 | companion object{ 7 | var exoPlayer : ExoPlayer? = null 8 | fun getIfExoExist():Boolean{ 9 | return exoPlayer != null 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/Play.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player 2 | 3 | import androidx.media3.common.MediaItem 4 | 5 | object Play { 6 | var list = arrayListOf() 7 | var position = 0 8 | var path = arrayListOf() 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/PlayerSliderV2ViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player 2 | 3 | import androidx.compose.runtime.mutableStateOf 4 | import androidx.lifecycle.ViewModel 5 | 6 | class PlayerSliderV2ViewModel : ViewModel() { 7 | val nowPosition = mutableStateOf(0f) 8 | val maxPosition = mutableStateOf(0f) 9 | val valueChanging = mutableStateOf(false) 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/VideoInfo.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player 2 | 3 | import android.content.Context 4 | import android.media.MediaMetadataRetriever 5 | import android.net.Uri 6 | 7 | class VideoInfo { 8 | companion object { 9 | fun get(context: Context, uri: Uri, list: ArrayList): ArrayList { 10 | //list: MediaMetadataRetriever.METADATA_KEY_xxx 11 | val resultList = arrayListOf() 12 | val retriever = MediaMetadataRetriever() 13 | retriever.setDataSource(context, uri) 14 | for (type in list) { 15 | resultList.add( 16 | retriever.extractMetadata(type) 17 | .toString() 18 | ) 19 | } 20 | retriever.release() 21 | return resultList 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.platform.LocalContext 14 | import androidx.compose.ui.platform.LocalView 15 | import com.gyf.immersionbar.BarHide 16 | import com.gyf.immersionbar.ImmersionBar 17 | import com.videoyou.x.utils.System 18 | 19 | private val darkColorScheme = darkColorScheme( 20 | primary = Purple80, 21 | secondary = PurpleGrey80, 22 | tertiary = Pink80 23 | ) 24 | 25 | private val lightColorScheme = lightColorScheme( 26 | primary = Purple40, 27 | secondary = PurpleGrey40, 28 | tertiary = Pink40 29 | ) 30 | 31 | @Composable 32 | fun VideoYouXTheme( 33 | darkTheme: Boolean = isSystemInDarkTheme(), 34 | hideBar: Boolean, 35 | darkBar: Boolean = false, 36 | dynamicColor: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S, 37 | content: @Composable () -> Unit 38 | ) { 39 | val colorScheme = when { 40 | dynamicColor -> { 41 | val context = LocalContext.current 42 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 43 | } 44 | darkTheme -> darkColorScheme 45 | else -> lightColorScheme 46 | } 47 | val view = LocalView.current 48 | if (!view.isInEditMode) { 49 | SideEffect { 50 | if (hideBar) { 51 | ImmersionBar.with(view.context as Activity).hideBar(BarHide.FLAG_HIDE_BAR).init() 52 | } else { 53 | if (!darkBar) { 54 | ImmersionBar.with(view.context as Activity) 55 | .statusBarDarkFont(!System.isNightMode(view.resources)).transparentBar() 56 | .init() 57 | } else { 58 | ImmersionBar.with(view.context as Activity) 59 | .statusBarDarkFont(false).transparentBar() 60 | .init() 61 | } 62 | } 63 | } 64 | } 65 | 66 | MaterialTheme( 67 | colorScheme = colorScheme, 68 | typography = Typography, 69 | content = content 70 | ) 71 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/player/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.player.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/storage/AndroidMediaStore.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.storage 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.provider.MediaStore 6 | import android.widget.Toast 7 | import com.bumptech.glide.Glide 8 | import com.tencent.mmkv.MMKV 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.withContext 11 | import kotlin.system.measureNanoTime 12 | 13 | 14 | object AndroidMediaStore { 15 | private val kv_video = MMKV.mmkvWithID("vyx-videos", MMKV.SINGLE_PROCESS_MODE)!! 16 | private val kv_folder_time = MMKV.mmkvWithID("vyx-folders", MMKV.SINGLE_PROCESS_MODE)!! 17 | private val kv_folder_video = MMKV.mmkvWithID("vyx-folders-videos", MMKV.SINGLE_PROCESS_MODE)!! 18 | fun readVideosData(): MMKV = kv_video 19 | fun readFoldersData(): MMKV = kv_folder_time 20 | fun readFoldersVideosData():MMKV = kv_folder_video 21 | suspend fun writeData(context: Context, requireTw: Boolean? = true) { 22 | kv_video.clearAll() 23 | kv_folder_time.clearAll() 24 | kv_folder_video.clearAll() 25 | val contentResolver = context.contentResolver 26 | measureNanoTime { 27 | contentResolver.query( 28 | MediaStore.Video.Media.EXTERNAL_CONTENT_URI, null, null, null, null 29 | )!!.apply { 30 | moveToPosition(-1) 31 | var videoCount = 0 32 | var videoSize = 0L 33 | while (moveToNext()) { 34 | val timeStamp = 35 | getString(getColumnIndexOrThrow(MediaStore.Video.Media.DATE_ADDED)) 36 | val title = getString(getColumnIndexOrThrow(MediaStore.Video.Media.TITLE)) 37 | val size = getString(getColumnIndexOrThrow(MediaStore.Video.Media.SIZE)) 38 | val folder = 39 | getString(getColumnIndexOrThrow(MediaStore.Video.Media.BUCKET_DISPLAY_NAME)) 40 | val path = getString(getColumnIndexOrThrow(MediaStore.Video.Media.DATA)) 41 | val duration = 42 | getString(getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)) 43 | val uri = Uri.withAppendedPath( 44 | MediaStore.Video.Media.EXTERNAL_CONTENT_URI, 45 | getString(getColumnIndexOrThrow(MediaStore.Video.Media._ID)) 46 | ) 47 | val folderPath = path.substring(0, path!!.lastIndexOf("/") + 1) 48 | val content = 49 | "$path\u001A$uri\u001A$timeStamp\u001A$title\u001A$size\u001A$folder\u001A$duration" 50 | kv_folder_time.encode(folderPath, timeStamp) 51 | kv_video.encode( 52 | timeStamp, 53 | content 54 | ) 55 | kv_folder_video.encode(folderPath+"\u001A"+(0..10000000000).random().toString(),content) 56 | videoSize += size.toLong() 57 | videoCount += 1 58 | } 59 | Statistics.clear() 60 | Statistics.writeInfo( 61 | Statistics.FOLDERS_COUNT, 62 | kv_folder_time.allKeys()!!.size.toString() 63 | ) 64 | Statistics.writeInfo(Statistics.VIDEOS_COUNT, videoCount.toString()) 65 | Statistics.writeInfo(Statistics.VIDEOS_SIZE, videoSize.toString()) 66 | Glide.get(context).clearDiskCache() 67 | close() 68 | } 69 | }.also { time -> 70 | if (requireTw!!) { 71 | withContext(Dispatchers.Main) { 72 | Toast.makeText( 73 | context, 74 | "Scanning is completed within ${time / 1_000_000} ms.", 75 | Toast.LENGTH_SHORT 76 | ).show() 77 | } 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/storage/MediaStorage.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.storage 2 | 3 | import com.tencent.mmkv.MMKV 4 | 5 | abstract class MediaStorage { 6 | open val kv_video: MMKV = MMKV.defaultMMKV() 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/storage/Statistics.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.storage 2 | 3 | import com.tencent.mmkv.MMKV 4 | 5 | object Statistics { 6 | const val FOLDERS_COUNT = "folders_count" 7 | const val VIDEOS_COUNT = "videos_count" 8 | const val PLAY_COUNT = "videos_count" 9 | const val VIDEOS_SIZE = "videos_size" 10 | 11 | private val kv_statistics = MMKV.mmkvWithID("vyx-statistics", MMKV.SINGLE_PROCESS_MODE)!! 12 | fun info(): MMKV = kv_statistics 13 | 14 | fun clear() { 15 | kv_statistics.clearAll() 16 | } 17 | 18 | fun writeInfo(type: String, value: String) { 19 | kv_statistics.encode(type, value) 20 | } 21 | 22 | fun readInfo(type: String): String { 23 | return kv_statistics.decodeString(type).let { if (it.isNullOrEmpty()) "0" else it } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/functions/FunctionsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.functions 2 | 3 | import androidx.recyclerview.widget.LinearLayoutManager 4 | import com.drake.brv.utils.linear 5 | import com.drake.brv.utils.setup 6 | import com.videoyou.x.R 7 | import com.videoyou.x.databinding.FragmentMainFunctionsBinding 8 | import com.videoyou.x.storage.Statistics 9 | import com.videoyou.x.ui.fragment.functions.model.OverViewModel 10 | import com.videoyou.x.utils.base.BaseFragment 11 | 12 | class FunctionsFragment : BaseFragment() { 13 | override fun onViewCreate() { 14 | binding.overViewRv.linear().setup { 15 | val layoutManager = LinearLayoutManager(requireContext()) 16 | layoutManager.orientation = LinearLayoutManager.HORIZONTAL 17 | it.layoutManager = layoutManager 18 | addType { R.layout.item_functions_overview } 19 | }.models = mutableListOf().apply { 20 | add(OverViewModel(getString(R.string.videos_count), Statistics.VIDEOS_COUNT)) 21 | add(OverViewModel(getString(R.string.folders_count), Statistics.FOLDERS_COUNT)) 22 | add(OverViewModel(getString(R.string.total_size), Statistics.VIDEOS_SIZE)) 23 | } 24 | } 25 | 26 | override fun getViewBinding(): FragmentMainFunctionsBinding { 27 | return FragmentMainFunctionsBinding.inflate(layoutInflater) 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/functions/model/OverViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.functions.model 2 | 3 | import com.drake.brv.BindingAdapter 4 | import com.drake.brv.item.ItemBind 5 | import com.videoyou.x.databinding.ItemFunctionsOverviewBinding 6 | import com.videoyou.x.storage.Statistics 7 | import kotlin.math.roundToInt 8 | 9 | class OverViewModel(private val item: String, private val content: String) : ItemBind { 10 | override fun onBind(vh: BindingAdapter.BindingViewHolder) { 11 | vh.getBinding().apply { 12 | title.text = if (content == Statistics.VIDEOS_SIZE) { 13 | val size = (Statistics.readInfo(content).toFloat() / 1024000F) 14 | if (size >= 1024) { 15 | String.format("%.2f",(size/1024F)) + " GiB" 16 | } else { 17 | String.format("%.2f",size) + " MiB" 18 | } 19 | } else { 20 | Statistics.readInfo(content) 21 | } 22 | subTitle.text = item 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/guide/GuidePermissionFragment.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.guide 2 | 3 | import android.animation.ObjectAnimator 4 | import android.view.View 5 | import android.view.animation.CycleInterpolator 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.appcompat.content.res.AppCompatResources 8 | import androidx.core.os.bundleOf 9 | import androidx.navigation.Navigation 10 | import androidx.navigation.navOptions 11 | import com.drake.brv.utils.linear 12 | import com.drake.brv.utils.setup 13 | import com.google.android.material.shape.ShapeAppearanceModel 14 | import com.hjq.permissions.Permission 15 | import com.hjq.permissions.XXPermissions 16 | import com.videoyou.x.R 17 | import com.videoyou.x.utils.base.BaseFragment 18 | import com.videoyou.x.databinding.FragmentGuidePermissionBinding 19 | import com.videoyou.x.ui.fragment.guide.model.PermissionListModel 20 | 21 | class GuidePermissionFragment : 22 | BaseFragment() { 23 | override fun onViewCreate() { 24 | val controller = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment) 25 | binding.guidePermissionBack.setOnClickListener { 26 | controller.navigate(R.id.guideWelcomeFragment, bundleOf(), navOptions { 27 | anim { 28 | enter = R.anim.guide_back_in 29 | exit = R.anim.guide_back_out 30 | } 31 | }) 32 | } 33 | binding.guidePermissionNext.setOnClickListener { 34 | if (XXPermissions.isGranted( 35 | requireContext(), 36 | arrayListOf(Permission.READ_MEDIA_VIDEO, Permission.PICTURE_IN_PICTURE) 37 | ) 38 | ) { 39 | val sharedPreferences = requireContext().getSharedPreferences("values", 40 | AppCompatActivity.MODE_PRIVATE 41 | ) 42 | sharedPreferences.edit().putBoolean("first-enter",false).apply() 43 | this@GuidePermissionFragment.requireActivity().finish() 44 | } else { 45 | val anim = 46 | ObjectAnimator.ofFloat( 47 | it, 48 | View.TRANSLATION_X.name, 49 | 0f, 50 | 8f 51 | ) 52 | anim.setDuration(300) 53 | anim.interpolator = CycleInterpolator(4f) 54 | anim.start() 55 | } 56 | } 57 | binding.guidePermissionList.linear().setup { 58 | addType { R.layout.item_permission_list } 59 | }.models = mutableListOf().apply { 60 | val topCorner = ShapeAppearanceModel().toBuilder().setTopLeftCornerSize { 65f } 61 | .setTopRightCornerSize { 65f }.setBottomLeftCornerSize { 10f } 62 | .setBottomRightCornerSize { 10f }.build() 63 | val bottomCorner = ShapeAppearanceModel().toBuilder().setTopLeftCornerSize { 10f } 64 | .setTopRightCornerSize { 10f }.setBottomLeftCornerSize { 65f } 65 | .setBottomRightCornerSize { 65f }.build() 66 | val centerCorner = ShapeAppearanceModel().toBuilder().setAllCornerSizes(10f).build() 67 | val right = AppCompatResources.getDrawable(requireContext(), R.drawable.round_done_24) 68 | val unknown = AppCompatResources.getDrawable(requireContext(), R.drawable.round_question_mark_24) 69 | arrayListOf( 70 | getString(R.string.read_video_permission) to getString(R.string.read_video_permission_usage) to R.drawable.outline_topic_24 to Permission.READ_MEDIA_VIDEO, 71 | getString(R.string.pic_in_pic_permission) to getString(R.string.pic_in_pic_permission_usage) to R.drawable.baseline_picture_in_picture_alt_24 to Permission.PICTURE_IN_PICTURE, 72 | getString(R.string.other_permission) to getString(R.string.other_permission_usage) to R.drawable.outline_info_24 to "", 73 | ).onEach { 74 | add( 75 | PermissionListModel( 76 | it.first.first.first, 77 | it.first.first.second, 78 | it.first.second, 79 | it.second, 80 | right!!, 81 | unknown!!, 82 | topCorner, 83 | bottomCorner, 84 | centerCorner 85 | ) 86 | ) 87 | } 88 | } 89 | } 90 | 91 | override fun getViewBinding(): FragmentGuidePermissionBinding { 92 | return FragmentGuidePermissionBinding.inflate(layoutInflater) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/guide/GuideWelcomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.guide 2 | 3 | import android.graphics.Color 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.core.os.bundleOf 7 | import androidx.navigation.Navigation 8 | import androidx.navigation.navOptions 9 | import androidx.transition.Transition.TransitionListener 10 | import androidx.transition.TransitionManager 11 | import com.blankj.utilcode.util.LanguageUtils 12 | import com.drake.brv.utils.linear 13 | import com.drake.brv.utils.setup 14 | import com.google.android.material.transition.MaterialContainerTransform 15 | import com.videoyou.x.R 16 | import com.videoyou.x.utils.base.BaseFragment 17 | import com.videoyou.x.databinding.FragmentGuideWelcomeBinding 18 | import com.videoyou.x.ui.fragment.guide.model.LanguageListModel 19 | import java.util.Locale 20 | import kotlin.properties.Delegates 21 | 22 | 23 | class GuideWelcomeFragment : BaseFragment() { 24 | private var animIsRunning = false 25 | private var startView by Delegates.notNull() 26 | private var endView by Delegates.notNull() 27 | private var rootView by Delegates.notNull() 28 | private var maskView by Delegates.notNull() 29 | private var duration by Delegates.notNull() 30 | private var maskColor by Delegates.notNull() 31 | var choseLocale by Delegates.notNull() 32 | var isChoseLanguage = false 33 | override fun onViewCreate() { 34 | val controller = Navigation.findNavController(requireActivity(), R.id.nav_host_fragment) 35 | 36 | binding.languageTitle.text = if (LanguageUtils.isAppliedLanguage()) { 37 | LanguageUtils.getAppliedLanguage().displayName 38 | } else Locale.getDefault().displayName 39 | 40 | binding.guideExit.setOnClickListener { 41 | if (!animIsRunning) { 42 | requireActivity().finish() 43 | } 44 | } 45 | binding.guideGetStart.setOnClickListener { 46 | if (!animIsRunning) { 47 | controller.navigate(R.id.guidePermissionFragment, bundleOf(), navOptions { 48 | anim { 49 | enter = R.anim.guide_next_in 50 | exit = R.anim.guide_next_out 51 | } 52 | }) 53 | } 54 | } 55 | 56 | languageAnimSet() 57 | 58 | binding.rv.linear().setup { 59 | addType { R.layout.item_language_list } 60 | }.models = mutableListOf().apply { 61 | val tagList = arrayListOf( 62 | LanguageUtils.getSystemLanguage(), 63 | Locale.SIMPLIFIED_CHINESE, 64 | Locale.TAIWAN, 65 | Locale.ENGLISH, 66 | Locale.JAPANESE, 67 | Locale.GERMANY, 68 | Locale.FRANCE, 69 | Locale.forLanguageTag("ru") 70 | ) 71 | tagList.forEachIndexed { _, s -> 72 | add(LanguageListModel(s)) 73 | } 74 | } 75 | } 76 | 77 | private fun languageAnimSet() { 78 | startView = binding.localeChoose 79 | endView = binding.languageRoot 80 | rootView = binding.root 81 | maskView = binding.languageBackground 82 | duration = 650 83 | maskColor = Color.parseColor("#44000000") 84 | binding.localeChooseRoot.setOnClickListener { 85 | materialTransition( 86 | binding.localeChoose, 87 | binding.languageRoot, 88 | binding.root, 89 | binding.languageBackground, 90 | 650, 91 | true, 92 | Color.parseColor("#44000000") 93 | ) 94 | } 95 | binding.languageBackground.setOnClickListener { 96 | materialTransition( 97 | binding.languageRoot, 98 | binding.localeChoose, 99 | binding.root, 100 | binding.languageBackground, 101 | 650, 102 | false, 103 | Color.parseColor("#44000000") 104 | ) 105 | } 106 | binding.cancel.setOnClickListener { 107 | materialTransition( 108 | binding.languageRoot, 109 | binding.localeChoose, 110 | binding.root, 111 | binding.languageBackground, 112 | 650, 113 | false, 114 | Color.parseColor("#44000000") 115 | ) 116 | } 117 | } 118 | 119 | fun materialTransition( 120 | startView: View, 121 | endView: View, 122 | rootView: View, 123 | maskView: View, 124 | duration: Long, 125 | type: Boolean, 126 | maskColor: Int 127 | ) { 128 | if (!animIsRunning) { 129 | animIsRunning = true 130 | MaterialContainerTransform().apply { 131 | fadeMode = MaterialContainerTransform.FADE_MODE_CROSS 132 | scrimColor = maskColor 133 | maskView.setBackgroundColor(maskColor) 134 | this.duration = duration 135 | this.startView = startView 136 | this.endView = endView 137 | addTarget(endView) 138 | addListener(object : TransitionListener { 139 | override fun onTransitionStart(transition: androidx.transition.Transition) { 140 | maskView.visibility = View.GONE 141 | } 142 | 143 | override fun onTransitionEnd(transition: androidx.transition.Transition) { 144 | if (type) { 145 | maskView.visibility = View.VISIBLE 146 | } 147 | if (isChoseLanguage) { 148 | isChoseLanguage = false 149 | LanguageUtils.applyLanguage(choseLocale) 150 | } 151 | animIsRunning = false 152 | } 153 | 154 | override fun onTransitionCancel(transition: androidx.transition.Transition) {} 155 | override fun onTransitionPause(transition: androidx.transition.Transition) {} 156 | override fun onTransitionResume(transition: androidx.transition.Transition) {} 157 | }) 158 | TransitionManager.beginDelayedTransition((rootView as ViewGroup?)!!, this) 159 | endView.visibility = View.VISIBLE 160 | startView.visibility = View.INVISIBLE 161 | } 162 | } 163 | } 164 | 165 | override fun getViewBinding(): FragmentGuideWelcomeBinding { 166 | return FragmentGuideWelcomeBinding.inflate(layoutInflater) 167 | } 168 | 169 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/guide/model/LanguageListModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.guide.model 2 | 3 | import com.drake.brv.BindingAdapter 4 | import com.drake.brv.item.ItemBind 5 | import com.videoyou.x.R 6 | import com.videoyou.x.databinding.ItemLanguageListBinding 7 | import com.videoyou.x.ui.fragment.guide.GuideWelcomeFragment 8 | import kotlinx.coroutines.CoroutineScope 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.cancel 11 | import kotlinx.coroutines.delay 12 | import kotlinx.coroutines.launch 13 | import kotlinx.coroutines.withContext 14 | import java.util.Locale 15 | 16 | class LanguageListModel( 17 | private val locale: Locale 18 | ) : ItemBind { 19 | override fun onBind(holder: BindingAdapter.BindingViewHolder) { 20 | holder.getBinding().apply { 21 | if (holder.layoutPosition == 0) { 22 | title.text = holder.context.getString(R.string.follow_system) 23 | info.text = holder.context.getString( 24 | R.string.follow_system_info, 25 | locale.displayName, 26 | locale.toLanguageTag() 27 | ) 28 | } else { 29 | title.text = locale.displayName 30 | info.text = locale.toLanguageTag() 31 | } 32 | root.setOnClickListener { 33 | 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/guide/model/PermissionListModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.guide.model 2 | 3 | import android.graphics.drawable.Drawable 4 | import androidx.appcompat.content.res.AppCompatResources 5 | import com.drake.brv.BindingAdapter 6 | import com.drake.brv.item.ItemBind 7 | import com.google.android.material.shape.ShapeAppearanceModel 8 | import com.hjq.permissions.OnPermissionCallback 9 | import com.hjq.permissions.XXPermissions 10 | import com.videoyou.x.R 11 | import com.videoyou.x.databinding.ItemPermissionListBinding 12 | 13 | class PermissionListModel( 14 | private val title: String, 15 | private val info: String, 16 | private val resId: Int, 17 | private val permission: String, 18 | private val right: Drawable, 19 | private val unknown: Drawable, 20 | private val topCorner:ShapeAppearanceModel, 21 | private val bottomCorner:ShapeAppearanceModel, 22 | private val centerCorner:ShapeAppearanceModel 23 | ) : ItemBind { 24 | override fun onBind(holder: BindingAdapter.BindingViewHolder) { 25 | val id = holder.layoutPosition 26 | val count = holder.adapter.itemCount 27 | holder.getBinding().apply { 28 | title.text = this@PermissionListModel.title 29 | info.text = this@PermissionListModel.info 30 | icon.setImageDrawable(AppCompatResources.getDrawable(holder.context, resId)) 31 | if (XXPermissions.isGranted(holder.context, permission) || id == count - 1) { 32 | more.setImageDrawable(right) 33 | } else { 34 | more.setImageDrawable(unknown) 35 | } 36 | val corner = when (id) { 37 | 0 -> { 38 | topCorner 39 | } 40 | 41 | count - 1 -> { 42 | more.setImageDrawable(right) 43 | bottomCorner 44 | } 45 | 46 | else -> { 47 | centerCorner 48 | } 49 | } 50 | card.shapeAppearanceModel = corner 51 | if (id != count - 1) { 52 | card.setOnClickListener { 53 | val error = 54 | AppCompatResources.getDrawable(holder.context, R.drawable.round_close_24) 55 | XXPermissions.with(holder.context) 56 | .permission(permission) 57 | .request(object : OnPermissionCallback { 58 | override fun onGranted( 59 | permissions: MutableList, 60 | allGranted: Boolean 61 | ) { 62 | if (allGranted) { 63 | more.setImageDrawable( 64 | right 65 | ) 66 | } else { 67 | more.setImageDrawable(error) 68 | } 69 | } 70 | 71 | override fun onDenied( 72 | permissions: MutableList, 73 | doNotAskAgain: Boolean 74 | ) { 75 | more.setImageDrawable(error) 76 | XXPermissions.startPermissionActivity(holder.context, permission) 77 | } 78 | }) 79 | } 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.home 2 | 3 | import android.os.Environment 4 | import androidx.core.net.toUri 5 | import androidx.media3.common.MediaItem 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.blankj.utilcode.util.TimeUtils.millis2String 8 | import com.bumptech.glide.Glide 9 | import com.bumptech.glide.load.engine.DiskCacheStrategy 10 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions 11 | import com.bumptech.glide.request.RequestOptions 12 | import com.drake.brv.utils.linear 13 | import com.drake.brv.utils.setup 14 | import com.google.android.material.carousel.CarouselLayoutManager 15 | import com.tencent.mmkv.MMKV 16 | import com.videoyou.x.R 17 | import com.videoyou.x.databinding.FragmentMainHomeBinding 18 | import com.videoyou.x.player.Play 19 | import com.videoyou.x.storage.AndroidMediaStore 20 | import com.videoyou.x.ui.fragment.home.model.CarouselModel 21 | import com.videoyou.x.ui.fragment.home.model.FoldersModel 22 | import com.videoyou.x.utils.MediaUtils 23 | import com.videoyou.x.utils.base.BaseFragment 24 | import kotlinx.coroutines.CoroutineScope 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.launch 27 | import kotlinx.coroutines.withContext 28 | 29 | class HomeFragment : BaseFragment() { 30 | 31 | override fun onViewCreate() { 32 | binding.toolbar.setOnMenuItemClickListener { 33 | when (it.itemId) { 34 | R.id.refresh -> { 35 | CoroutineScope(Dispatchers.IO).launch { 36 | AndroidMediaStore.writeData(requireContext()) 37 | withContext(Dispatchers.Main) { 38 | logicList(binding.homeRv) 39 | logicListForFolders(binding.homeFoldersRv) 40 | } 41 | } 42 | true 43 | } 44 | 45 | else -> false 46 | } 47 | } 48 | logicList(binding.homeRv) 49 | logicListForFolders(binding.homeFoldersRv) 50 | } 51 | 52 | override fun getViewBinding(): FragmentMainHomeBinding { 53 | return FragmentMainHomeBinding.inflate(layoutInflater) 54 | } 55 | 56 | private fun refreshMediaData() { 57 | MediaUtils.updateMedia( 58 | requireContext(), 59 | Environment.getExternalStorageDirectory().toString() 60 | ) 61 | } 62 | 63 | 64 | private fun logicList(rv: RecyclerView) { 65 | CoroutineScope(Dispatchers.IO).launch { 66 | refreshMediaData().apply { 67 | val data = AndroidMediaStore.readVideosData() 68 | val model = model(data) 69 | withContext(Dispatchers.Main) { 70 | rv.linear().setup { 71 | it.layoutManager = CarouselLayoutManager() 72 | addType { R.layout.item_main_carousel } 73 | }.models = model 74 | rv.setHasFixedSize(true) 75 | } 76 | } 77 | } 78 | } 79 | 80 | private fun model(dataList: MMKV): MutableList { 81 | return mutableListOf().apply { 82 | val sortedList = dataList.allKeys()!!.sorted() 83 | val titleList = arrayListOf() 84 | val arrayList = arrayListOf() 85 | sortedList.reversed().take(10).forEachIndexed { _, s -> 86 | val items = dataList.decodeString(s)!! 87 | val list = items.split("\u001A") 88 | titleList.add(list[0]) 89 | arrayList.add(MediaItem.fromUri(list[1].toUri())) 90 | Play.list = arrayList 91 | Play.path = titleList 92 | val load = Glide.with(requireContext()) 93 | .setDefaultRequestOptions(RequestOptions().frame(1000000)).load(list[0]) 94 | .transition(DrawableTransitionOptions.withCrossFade()).diskCacheStrategy( 95 | DiskCacheStrategy.ALL 96 | ).centerCrop().override(500) 97 | add(CarouselModel(load, list)) 98 | } 99 | } 100 | } 101 | 102 | private fun logicListForFolders(rv: RecyclerView) { 103 | CoroutineScope(Dispatchers.IO).launch { 104 | refreshMediaData().apply { 105 | val data = AndroidMediaStore.readFoldersData() 106 | val model = modelForFolders(data) 107 | withContext(Dispatchers.Main) { 108 | rv.linear().setup { 109 | addType { R.layout.item_main_folders } 110 | }.models = model 111 | rv.setHasFixedSize(true) 112 | } 113 | } 114 | } 115 | } 116 | 117 | private fun modelForFolders(dataList: MMKV): MutableList { 118 | return mutableListOf().apply { 119 | dataList.allKeys()!!.forEachIndexed { _, s -> 120 | val titleList = s.split("/") 121 | val title = titleList[titleList.lastIndex - 1] 122 | val timeStamp = dataList.decodeString(s) 123 | val updateTime = millis2String(timeStamp!!.toLong() * 1000) 124 | add(FoldersModel(title, updateTime,parentFragmentManager,s)) 125 | } 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/home/ModalBottomSheet.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.home 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.core.net.toUri 8 | import androidx.media3.common.MediaItem 9 | import com.drake.brv.utils.linear 10 | import com.drake.brv.utils.setup 11 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 12 | import com.videoyou.x.R 13 | import com.videoyou.x.databinding.FoldersSheetBinding 14 | import com.videoyou.x.player.Play 15 | import com.videoyou.x.storage.AndroidMediaStore 16 | import com.videoyou.x.ui.fragment.home.model.VideosModel 17 | import kotlinx.coroutines.CoroutineScope 18 | import kotlinx.coroutines.Dispatchers 19 | import kotlinx.coroutines.launch 20 | import kotlinx.coroutines.withContext 21 | 22 | class ModalBottomSheet(private val folderPath: String) : BottomSheetDialogFragment() { 23 | private lateinit var binding: FoldersSheetBinding 24 | override fun onCreateView( 25 | inflater: LayoutInflater, 26 | container: ViewGroup?, 27 | savedInstanceState: Bundle? 28 | ): View { 29 | binding = FoldersSheetBinding.inflate(layoutInflater) 30 | return binding.root 31 | } 32 | 33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 34 | super.onViewCreated(view, savedInstanceState) 35 | CoroutineScope(Dispatchers.IO).launch { 36 | val model = models() 37 | withContext(Dispatchers.Main) { 38 | binding.videosRv.linear().setup { 39 | addType { R.layout.item_videos_item } 40 | }.models = model 41 | } 42 | } 43 | } 44 | 45 | private fun models(): MutableList = 46 | mutableListOf().apply { 47 | val kv = AndroidMediaStore.readFoldersVideosData() 48 | val arrayList = arrayListOf() 49 | kv.allKeys()!!.forEach { 50 | if (it.contains(folderPath) && it.split("\u001A")[0] == folderPath) { 51 | val data = kv.decodeString(it)!!.split("\u001A") 52 | arrayList.add(MediaItem.fromUri(data[1].toUri())) 53 | add(VideosModel(data[0], data[3], data[4],data[1],arrayList)) 54 | } 55 | } 56 | } 57 | 58 | companion object { 59 | const val TAG = "ModalBottomSheet" 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/home/model/CarouselModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.home.model 2 | 3 | import android.graphics.drawable.Drawable 4 | import androidx.core.net.toUri 5 | import com.bumptech.glide.RequestBuilder 6 | import com.drake.brv.BindingAdapter 7 | import com.drake.brv.item.ItemBind 8 | import com.drake.serialize.intent.openActivity 9 | import com.videoyou.x.PlayerActivity 10 | import com.videoyou.x.player.Play 11 | import com.videoyou.x.databinding.ItemMainCarouselBinding 12 | 13 | data class CarouselModel( 14 | private val img: RequestBuilder, 15 | private val list: List 16 | ) : ItemBind { 17 | override fun onBind(vh: BindingAdapter.BindingViewHolder) { 18 | vh.getBinding().apply { 19 | title.text = list[3] 20 | subTitle.text = "@ "+list[5] 21 | img.into(carouselImageView) 22 | carouselItemContainer.setOnClickListener { 23 | Play.position = vh.layoutPosition 24 | vh.context.openActivity( 25 | "path" to list[0], 26 | "uri" to (list[1]).toUri() 27 | ) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/home/model/FoldersModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.home.model 2 | 3 | import androidx.fragment.app.FragmentManager 4 | import com.drake.brv.BindingAdapter 5 | import com.drake.brv.item.ItemBind 6 | import com.videoyou.x.databinding.ItemMainFoldersBinding 7 | import com.videoyou.x.ui.fragment.home.ModalBottomSheet 8 | 9 | data class FoldersModel( 10 | private val title: String, 11 | private val updateTime: String, 12 | private val fragmentManager: FragmentManager, 13 | private val path: String, 14 | private val count: Int 15 | ) : ItemBind { 16 | override fun onBind(vh: BindingAdapter.BindingViewHolder) { 17 | vh.getBinding().apply { 18 | foldersTitle.text = title 19 | foldersInfo.text = updateTime 20 | count.text = count.toString() 21 | root.setOnClickListener { 22 | val modalBottomSheet = ModalBottomSheet(path) 23 | modalBottomSheet.show(fragmentManager, ModalBottomSheet.TAG) 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/ui/fragment/home/model/VideosModel.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.ui.fragment.home.model 2 | 3 | import androidx.core.net.toUri 4 | import com.bumptech.glide.Glide 5 | import com.bumptech.glide.load.engine.DiskCacheStrategy 6 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions 7 | import com.bumptech.glide.request.RequestOptions 8 | import com.drake.brv.BindingAdapter 9 | import com.drake.brv.item.ItemBind 10 | import com.drake.serialize.intent.openActivity 11 | import com.videoyou.x.PlayerActivity 12 | import com.videoyou.x.databinding.ItemVideosItemBinding 13 | import com.videoyou.x.player.Play 14 | 15 | class VideosModel( 16 | private val videoPath: String, 17 | private val mVideoTitle: String, 18 | private val videoSize: String, 19 | private val videoUri: String, 20 | private val playList: ArrayList 21 | ) : ItemBind { 22 | override fun onBind(vh: BindingAdapter.BindingViewHolder) { 23 | vh.getBinding().apply { 24 | val load = Glide.with(vh.context) 25 | .setDefaultRequestOptions(RequestOptions().frame(1000000)).load(videoPath) 26 | .transition(DrawableTransitionOptions.withCrossFade()).diskCacheStrategy( 27 | DiskCacheStrategy.ALL 28 | ).centerCrop().override(200) 29 | load.into(Thumbnail) 30 | videoTitle.text = mVideoTitle 31 | val size = (videoSize.toFloat() / 1024000F) 32 | if (size >= 1024) { 33 | videoSubTitle.text = String.format("%.2f", (size / 1024F)) + " GiB" 34 | } else { 35 | videoSubTitle.text = String.format("%.2f", size) + " MiB" 36 | } 37 | clickRoot.setOnClickListener { 38 | Play.list = playList 39 | Play.position = vh.layoutPosition 40 | vh.context.openActivity( 41 | "path" to videoPath, 42 | "uri" to videoUri.toUri() 43 | ) 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/utils/MediaUtils.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.utils 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.media.MediaScannerConnection 6 | 7 | object MediaUtils { 8 | @Suppress("DEPRECATION") 9 | fun updateMedia(context: Context, path: String) { 10 | MediaScannerConnection.scanFile( 11 | context, arrayOf(path), null 12 | ) { _, uri -> 13 | val mediaScanIntent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) 14 | mediaScanIntent.data = uri 15 | context.sendBroadcast(mediaScanIntent) 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/utils/System.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.utils 2 | 3 | import android.content.res.Configuration 4 | import android.content.res.Resources 5 | 6 | object System { 7 | fun isNightMode(resources: Resources): Boolean { 8 | return when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) { 9 | Configuration.UI_MODE_NIGHT_YES -> true 10 | else -> false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/utils/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.utils.base 2 | 3 | import android.os.Build 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import androidx.activity.enableEdgeToEdge 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.databinding.DataBindingUtil 9 | import androidx.databinding.ViewDataBinding 10 | import com.google.android.material.color.DynamicColors 11 | 12 | abstract class BaseActivity : AppCompatActivity() { 13 | 14 | lateinit var binding: VB 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | DynamicColors.applyToActivityIfAvailable(this) 19 | binding = DataBindingUtil.inflate( 20 | LayoutInflater.from(this), 21 | getLayout(), null, false 22 | ) 23 | enableEdgeToEdge() 24 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 25 | window.isNavigationBarContrastEnforced = false 26 | } 27 | setContentView(binding.root) 28 | } 29 | 30 | abstract fun getLayout(): Int 31 | } 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/videoyou/x/utils/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.videoyou.x.utils.base 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.databinding.ViewDataBinding 8 | import androidx.fragment.app.Fragment 9 | import androidx.viewbinding.ViewBinding 10 | 11 | abstract class BaseFragment : Fragment(), 12 | BaseFragmentView { 13 | protected val TAG: String = javaClass.simpleName 14 | protected lateinit var binding: VB 15 | 16 | override fun onCreateView( 17 | inflater: LayoutInflater, 18 | container: ViewGroup?, 19 | savedInstanceState: Bundle? 20 | ): View? { 21 | binding = getViewBinding() 22 | return binding.root 23 | } 24 | 25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 26 | super.onViewCreated(view, savedInstanceState) 27 | onViewCreate() 28 | } 29 | } 30 | 31 | interface BaseFragmentView { 32 | fun onViewCreate() 33 | 34 | fun getViewBinding(): VB 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/guide_back_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/guide_back_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/guide_next_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/guide_next_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_center_focus_weak_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_data_usage_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_folder_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_home_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_language_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_more_vert_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_picture_in_picture_alt_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_refresh_24.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color_dark_system_mask.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/curved_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 12 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 13 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_foreground.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_folder_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_functions_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_home_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_info_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_layers_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_list_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_topic_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_verified_user_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/outline_video_file_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_close_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_delete_outline_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_done_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_format_list_bulleted_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_history_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_menu_open_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_navigate_before_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_navigate_next_24.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_pause_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_picture_in_picture_alt_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_play_arrow_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_playlist_play_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_question_mark_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_sort_by_alpha_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_view_in_ar_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/strange_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/system_light_ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_guide.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 19 | 20 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 32 | 33 | 45 | 46 | 47 | 60 | 61 | 72 | 73 | 88 | 89 | 98 | 99 | 108 | 109 | 117 | 118 | 131 | 132 | 145 | 146 | 159 | 160 | 173 | 174 | 187 | 188 | -------------------------------------------------------------------------------- /app/src/main/res/layout/folders_sheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 18 | 19 | 23 | 24 | 28 | 29 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_guide_permission.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 18 | 19 | 28 | 29 | 40 | 41 | 51 | 52 | 53 | 54 | 64 | 65 | 76 | 77 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_functions.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 20 | 21 | 30 | 31 | 32 | 33 | 37 | 38 | 41 | 42 | 45 | 46 | 54 | 55 | 58 | 59 | 70 | 71 | 81 | 82 | 88 | 89 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 20 | 21 | 31 | 32 | 33 | 34 | 39 | 40 | 44 | 45 | 48 | 49 | 50 | 60 | 61 | 62 | 74 | 75 | 86 | 87 | 90 | 91 | 100 | 101 | 107 | 108 | 109 | 110 | 111 | 121 | 122 | 130 | 131 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_functions_overview.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 22 | 23 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_language_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 18 | 19 | 25 | 26 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_main_carousel.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 19 | 20 | 24 | 25 | 31 | 32 | 43 | 44 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_main_folders.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 22 | 23 | 32 | 33 | 39 | 40 | 46 | 47 | 48 | 59 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_permission_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 18 | 19 | 29 | 30 | 39 | 40 | 48 | 49 | 55 | 56 | 57 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_videos_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 19 | 20 | 26 | 27 | 28 | 37 | 38 | 46 | 47 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/module_page_footer.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/menu/home_topbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/menu/nav_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 17 | 22 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/navigation/guide_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx Player 5 | 6 | 选择语言 7 | 取消 8 | 跟随系统 9 | VideoYouX Verlassen 10 | Starte jetzt 11 | 12 | Zurück 13 | 继续 14 | 15 | 16 | 17 | 18 | Zugriff erforderlich 19 | Bitte erteilen Sie die Erlaubnis, damit diese App funktionieren kann. 20 | Zugriffsrechte für Mediendateien 21 | 用于访问您设备上的视频 22 | 画中画权限 23 | 用于支持以画中画模式播放视频 24 | 其他权限 25 | 例如震动等一些由系统默认给予的权限 26 | 27 | Scannen von Videos 28 | Scannen Sie ihre Dateien nach Videos. 29 | Scan Starten 30 | Scannen 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Start 45 | 文件 46 | Suche 47 | Abspielen 48 | Einstellungen 49 | Passe deinen Vyx an 50 | 51 | %1$s Ordner 52 | %1$s Videos 53 | %1$s Abspielen von Sequenzen 54 | 最新添加 55 | 暂无播放历史 56 | 暂无播放列表 57 | 功能 58 | 一览 59 | 刷新 60 | 更多 61 | 视频数量 62 | 文件夹数量 63 | 总体积 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values-en/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx Player 5 | 6 | Select Language 7 | Cancel 8 | Follow System 9 | Exit VideoYouX 10 | Go on 11 | 12 | Back 13 | Continue 14 | 15 | 16 | 17 | 18 | Permissions request 19 | Please grant this application permission to ensure its normal operation. 20 | Media file read permission 21 | Used to access videos on your device 22 | Picture-in-picture permission 23 | Used to support playing video in Picture-in-picture mode 24 | Other permissions 25 | Some permissions granted by system default such as vibrations 26 | 27 | Scan media files 28 | Scan the videos on your device from the MediaStore. 29 | Start scanning 30 | Scanning 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Home 45 | Files 46 | Search 47 | Play 48 | Settings 49 | Customize your Vyx 50 | 51 | %1$s Folders 52 | %1$s Videos 53 | %1$s Playing sequences 54 | Latest addition 55 | No playback history available 56 | No playlist available 57 | Functions 58 | Overview 59 | refresh 60 | More 61 | video count 62 | folder count 63 | total size 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx 播放器 5 | 6 | 选择语言 7 | 取消 8 | 跟随系统 9 | 退出 VideoYouX 10 | 现在开始 11 | 12 | 返回 13 | 继续 14 | 15 | 16 | 17 | 18 | 权限申请 19 | 请给予此应用权限以保证其能够正常运行。 20 | 媒体文件访问权限 21 | 用于访问您设备上的视频 22 | 画中画权限 23 | 用于支持以画中画模式播放视频 24 | 其他权限 25 | 例如震动等一些由系统默认给予的权限 26 | 27 | 扫描媒体文件 28 | 从媒体库扫描您设备中的视频。 29 | 开始扫描 30 | 正在扫描中 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 主页 45 | 文件 46 | 搜索 47 | 播放 48 | 设置 49 | 自定义您的 Vyx 50 | 51 | %1$s 个文件夹 52 | %1$s 个视频 53 | %1$s 个播放列表 54 | 最新添加 55 | 暂无播放历史 56 | 暂无播放列表 57 | 功能 58 | 一览 59 | 刷新 60 | 更多 61 | 视频数量 62 | 文件夹数量 63 | 总体积 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx 播放器 5 | 6 | 选择语言 7 | 取消 8 | 跟随系统 9 | 退出 VideoYouX 10 | 现在开始 11 | 12 | 返回 13 | 继续 14 | 15 | 16 | 17 | 18 | 权限申请 19 | 请给予此应用权限以保证其能够正常运行。 20 | 媒体文件访问权限 21 | 用于访问您设备上的视频 22 | 画中画权限 23 | 用于支持以画中画模式播放视频 24 | 其他权限 25 | 例如震动等一些由系统默认给予的权限 26 | 27 | 扫描媒体文件 28 | 从媒体库扫描您设备中的视频。 29 | 开始扫描 30 | 正在扫描中 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 主页 45 | 文件 46 | 搜索 47 | 播放 48 | 设置 49 | 自定义您的 Vyx 50 | 51 | %1$s 个文件夹 52 | %1$s 个视频 53 | %1$s 个播放列表 54 | 最新添加 55 | 暂无播放历史 56 | 暂无播放列表 57 | 功能 58 | 一览 59 | 刷新 60 | 更多 61 | 视频数量 62 | 文件夹数量 63 | 总体积 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx 播放器 5 | 6 | 选择语言 7 | 取消 8 | 跟随系统 9 | 退出 VideoYouX 10 | 现在开始 11 | 12 | 返回 13 | 继续 14 | 15 | 16 | 17 | 18 | 权限申请 19 | 请给予此应用权限以保证其能够正常运行。 20 | 媒体文件访问权限 21 | 用于访问您设备上的视频 22 | 画中画权限 23 | 用于支持以画中画模式播放视频 24 | 其他权限 25 | 例如震动等一些由系统默认给予的权限 26 | 27 | 扫描媒体文件 28 | 从媒体库扫描您设备中的视频。 29 | 开始扫描 30 | 正在扫描中 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 主页 45 | 文件 46 | 搜索 47 | 播放 48 | 设置 49 | 自定义您的 Vyx 50 | 51 | %1$s 个文件夹 52 | %1$s 个视频 53 | %1$s 个播放列表 54 | 最新添加 55 | 暂无播放历史 56 | 暂无播放列表 57 | 功能 58 | 一览 59 | 刷新 60 | 更多 61 | 视频数量 62 | 文件夹数量 63 | 总体积 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rTW/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vyx 播放器 5 | 6 | 选择语言 7 | 取消 8 | 跟随系统 9 | 退出 VideoYouX 10 | 现在开始 11 | 12 | 返回 13 | 继续 14 | 15 | 16 | 17 | 18 | 权限申请 19 | 请给予此应用权限以保证其能够正常运行。 20 | 媒体文件访问权限 21 | 用于访问您设备上的视频 22 | 画中画权限 23 | 用于支持以画中画模式播放视频 24 | 其他权限 25 | 例如震动等一些由系统默认给予的权限 26 | 27 | 扫描媒体文件 28 | 从媒体库扫描您设备中的视频。 29 | 开始扫描 30 | 正在扫描中 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 主页 45 | 文件 46 | 搜索 47 | 播放 48 | 设置 49 | 自定义您的 Vyx 50 | 51 | %1$s 个文件夹 52 | %1$s 个视频 53 | %1$s 个播放列表 54 | 最新添加 55 | 暂无播放历史 56 | 暂无播放列表 57 | 功能 58 | 一览 59 | 刷新 60 | 更多 61 | 视频数量 62 | 文件夹数量 63 | 总体积 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF000000 4 | #FFFFFFFF 5 | #d0374c 6 | #645959 7 | #00000000 8 | ?attr/colorPrimary 9 | #444444 10 | #50444444 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VideoYouX 5 | Vyx 播放器 6 | 7 | 8 | VideoYou 9 | 𝕏 10 | Welcome to 11 | 12 | 选择语言 13 | 取消 14 | 跟随系统 15 | %1$s · %2$s 16 | 17 | 退出 VideoYouX 18 | 现在开始 19 | 20 | 21 | 返回 22 | 继续 23 | 24 | 25 | 26 | 27 | 28 | 权限申请 29 | 请给予此应用权限以保证其能够正常运行。 30 | 媒体文件访问权限 31 | 用于访问您设备上的视频 32 | 画中画权限 33 | 用于支持以画中画模式播放视频 34 | 其他权限 35 | 例如震动等一些由系统默认给予的权限 36 | 37 | 38 | 扫描媒体文件 39 | 从媒体库扫描您设备中的视频。 40 | 开始扫描 41 | 正在扫描中 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 主页 58 | 文件 59 | 搜索 60 | 播放 61 | 设置 62 | 自定义您的 Vyx 63 | 64 | %1$s 个文件夹 65 | %1$s 个视频 66 | %1$s 个播放列表 67 | 最新添加 68 | 暂无播放历史 69 | 暂无播放列表 70 | 功能 71 | 一览 72 | 刷新 73 | 更多 74 | 视频数量 75 | 文件夹数量 76 | 总体积 77 | 按修改时间排序 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 12 | 13 | 16 | 17 | 21 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/locales_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.android.application) apply false 3 | alias(libs.plugins.kotlin.android) apply false 4 | alias(libs.plugins.kotlin.compose.compiler) apply false 5 | } 6 | 7 | allprojects { 8 | repositories { 9 | google() 10 | mavenCentral() 11 | maven("https://jitpack.io") 12 | maven("https://maven.aliyun.com/repository/google") 13 | maven("https://maven.aliyun.com/repository/gradle-plugin") 14 | maven("https://maven.aliyun.com/repository/public") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /app/src/main/res/values/ 3 | ignore: 4 | - /app/src/main/res/values/colors.xml 5 | - /app/src/main/res/values/themes.xml 6 | translation: /app/src/main/res/values-%two_letters_code%/%original_file_name% 7 | -------------------------------------------------------------------------------- /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 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.enableJetifier=true 25 | android.injected.testOnly=false 26 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | android-application = "8.7.1" 3 | kotlin = "2.0.21" 4 | 5 | androidx-appcompat = "1.7.0" 6 | androidx-activity = "1.9.3" 7 | androidx-activity-compose = "1.9.3" 8 | androidx-constraintlayout = "2.1.4" 9 | androidx-recyclerview = "1.3.2" 10 | androidx-lifecycle-livedata-ktx = "2.8.6" 11 | androidx-lifecycle-viewmodel-ktx = "2.8.6" 12 | androidx-navigation-fragment = "2.8.3" 13 | androidx-compose-animation = "1.7.4" 14 | androidx-compose-material3 = "1.3.0" 15 | androidx-compose-ui-viewbinding = "1.7.4" 16 | material = "1.12.0" 17 | media3 = "1.4.1" 18 | 19 | brv = "1.6.0" 20 | collapsingtoolbarlayoutSubtitle = "1.5.0" 21 | glide = "4.16.0" 22 | immersionbar = "3.2.2" 23 | lottie = "6.5.2" 24 | serialize = "3.0.1" 25 | utilcodex = "1.31.1" 26 | xx-permissions = "20.0" 27 | 28 | [libraries] 29 | androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } 30 | androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "media3" } 31 | androidx-media3-exoplayer-dash = { module = "androidx.media3:media3-exoplayer-dash", version.ref = "media3" } 32 | androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } 33 | 34 | androidx-compose-ui-viewbinding = { module = "androidx.compose.ui:ui-viewbinding", version.ref = "androidx-compose-ui-viewbinding" } 35 | androidx-compose-animation = { group = "androidx.compose.animation", name = "animation", version.ref = "androidx-compose-animation" } 36 | androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidx-compose-material3" } 37 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-compose" } 38 | 39 | androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } 40 | androidx-appcompat-resources = { group = "androidx.appcompat", name = "appcompat-resources", version.ref = "androidx-appcompat" } 41 | androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidx-activity" } 42 | androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } 43 | androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "androidx-recyclerview" } 44 | androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidx-lifecycle-livedata-ktx" } 45 | androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-viewmodel-ktx" } 46 | androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment", version.ref = "androidx-navigation-fragment" } 47 | 48 | brv = { module = "com.github.liangjingkanji:BRV", version.ref = "brv" } 49 | collapsingtoolbarlayout-subtitle = { module = "com.hendraanggrian.material:collapsingtoolbarlayout-subtitle", version.ref = "collapsingtoolbarlayoutSubtitle" } 50 | glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" } 51 | immersionbar = { module = "com.geyifeng.immersionbar:immersionbar", version.ref = "immersionbar" } 52 | serialize = { module = "com.github.liangjingkanji:Serialize", version.ref = "serialize" } 53 | utilcodex = { module = "com.blankj:utilcodex", version.ref = "utilcodex" } 54 | lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" } 55 | xx-permissions = { module = "com.github.getActivity:XXPermissions", version.ref = "xx-permissions" } 56 | google-material = { group = "com.google.android.material", name = "material", version.ref = "material" } 57 | 58 | 59 | [plugins] 60 | android-application = { id = "com.android.application", version.ref = "android-application" } 61 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 62 | kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 63 | 64 | [bundles] 65 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 12 18:59:03 CST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /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 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /imgs/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clearpole/VideoYouX/d1108028327b593d7325b87f6a8b390b3e553715/imgs/ic_launcher_round.webp -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "labels": [ 3 | "dependencies" 4 | ], 5 | "extends": [ 6 | "config:base" 7 | ], 8 | "automerge": true, 9 | "automergeType": "pr" 10 | } 11 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | maven("https://jitpack.io") 7 | maven("https://maven.aliyun.com/repository/google") 8 | maven("https://maven.aliyun.com/repository/gradle-plugin") 9 | maven("https://maven.aliyun.com/repository/public") 10 | } 11 | } 12 | 13 | plugins { 14 | id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0") 15 | } 16 | 17 | rootProject.name = "VideoYouX" 18 | include(":app") 19 | --------------------------------------------------------------------------------