├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── en-bug-report.yml │ ├── en-feature-request.yml │ ├── ja-bug-report.yml │ ├── ja-feature-request.yml │ ├── redirected-from-app.yml │ └── translation-error.yml └── dependabot.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README-ja.md ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── vrc_manager │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-mdpi │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_launcher_background.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ └── ic_launcher.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── build │ └── .last_build_id ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── img │ ├── FutureGraphics.kra │ ├── FutureGraphics.png │ ├── foreground.kra │ ├── foreground.png │ ├── icon.kra │ └── icon.png ├── svg │ ├── bandsintown.svg │ ├── behance.svg │ ├── codepen.svg │ ├── dribbble.svg │ ├── dropbox.svg │ ├── email.svg │ ├── facebook.svg │ ├── fivehundredpix.svg │ ├── flickr.svg │ ├── foursquare.svg │ ├── github.svg │ ├── google.svg │ ├── google_play.svg │ ├── instagram.svg │ ├── itunes.svg │ ├── linkedin.svg │ ├── mailto.svg │ ├── medium.svg │ ├── meetup.svg │ ├── pinterest.svg │ ├── rdio.svg │ ├── reddit.svg │ ├── rss.svg │ ├── sharethis.svg │ ├── smugmug.svg │ ├── snapchat.svg │ ├── soundcloud.svg │ ├── spotify.svg │ ├── squarespace.svg │ ├── tumblr.svg │ ├── twitch.svg │ ├── twitter.svg │ ├── vevo.svg │ ├── vimeo.svg │ ├── vine.svg │ ├── vk.svg │ ├── vsco.svg │ ├── wechat.svg │ ├── whatsapp.svg │ ├── yelp.svg │ └── youtube.svg └── tools │ ├── error_handling_checker.py │ ├── l10n │ ├── checker.py │ ├── marge.py │ ├── to_arb.py │ └── to_txt.py │ └── svg │ ├── svg.json │ └── svg.py ├── build.ps1 ├── cspell.json ├── docs ├── contribute │ ├── en.md │ └── ja.md ├── img │ ├── screenshots1.png │ ├── screenshots2.png │ ├── screenshots3.png │ ├── screenshots4.png │ └── screenshots5.png ├── privacy_policy │ ├── en.md │ └── ja.md └── user_policies │ ├── en.md │ └── ja.md ├── gen.ps1 ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── l10n.yaml ├── lib ├── api │ ├── .gitignore │ ├── assets │ │ ├── assets.dart │ │ ├── icon.dart │ │ ├── instance_type.dart │ │ ├── lang.dart │ │ ├── region.dart │ │ └── status.dart │ ├── data_class.dart │ ├── data_class_ext.dart │ └── main.dart ├── assets.dart ├── assets │ ├── anchor.dart │ ├── api │ │ ├── get.dart │ │ └── post.dart │ ├── conditions.dart │ ├── date.dart │ ├── flutter │ │ ├── text_stream.dart │ │ └── url_parser.dart │ ├── license.dart │ ├── session.dart │ ├── sort │ │ ├── users.dart │ │ └── worlds.dart │ ├── storage.dart │ └── theme │ │ ├── enum.dart │ │ └── true_black.dart ├── l10n │ ├── app_en.arb │ ├── app_es.arb │ ├── app_ja.arb │ ├── app_pt.arb │ ├── app_ru.arb │ ├── app_th.arb │ ├── app_zh.arb │ └── code.dart ├── main.dart ├── scenes │ ├── core │ │ └── splash.dart │ ├── main │ │ ├── friend_request.dart │ │ ├── friends.dart │ │ ├── main.dart │ │ ├── search.dart │ │ └── worlds_favorite.dart │ ├── setting │ │ ├── accessibility.dart │ │ ├── account.dart │ │ ├── help.dart │ │ ├── logger.dart │ │ ├── other_account.dart │ │ ├── permissions.dart │ │ ├── settings.dart │ │ └── token.dart │ ├── sub │ │ ├── json_viewer.dart │ │ ├── login.dart │ │ ├── self.dart │ │ ├── user.dart │ │ └── world.dart │ └── web │ │ ├── web_view.dart │ │ ├── web_view_login.dart │ │ └── web_view_policies.dart ├── storage │ ├── accessibility.dart │ ├── account.dart │ ├── grid_modal.dart │ └── user_policy.dart └── widgets │ ├── config_modal │ ├── locale.dart │ └── theme.dart │ ├── drawer.dart │ ├── future │ ├── button.dart │ └── tile.dart │ ├── grid_modal │ ├── config.dart │ └── modal.dart │ ├── grid_view │ ├── extraction │ │ ├── consumer.dart │ │ └── render_grid │ │ │ ├── favorite_world.dart │ │ │ ├── friends.dart │ │ │ ├── user.dart │ │ │ └── world.dart │ ├── template │ │ └── template.dart │ └── widget │ │ └── world.dart │ ├── loading.dart │ ├── lunch_world.dart │ ├── modal.dart │ ├── modal │ ├── share.dart │ ├── user.dart │ └── world.dart │ ├── region.dart │ ├── scroll.dart │ ├── share.dart │ ├── status.dart │ ├── user.dart │ └── world.dart ├── pubspec.lock ├── pubspec.yaml └── windows ├── .gitignore ├── CMakeLists.txt ├── build.ps1 ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake ├── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources │ └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h └── setup.iss /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/en-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "[en] 🐛 Report a bug" 2 | description: Report a bug in vrc_manager. 3 | labels: ["bug"] 4 | body: 5 | 6 | - type: input 7 | id: os 8 | attributes: 9 | label: OS 10 | description: Please include the version. 11 | placeholder: "Example: Android 12, Window 10" 12 | validations: 13 | required: true 14 | 15 | - type: input 16 | id: device-name 17 | attributes: 18 | label: Device name 19 | placeholder: "Example: Samsung Galaxy S22 Ultra" 20 | validations: 21 | required: true 22 | 23 | - type: input 24 | id: country-of-origin 25 | attributes: 26 | label: Country of origin of connection 27 | placeholder: "Example: Japan" 28 | validations: 29 | required: true 30 | 31 | - type: dropdown 32 | id: download-location 33 | attributes: 34 | label: Where you downloaded the app 35 | options: 36 | - Github(GMS) 37 | - Github(HMS) 38 | - Github(Windows) 39 | - Google Play Store 40 | - HUAWEI AppGallery 41 | - Xiaomi GetApps 42 | - Other 43 | validations: 44 | required: true 45 | 46 | - type: dropdown 47 | id: reinstall 48 | attributes: 49 | label: Have you reinstalled it ? 50 | options: 51 | - "Yes" 52 | - No, perhaps that won't be necessary. 53 | validations: 54 | required: true 55 | 56 | - type: textarea 57 | id: bug-description 58 | attributes: 59 | label: Bug description 60 | description: Describe in detail what the bug is about. Include the expected behaviour and actual behaviour. Please attach images if necessary. 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | id: steps-to-reproduce 66 | attributes: 67 | label: Steps to reproduce 68 | description: Provide detailed intructions to trigger the bug. 69 | placeholder: | 70 | Example: 71 | 1. Open "Online Friends" 72 | 2. Change the display setting from "Default" to "Simple" 73 | 3. ... 74 | ... 75 | validations: 76 | required: true 77 | 78 | - type: textarea 79 | id: error-message 80 | attributes: 81 | label: Relevant error message output 82 | description: Please paste any relevant Relevant error message output. 83 | render: shell 84 | validations: 85 | required: false 86 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/en-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: "[en] 💡 Request a feature" 2 | description: Please suggest features you would like to see in vrc_manager 3 | labels: ["enhancement"] 4 | body: 5 | 6 | - type: textarea 7 | id: feature-description 8 | attributes: 9 | label: Describe the feature 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | id: feature-reasoning 15 | attributes: 16 | label: How is this feature useful to users? 17 | validations: 18 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ja-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "[ja] 🐛 バグを報告する" 2 | description: vrc_managerの不具合を報告する。 3 | labels: ["bug"] 4 | body: 5 | 6 | - type: input 7 | id: os 8 | attributes: 9 | label: OS 10 | description: バージョンも含めてください 11 | placeholder: "例: Android 12, Window 10" 12 | validations: 13 | required: true 14 | 15 | - type: input 16 | id: device-name 17 | attributes: 18 | label: 端末名 19 | placeholder: "例: Samsung Galaxy S22 Ultra" 20 | validations: 21 | required: true 22 | 23 | - type: input 24 | id: country-of-origin 25 | attributes: 26 | label: 接続元の国名 27 | placeholder: "例: 日本" 28 | validations: 29 | required: true 30 | 31 | - type: dropdown 32 | id: download-location 33 | attributes: 34 | label: アプリをダウンロードした場所 35 | options: 36 | - Github(GMS) 37 | - Github(HMS) 38 | - Github(Windows) 39 | - Google Play Store 40 | - HUAWEI AppGallery 41 | - Xiaomi GetApps 42 | - Other 43 | validations: 44 | required: true 45 | 46 | - type: dropdown 47 | id: reinstall 48 | attributes: 49 | label: 再インストールしましたか? 50 | options: 51 | - はい 52 | - いいえ、恐らくその必要はない 53 | validations: 54 | required: true 55 | 56 | - type: textarea 57 | id: bug-description 58 | attributes: 59 | label: 不具合の説明 60 | description: バグの内容を詳しく記述してください。期待される動作と実際の動作を含めてください。必要があれば画像を添付して下さい。 61 | validations: 62 | required: true 63 | 64 | - type: textarea 65 | id: steps-to-reproduce 66 | attributes: 67 | label: 不具合を起こすための手順 68 | description: バグを発生させるための詳細な手順を記述してください。 69 | placeholder: | 70 | 例: 71 | 1. 「オンラインのフレンド」を開きます 72 | 2. 表示設定を「デフォルト」から「シンプル」に変更します 73 | 3. ... 74 | ... 75 | validations: 76 | required: true 77 | 78 | - type: textarea 79 | id: error-message 80 | attributes: 81 | label: エラーメッセージ 82 | description: エラーメッセージが表示されていればそれを貼り付けてください。 83 | render: shell 84 | validations: 85 | required: false 86 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/ja-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: "[ja] 💡 機能を提案する" 2 | description: vrc_managerに欲しい機能を提案して下さい 3 | labels: ["enhancement"] 4 | body: 5 | 6 | - type: textarea 7 | id: feature-description 8 | attributes: 9 | label: 機能を説明する 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | id: feature-reasoning 15 | attributes: 16 | label: この機能はユーザーにとってどのように役に立つのでしょうか? 17 | validations: 18 | required: true -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/redirected-from-app.yml: -------------------------------------------------------------------------------- 1 | name: "📓 Redirected from app" 2 | description: Report error message in vrc_manager. 3 | labels: ["bug"] 4 | body: 5 | 6 | - type: textarea 7 | id: steps-to-reproduce 8 | attributes: 9 | label: Steps to reproduce 10 | description: Provide detailed intructions to trigger the bug. 11 | placeholder: | 12 | Example: 13 | 1. Start with all application data deleted. 14 | 2. Login and open Home 15 | 3. ... 16 | ... 17 | validations: 18 | required: true 19 | 20 | - type: dropdown 21 | id: download-location 22 | attributes: 23 | label: Where you downloaded the app 24 | options: 25 | - Github(GMS) 26 | - Github(HMS) 27 | - Github(Windows) 28 | - Google Play Store 29 | - HUAWEI AppGallery 30 | - Xiaomi GetApps 31 | - Other 32 | validations: 33 | required: true 34 | 35 | - type: textarea 36 | id: error-message 37 | attributes: 38 | label: Relevant error message output 39 | description: Please paste the log. Logs are automatically copied to the clipboard. 40 | render: shell 41 | validations: 42 | required: true 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/translation-error.yml: -------------------------------------------------------------------------------- 1 | name: "📓 Report a translation error" 2 | description: Report error message in vrc_manager. 3 | labels: ["bug"] 4 | body: 5 | 6 | - type: dropdown 7 | id: language 8 | attributes: 9 | label: Language 10 | options: 11 | - English 12 | - 日本語 13 | - español 14 | - Português 15 | - русский 16 | - ไทย 17 | - 简体中文 18 | - Other 19 | validations: 20 | required: true 21 | 22 | - type: dropdown 23 | id: locale-native-speaker 24 | attributes: 25 | label: Are you a native speaker of the language? 26 | options: 27 | - "True" 28 | - "False" 29 | validations: 30 | required: true 31 | 32 | - type: textarea 33 | id: steps-to-reproduce 34 | attributes: 35 | label: Steps to reproduce 36 | description: Procedure for displaying translation errors. 37 | placeholder: | 38 | Example: 39 | 1. Open "Setting" 40 | 2. Open "Accessibility" 41 | validations: 42 | required: true 43 | 44 | 45 | - type: input 46 | id: incorrect-translation 47 | attributes: 48 | label: Incorrect translation 49 | description: Please enter the incorrect translation. 50 | placeholder: "Example: 3 Billion Devices Run Coffee." 51 | validations: 52 | required: true 53 | 54 | - type: input 55 | id: correct-translation 56 | attributes: 57 | label: Correct translation 58 | description: Please enter the correct translation. 59 | placeholder: "Example: 3 Billion Devices Run Java." 60 | validations: 61 | required: true 62 | 63 | 64 | - type: textarea 65 | id: note 66 | attributes: 67 | label: Note 68 | description: Please indicate anything else of note. 69 | validations: 70 | required: false 71 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pub" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "4cf269e36de2573851eaef3c763994f8f9be494d" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 17 | base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 18 | - platform: windows 19 | create_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 20 | base_revision: 4cf269e36de2573851eaef3c763994f8f9be494d 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense を使用して利用可能な属性を学べます。 3 | // 既存の属性の説明をホバーして表示します。 4 | // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "vrchat_mobile_client", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "vrchat_mobile_client (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "vrchat_mobile_client (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.lineLength": 160, 3 | "markdownlint.config": { 4 | "MD033": false 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 yuki 4 | 5 | https://yuki0311.com/ 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

7 | マルチプラットフォーム Android, Windows(残念ながら Macbook を持っていません😫)
8 | 多言語対応 en, ja, es, pt, ru, th, zh
9 | 複数のログイン方法に対応 2fa, WebView, Token
10 | 複数のアカウントに対応 Just H 用のアカウントをお持ちですか?
11 | 全て無料 もちろん広告もなし
12 | アプリリンクに対応 URL から直接アプリを開けます
13 | 洗練された UI モダンなデザイン
14 | ダークテーマに対応 6種類のテーマと3種類の表示方法
15 |

16 |
17 | 18 | [English](README.md) / [Japanese](README-ja.md) 19 | 20 | 21 | 22 | ## インストール 23 | 24 | ### Android 25 | 26 | - [**play.google.com**](https://play.google.com/store/apps/details?id=com.yuki0311.vrc_manager) 27 | - [**appgallery.huawei.com**](https://appgallery.huawei.com/#/app/C106854219) **(非推奨)** 28 | - [**Releases**](https://github.com/fa0311/vrc_manager/releases)の**app-release.apk** 29 | 30 | ### Windows 31 | 32 | - [**Releases**](https://github.com/fa0311/vrc_manager/releases)の**VRCManager-Setup.exe** 33 | 34 | ## ドキュメント 35 | 36 | 開発・翻訳を手伝ってみませんか? [**貢献**](docs/contribute/ja.md) 37 | 38 | ## 免責事項 39 | 40 | VRChat API の使用に関する VRChat チーム(Tupper 氏)の公式な回答です。 41 | 42 | > Use of the API using applications other than the approved methods (website, VRChat application) are not officially supported. You may use the API for your own application, but keep these guidelines in mind: 43 | > 44 | > - We do not provide documentation or support for the API. 45 | > - Do not make queries to the API more than once per 60 seconds. 46 | > - Abuse of the API may result in account termination. 47 | > - Access to API endpoints may break at any given time, with no warning. 48 | > 49 | > [出典](https://vrchatapi.github.io/sdk/java/) 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

7 | Multi platform Android, Windows (unfortunately I don't have a Macbook😫)
8 | Multilingual support en, ja, es, pt, ru, th, zh
9 | Support for multiple login methods 2fa, WebView, Token
10 | Multiple account support Do you have a Just H account?
11 | Free for all And of course, no ads.
12 | Supports application links You can open the app directly from the URL
13 | Sophisticated UI Modern design
14 | Supports dark themes 6 different themes and 3 different display methods
15 |

16 |
17 | 18 | [English](README.md) / [Japanese](README-ja.md) 19 | 20 | 21 | 22 | ## Install 23 | 24 | ### Android 25 | 26 | - [**play.google.com**](https://play.google.com/store/apps/details?id=com.yuki0311.vrc_manager) 27 | - [**appgallery.huawei.com**](https://appgallery.huawei.com/#/app/C106854219) **(deprecated)** 28 | - [**Releases**](https://github.com/fa0311/vrc_manager/releases) **app-release.apk** 29 | 30 | ### Windows 31 | 32 | - [**Releases**](https://github.com/fa0311/vrc_manager/releases) **VRCManager-Setup.exe** 33 | 34 | ## Documents 35 | 36 | Want to help with development and translation? [**contribute**](docs/contribute/en.md) 37 | 38 | ## Disclaimer 39 | 40 | This is the official response of the VRChat Team (from Tupper more specifically) on the usage of the VRChat API. 41 | 42 | > Use of the API using applications other than the approved methods (website, VRChat application) are not officially supported. You may use the API for your own application, but keep these guidelines in mind: 43 | > 44 | > - We do not provide documentation or support for the API. 45 | > - Do not make queries to the API more than once per 60 seconds. 46 | > - Abuse of the API may result in account termination. 47 | > - Access to API endpoints may break at any given time, with no warning. 48 | > 49 | > [Reference](https://vrchatapi.github.io/sdk/java/) 50 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | errors: 3 | deprecated_member_use: ignore 4 | include: package:flutter_lints/flutter.yaml 5 | 6 | linter: 7 | rules: 8 | use_build_context_synchronously: false 9 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | def keystoreProperties = new Properties() 9 | def keystorePropertiesFile = rootProject.file('key.properties') 10 | if (keystorePropertiesFile.exists()) { 11 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 12 | } 13 | 14 | android { 15 | namespace = "com.yuki0311.vrc_manager" 16 | compileSdk = flutter.compileSdkVersion 17 | ndkVersion = flutter.ndkVersion 18 | 19 | compileOptions { 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | } 23 | 24 | kotlinOptions { 25 | jvmTarget = JavaVersion.VERSION_1_8 26 | } 27 | 28 | defaultConfig { 29 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 30 | applicationId = "com.yuki0311.vrc_manager" 31 | // You can update the following values to match your application needs. 32 | // For more information, see: https://flutter.dev/to/review-gradle-config. 33 | minSdk = flutter.minSdkVersion 34 | targetSdk = flutter.targetSdkVersion 35 | versionCode = flutter.versionCode 36 | versionName = flutter.versionName 37 | } 38 | 39 | signingConfigs { 40 | release { 41 | keyAlias keystoreProperties['keyAlias'] 42 | keyPassword keystoreProperties['keyPassword'] 43 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 44 | storePassword keystoreProperties['storePassword'] 45 | } 46 | } 47 | 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.release 54 | } 55 | } 56 | } 57 | 58 | 59 | java { 60 | toolchain { 61 | languageVersion = JavaLanguageVersion.of(17) 62 | } 63 | } 64 | 65 | flutter { 66 | source = "../.." 67 | } 68 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # https://github.com/mogol/flutter_secure_storage/issues/748 2 | -dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue 3 | -dontwarn com.google.errorprone.annotations.CheckReturnValue 4 | -dontwarn com.google.errorprone.annotations.Immutable 5 | -dontwarn com.google.errorprone.annotations.RestrictedApi 6 | -dontwarn javax.annotation.Nullable 7 | -dontwarn javax.annotation.concurrent.GuardedBy -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 22 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/vrc_manager/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.yuki0311.vrc_manager 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | subprojects { 7 | afterEvaluate { project -> 8 | if (project.hasProperty('android')) { 9 | project.android { 10 | if (namespace == null) { 11 | namespace project.group 12 | } 13 | } 14 | } 15 | } 16 | } 17 | } 18 | 19 | rootProject.buildDir = "../build" 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(":app") 25 | } 26 | 27 | tasks.register("clean", Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/build/.last_build_id: -------------------------------------------------------------------------------- 1 | 32031a0bdc0e7e5eafe51aacb3b02d03 -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /assets/img/FutureGraphics.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/FutureGraphics.kra -------------------------------------------------------------------------------- /assets/img/FutureGraphics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/FutureGraphics.png -------------------------------------------------------------------------------- /assets/img/foreground.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/foreground.kra -------------------------------------------------------------------------------- /assets/img/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/foreground.png -------------------------------------------------------------------------------- /assets/img/icon.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/icon.kra -------------------------------------------------------------------------------- /assets/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/assets/img/icon.png -------------------------------------------------------------------------------- /assets/svg/bandsintown.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/behance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/codepen.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/dribbble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/dropbox.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/fivehundredpix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/flickr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/foursquare.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/google.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/google_play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/instagram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/itunes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/mailto.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/meetup.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/pinterest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/rdio.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/reddit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/rss.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/sharethis.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/smugmug.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/snapchat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/soundcloud.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/spotify.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/squarespace.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/tumblr.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/twitch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/vevo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/vimeo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/vine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/vk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/vsco.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/wechat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/yelp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/svg/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /assets/tools/error_handling_checker.py: -------------------------------------------------------------------------------- 1 | import json 2 | import glob 3 | 4 | 5 | def in_dart(key1,key2): 6 | for file in glob.glob("lib/*.dart"): 7 | with open(file,'r',encoding="utf-8") as f: 8 | code = f.read() 9 | count = code.count(key1) - code.count(key2) 10 | if count != 0: 11 | print(f"{key1} is {count} more in {file}") 12 | 13 | for file in glob.glob("lib/*/*.dart"): 14 | with open(file,'r',encoding="utf-8") as f: 15 | code = f.read() 16 | count = code.count(key1) - code.count(key2) 17 | if count != 0: 18 | print(f"{key1} is {count} more in {file}") 19 | 20 | for file in glob.glob("lib/*/*/*.dart"): 21 | with open(file,'r',encoding="utf-8") as f: 22 | code = f.read() 23 | count = code.count(key1) - code.count(key2) 24 | if count != 0: 25 | print(f"{key1} is {count} more in {file}") 26 | 27 | in_dart("VRChatAPI(cookie","apiError") -------------------------------------------------------------------------------- /assets/tools/l10n/checker.py: -------------------------------------------------------------------------------- 1 | import json 2 | import glob 3 | 4 | with open('lib/l10n/app_en.arb','r',encoding="utf-8") as f: 5 | json_load = json.load(f) 6 | 7 | 8 | def in_dart(key): 9 | for file in glob.glob("lib/*.dart", recursive=True): 10 | with open(file,'r',encoding="utf-8") as f: 11 | if(f"AppLocalizations.of(context)!.{key}" in f.read()): 12 | return False 13 | for file in glob.glob("lib/**/*.dart", recursive=True): 14 | with open(file,'r',encoding="utf-8") as f: 15 | if(f"AppLocalizations.of(context)!.{key}" in f.read()): 16 | return False 17 | return True 18 | 19 | for key in json_load: 20 | if key[0] == "@": 21 | continue 22 | 23 | if in_dart(key): 24 | print(f"{key} is not found") -------------------------------------------------------------------------------- /assets/tools/l10n/marge.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | with open('lib/l10n/app_ja.arb','r',encoding="utf-8") as f: 5 | json_load_into = json.load(f) 6 | 7 | with open('lib/l10n/app_en.arb','r',encoding="utf-8") as f: 8 | json_load_from = json.load(f) 9 | 10 | output_json = {} 11 | 12 | for key in json_load_from: 13 | if type(json_load_from[key]) is str: 14 | output_json[key] = json_load_into.get(key,json_load_from[key]) 15 | 16 | 17 | with open('lib/l10n/l10n.arb', 'w+' ,encoding="utf-8") as f: 18 | json.dump(output_json, f, indent=4,ensure_ascii=False ) -------------------------------------------------------------------------------- /assets/tools/l10n/to_arb.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | with open('lib/l10n/app_ja.arb','r',encoding="utf-8") as f: 5 | json_load = json.load(f) 6 | 7 | with open('lib/l10n/l10n.txt','r',encoding="utf-8") as f: 8 | lines = f.read().split("\n") 9 | 10 | i = 0 11 | for key in json_load: 12 | if type(json_load[key]) is str: 13 | json_load[key] = lines[i] 14 | i += 1 15 | 16 | 17 | with open('lib/l10n/l10n.arb', 'w+' ,encoding="utf-8") as f: 18 | json.dump(json_load, f, indent=4,ensure_ascii=False ) -------------------------------------------------------------------------------- /assets/tools/l10n/to_txt.py: -------------------------------------------------------------------------------- 1 | import json 2 | from tokenize import String 3 | 4 | with open('lib/l10n/app_ja.arb','r',encoding="utf-8") as f: 5 | json_load = json.load(f) 6 | 7 | 8 | 9 | output = "" 10 | for key in json_load: 11 | if type(json_load[key]) is str: 12 | output += json_load[key] + "\n" 13 | 14 | 15 | with open('lib/l10n/l10n_base.txt', mode='w+',encoding="utf-8") as f: 16 | f.write(output) -------------------------------------------------------------------------------- /assets/tools/svg/svg.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import json 3 | 4 | svglist = json.load(open('assets/svg/svg.json', 'r')) 5 | 6 | 7 | for key in svglist.keys(): 8 | pathlib.Path("assets/svg/{0}.svg".format(key)).touch() 9 | with open("assets/svg/{0}.svg".format(key),'w') as f: 10 | f.write(""" 11 | 12 | 13 | 14 | """.format(svglist[key]["icon"])) -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | flutter build apk 2 | flutter build appbundle -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "version": "0.2", 4 | "language": "en", 5 | "words": [ 6 | /* Service Name */ 7 | "bandsintown", 8 | "behance", 9 | "codepen", 10 | "dribbble", 11 | "fivehundredpix", 12 | "flickr", 13 | "pinterest", 14 | "rdio", 15 | "sharethis", 16 | "smugmug", 17 | "soundcloud", 18 | "squarespace", 19 | "vevo", 20 | "vrchat", 21 | "vsco", 22 | "wechat", 23 | /* Product */ 24 | "Macbook", 25 | "appgallery", 26 | /* HTTP */ 27 | "KHTML", 28 | /* API */ 29 | "standalonewindows", 30 | "totp", 31 | "twofactorauth", 32 | "moderations", 33 | /* Flutter */ 34 | "unfocus", 35 | "autofocus", 36 | "ARGB", 37 | "pubspec", 38 | /* Dart */ 39 | "sublist", 40 | /* Library */ 41 | "cupertino", 42 | "fluttertoast", 43 | "agconnect", 44 | "riverpod", 45 | "inappwebview", 46 | /* Name */ 47 | "yuki", 48 | "Tupper", 49 | /* Other */ 50 | "dont" 51 | ], 52 | "ignorePaths": [ 53 | /*Flutter*/ 54 | "android/**", 55 | "build/**", 56 | "ios/**", 57 | "windows/**", 58 | "pubspec.yaml", 59 | /*Extension*/ 60 | "*kra", 61 | /* Other */ 62 | "lib/l10n/**", 63 | "lib/api/assets/lang.dart" 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /docs/contribute/en.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | ## About Branches 4 | 5 | - **master** Product Release Same as GooglePlay 6 | - **master-HMS** Product Release Same as AppGallery 7 | - **pre** Pre-release Major feature additions or Google Play review 8 | - **pre-HMS** Pre-release AppGallery review 9 | - **develop** Development version PullRequest goes here 10 | 11 | ## Translation 12 | 13 | - [Issues](https://github.com/fa0311/vrc_manager/issues/new?assignees=&labels=bug&template=translation-error.yml) 14 | - [lib/l10n](../../lib/l10n) 15 | 16 | ### icon 17 | 18 | The current app icon is temporary 19 | We are currently accepting applications 20 | 21 | ## Setup 22 | 23 | ```shell 24 | flutter pub get 25 | ``` 26 | 27 | ## Build 28 | 29 | ```shell 30 | flutter run 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/contribute/ja.md: -------------------------------------------------------------------------------- 1 | # 貢献 2 | 3 | ## ブランチについて 4 | 5 | - **master** プロダクトリリース GooglePlay と同じ 6 | - **master-HMS** プロダクトリリース AppGallery と同じ 7 | - **pre** プレリリース 大きな機能追加もしくは GooglePlay の審査 8 | - **pre-HMS** プレリリース AppGallery の審査 9 | - **develop** 開発バージョン PullRequest はここへ 10 | 11 | ## 翻訳 12 | 13 | - [Issues](https://github.com/fa0311/vrc_manager/issues/new?assignees=&labels=bug&template=translation-error.yml) 14 | - [lib/l10n](../../lib/l10n) 15 | 16 | ### アイコン 17 | 18 | 現在のアプリアイコンは仮のものです 19 | 募集中です 20 | 21 | ## セットアップ 22 | 23 | ```shell 24 | flutter pub get 25 | ``` 26 | 27 | ## ビルド 28 | 29 | ```shell 30 | flutter run 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/img/screenshots1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/docs/img/screenshots1.png -------------------------------------------------------------------------------- /docs/img/screenshots2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/docs/img/screenshots2.png -------------------------------------------------------------------------------- /docs/img/screenshots3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/docs/img/screenshots3.png -------------------------------------------------------------------------------- /docs/img/screenshots4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/docs/img/screenshots4.png -------------------------------------------------------------------------------- /docs/img/screenshots5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/docs/img/screenshots5.png -------------------------------------------------------------------------------- /docs/privacy_policy/en.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy. 2 | 3 | [Japanese](ja.md) / [English](en.md) 4 | 5 | # Introduction 6 | 7 | yuki0311 ("we", "us", "our") has established the following privacy policy regarding the handling of users' personal information in the services provided on various devices for smartphones and mobile devices (the "Services"). 8 | In the event of any inconsistency between this Privacy Policy in Japanese and the corresponding Privacy Policy in other languages, the Japanese version shall prevail. 9 | 10 | ## Data to be collected 11 | 12 | We do not collect any information that users provide directly through the Service's content or the Service's functionality. All data entered by you through the Services will only be stored on your device. However, the Service uses third party services that may collect personal information or information that can be used to identify you. 13 | 14 | The third party services used by the Service and links to their privacy-related information are listed below. 15 | 16 | - [vrchat.com](https://hello.vrchat.com/privacy) 17 | - [github.com](https://docs.github.com/en/site-policy/privacy-policies) 18 | 19 | ## Further information on personal data 20 | 21 | The Service is not intended for children under 13 years of age. The user declares that he/she is an adult according to the applicable law. Minors may only use the Services with the support of a parent or guardian. Under no circumstances may persons under the age of 13 use the Services. 22 | 23 | ## Links to other sites 24 | 25 | The Service may contain links to other sites. When you click on a third party link, you will be taken to that site. Note that these external sites are not operated by us. We therefore strongly recommend that you review the privacy policies of these websites. We have no control over, and accept no responsibility for, the content, privacy policies or practices of any third party sites or services. 26 | 27 | ## Changes to our privacy policy 28 | 29 | We may update our Privacy Policy from time to time. The Privacy Policy is maintained on Github and we will notify you of changes when we post a new Privacy Policy on this page by enabling Notifications on Github. These changes will take effect immediately after they are posted on this page. More information about notifications can be found [here](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications). 30 | 31 | ## Transparency of the Service 32 | 33 | All programmes of the Service are publicly available on Github. 34 | For more information, see [here](https://github.com/fa0311/vrc_manager). 35 | 36 | ## How to contact us. 37 | 38 | If you have any questions about this Privacy Policy, please contact us at the following email address. 39 | 40 | yuki@yuki0311.com 41 | -------------------------------------------------------------------------------- /docs/privacy_policy/ja.md: -------------------------------------------------------------------------------- 1 | # プライバシーポリシー 2 | 3 | [日本語](ja.md) / [英語](en.md) 4 | 5 | # はじめに 6 | 7 | yuki0311 (以下,「私達」) は、スマートフォンおよびモバイル デバイス用のさまざまなデバイス上で提供するサービス(以下,「本サービス」)における、ユーザーの個人情報の取扱いについて以下のとおりプライバシーポリシーを定めます。 8 | この日本語のプライバシーポリシーとそれに対応する他の言語のプライバシーポリシーとに矛盾がある場合は、日本語を優先するものとします。 9 | 10 | ## 収集するデータ 11 | 12 | 私達はユーザーが本サービスのコンテンツまたは本サービスの機能を通じて直接提供する情報を収集することはありません。本サービスを通じてユーザーが入力したすべてのデータはユーザーのデバイスにのみ保存されます。ただし本サービスは個人情報もしくはあなたを識別するために使用される情報を収集する可能性のあるサードパーティのサービスを使用します。 13 | 14 | 本サービスの利用するサードパーティのサービスとそのプライバシー関連の情報へのリンクを下記で示します。 15 | 16 | - [vrchat.com](https://hello.vrchat.com/privacy) 17 | - [github.com](https://docs.github.com/en/site-policy/privacy-policies) 18 | 19 | ## 個人データに関する詳細情報 20 | 21 | 本サービスは、13 歳未満の子供を対象としていません。ユーザーは、適用される法律に従って成人であると宣言します。未成年者は親または保護者の支援がある場合にのみ本サービスを使用できます。いかなる状況においても、13 歳未満の人は本サービスを使用できません。 22 | ​ 23 | 24 | ## 他のサイトへのリンク 25 | 26 | 本サービスには、他のサイトへのリンクが含まれている場合があります。サードパーティのリンクをクリックすると、そのサイトに移動します。これらの外部サイトは私達によって運営されていないことに注意してください。したがって、これらの Web サイトのプライバシーポリシーを確認することを強くお勧めします。当社は、第三者のサイトまたはサービスのコンテンツ、プライバシーポリシー、または慣行を管理することはできず、責任も負わないものとします。 27 | 28 | ## プライバシーポリシーの変更 29 | 30 | 私達はプライバシーポリシーを更新する場合があります。プライバシーポリシーは Github で管理されているため Github 上の Notifications を有効にすることによりこのページに新しいプライバシーポリシーを掲載する際に変更を通知します。これらの変更はこのページに掲載された直後に有効になります。通知についての詳細は[こちら](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications)をご覧ください。 31 | 32 | ## 本サービスの透明性 33 | 34 | 本サービスのプログラムはすべて Github 上で公開されています。 35 | 詳細は[こちら](https://github.com/fa0311/vrc_manager)をご覧ください。 36 | 37 | ## お問い合わせ方法 38 | 39 | このプライバシーポリシーに関してご質問がある場合は、次のメール アドレスまでご連絡ください。 40 | 41 | yuki@yuki0311.com 42 | -------------------------------------------------------------------------------- /docs/user_policies/en.md: -------------------------------------------------------------------------------- 1 | # User policy 2 | 3 | [日本語](ja.md) / [English](en.md) 4 | 5 | ## Introduction 6 | 7 | yuki0311 ("we", "us", "our", "we", "our") sets out the terms and conditions of use for the services provided on various devices for smartphones and mobile devices (the "Services"). 8 | In the event of any inconsistency between this English privacy policy and the corresponding privacy policy in other languages, the Japanese shall prevail. 9 | 10 | ## Use of data 11 | 12 | Personal data obtained through the use of the Service is governed by the [privacy policy](../privacy_policy/en.md). 13 | 14 | ## Account and registration 15 | 16 | You must register for a third party [VRChat](https://hello.vrchat.com/) account to access most features of the Service. 17 | The Service may use [VRChat](https://hello.vrchat.com/) login credentials to access certain features of the Platform. 18 | 19 | ## Copyright 20 | 21 | Please see [MIT LICENSE](/LICENSE) for the copyright of the Service. 22 | 23 | ## Prohibitions 24 | 25 | Please see [MIT LICENSE](/LICENSE) for information on the prohibitions of the Service. 26 | In addition, the User may not engage in any conduct prohibited by the Service when using third party services. 27 | 28 | - [vrchat.com](https://hello.vrchat.com/legal) 29 | 30 | ## Disclaimer 31 | 32 | Please see [MIT LICENSE](/LICENSE) for a disclaimer of liability for the Service. 33 | 34 | ## Prohibition of transfer of rights and obligations 35 | 36 | The User may not transfer his/her position in the contract of use or his/her rights or obligations under these Terms and Conditions to a third party or offer them as security without the prior written consent of the Company. 37 | 38 | ## Governing law and jurisdiction 39 | 40 | The interpretation of these Terms and Conditions shall be governed by the laws of Japan. 41 | In the event of disputes regarding the Service, the court with jurisdiction over the location of our head office shall have exclusive jurisdiction. 42 | 43 | ## Changes to the User Policy 44 | 45 | We may update the User Policy from time to time. As the User Policy is maintained on Github, you will be notified of changes when we post a new User Policy on this page by enabling Notifications on Github. These changes will take effect immediately after they are posted on this page. More information about notifications can be found [here](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications). 46 | 47 | ## Transparency of the Service 48 | 49 | All programmes of the Service are publicly available on Github. 50 | For more information, see [here](https://github.com/fa0311/vrc_manager). 51 | 52 | ## How to contact us 53 | 54 | If you have any questions regarding this user policy, please contact us at the following email address. 55 | 56 | yuki@yuki0311.com 57 | -------------------------------------------------------------------------------- /docs/user_policies/ja.md: -------------------------------------------------------------------------------- 1 | # ユーザーポリシー 2 | 3 | [日本語](ja.md) / [English](en.md) 4 | 5 | ## はじめに 6 | 7 | yuki0311 (以下,「私達」) は、スマートフォンおよびモバイル デバイス用のさまざまなデバイス上で提供するサービス(以下,「本サービス」)における利用条件を定めます。 8 | この日本語のプライバシーポリシーとそれに対応する他の言語のプライバシーポリシーとに矛盾がある場合は、日本語を優先するものとします。 9 | 10 | ## データの使用 11 | 12 | 本サービスの利用によって取得する個人情報については[プライバシーポリシー](../privacy_policy/ja.md)に従い適切に取り扱うものとします。 13 | 14 | ## アカウントと登録 15 | 16 | 本サービスのほとんどの機能にアクセスするにはサードパーティの[VRChat](https://hello.vrchat.com/)のアカウントを登録する必要があります。 17 | 本サービスは[VRChat](https://hello.vrchat.com/)のログイン資格情報を使用してプラットフォームの特定の機能にアクセスすることがあります。 18 | 19 | ## 著作権 20 | 21 | 本サービスの著作権については[MIT LICENSE](/LICENSE)をご覧ください。 22 | 23 | ## 禁止事項 24 | 25 | 本サービスの禁止事項については[MIT LICENSE](/LICENSE)をご覧ください。 26 | また、ユーザーはサードパーティサービスの利用にあたりサービスが禁止している行為をしてはなりません。 27 | 28 | - [vrchat.com](https://hello.vrchat.com/legal) 29 | 30 | ## 免責事項 31 | 32 | 本サービスの免責事項については[MIT LICENSE](/LICENSE)をご覧ください。 33 | 34 | ## 権利義務の譲渡の禁止 35 | 36 | ユーザーは,当社の書面による事前の承諾なく,利用契約上の地位または本規約に基づく権利もしくは義務を第三者に譲渡し,または担保に供することはできません。 37 | 38 | ## 準拠法・裁判管轄 39 | 40 | 本規約の解釈にあたっては,日本法を準拠法とします。 41 | 本サービスに関して紛争が生じた場合には,当社の本店所在地を管轄する裁判所を専属的合意管轄とします。 42 | 43 | ## ユーザーポリシーの変更 44 | 45 | 私達はユーザーポリシーを更新する場合があります。ユーザーポリシーは Github で管理されているため Github 上の Notifications を有効にすることによりこのページに新しいユーザーポリシーを掲載する際に変更を通知します。これらの変更はこのページに掲載された直後に有効になります。通知についての詳細は[こちら](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications)をご覧ください。 46 | 47 | ## 本サービスの透明性 48 | 49 | 本サービスのプログラムはすべて Github 上で公開されています。 50 | 詳細は[こちら](https://github.com/fa0311/vrc_manager)をご覧ください。 51 | 52 | ## お問い合わせ方法 53 | 54 | このユーザーポリシーに関してご質問がある場合は、次のメール アドレスまでご連絡ください。 55 | 56 | yuki@yuki0311.com 57 | -------------------------------------------------------------------------------- /gen.ps1: -------------------------------------------------------------------------------- 1 | cspell ** 2 | python assets/tools/l10n/checker.py 3 | flutter pub run import_sorter:main 4 | flutter gen-l10n -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | # target 'RunnerTests' do 36 | # inherit! :search_paths 37 | # end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 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 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Vrchat Mobile Client 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | vrc_manager 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | $(FLUTTER_BUILD_NUMBER) 27 | LSApplicationQueriesSchemes 28 | 29 | https 30 | 31 | LSRequiresIPhoneOS 32 | 33 | UIApplicationSupportsIndirectInputEvents 34 | 35 | UILaunchStoryboardName 36 | LaunchScreen 37 | UIMainStoryboardFile 38 | Main 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | UISupportedInterfaceOrientations~ipad 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | template-arb-file: app_en.arb 3 | output-localization-file: app_localizations.dart -------------------------------------------------------------------------------- /lib/api/.gitignore: -------------------------------------------------------------------------------- 1 | *.json -------------------------------------------------------------------------------- /lib/api/assets/assets.dart: -------------------------------------------------------------------------------- 1 | class VRChatAssets { 2 | static Uri vrchat = Uri.https("vrchat.com"); 3 | static Uri vrchatScheme = Uri(scheme: "vrchat"); 4 | static Uri assets = Uri.https("assets.vrchat.com"); 5 | 6 | // cspell:disable-next-line 7 | static String apiKey = "JlE5Jldo5Jibnk5O5hTx6XVqsJu4WJ26"; 8 | 9 | static String homeTitle = "Home - VRChat"; 10 | static String loginTitle = "Login - VRChat"; 11 | 12 | static Uri login = VRChatAssets.vrchat.resolve("/home/login"); 13 | static Uri user = VRChatAssets.vrchat.resolve("/home/user/"); 14 | static Uri launch = VRChatAssets.vrchat.resolve("/home/launch"); 15 | static Uri locations = VRChatAssets.vrchat.resolve("/home/locations"); 16 | static Uri groups = VRChatAssets.vrchat.resolve("/home/groups"); 17 | static Uri download = VRChatAssets.vrchat.resolve("/home/download"); 18 | static Uri worlds = VRChatAssets.vrchat.resolve("/home/worlds/"); 19 | static Uri content = VRChatAssets.vrchat.resolve("/home/content"); 20 | static Uri avatars = VRChatAssets.vrchat.resolve("/home/avatars"); 21 | static Uri favoritesWorlds = VRChatAssets.vrchat.resolve("/home/favorites/world/"); 22 | static Uri favoritesAvatars = VRChatAssets.vrchat.resolve("/home/favorites/avatar/"); 23 | // cspell:disable-next-line 24 | static Uri accountLink = VRChatAssets.vrchat.resolve("/home/accountlink"); 25 | // cspell:disable-next-line 26 | static Uri playerModerations = VRChatAssets.vrchat.resolve("/home/playermoderations"); 27 | static Uri messages = VRChatAssets.vrchat.resolve("/home/messages"); 28 | static Uri profile = VRChatAssets.vrchat.resolve("/home/profile"); 29 | static Uri search = VRChatAssets.vrchat.resolve("/home/search/"); 30 | 31 | static Uri defaultPrivateImage = assets.resolve("/www/images/default_private_image.png"); 32 | static Uri defaultBetweenImage = assets.resolve("/www/images/default_between_image.png"); 33 | } 34 | -------------------------------------------------------------------------------- /lib/api/assets/icon.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | enum VRChatExternalServices { 5 | none(color: 0xFF000000, text: "none"), 6 | fivehundredpix(color: 0xFF222222, text: "fivehundredpix"), 7 | bandsintown(color: 0xFF1B8793, text: "bandsintown"), 8 | behance(color: 0xFF007CFF, text: "behance"), 9 | codepen(color: 0xFF151515, text: "codepen"), 10 | dribbble(color: 0xFFea4c89, text: "dribbble"), 11 | dropbox(color: 0xFF1081DE, text: "dropbox"), 12 | email(color: 0xFF7f7f7f, text: "email"), 13 | facebook(color: 0xFF3b5998, text: "facebook"), 14 | flickr(color: 0xFF0063db, text: "flickr"), 15 | foursquare(color: 0xFF0072b1, text: "foursquare"), 16 | github(color: 0xFF4183c4, text: "github"), 17 | googlePlay(color: 0xFF40BBC1, text: "google_play"), 18 | google(color: 0xFFdd4b39, text: "google"), 19 | instagram(color: 0xFF3f729b, text: "instagram"), 20 | itunes(color: 0xFFE049D1, text: "itunes"), 21 | linkedin(color: 0xFF007fb1, text: "linkedin"), 22 | mailto(color: 0xFF7f7f7f, text: "mailto"), 23 | medium(color: 0xFF333332, text: "medium"), 24 | meetup(color: 0xFFE51937, text: "meetup"), 25 | pinterest(color: 0xFFcb2128, text: "pinterest"), 26 | rdio(color: 0xFF0475C5, text: "rdio"), 27 | reddit(color: 0xFFFF4500, text: "reddit"), 28 | rss(color: 0xFFEF8733, text: "rss"), 29 | sharethis(color: 0xFF00BF00, text: "sharethis"), 30 | smugmug(color: 0xFF8cca1e, text: "smugmug"), 31 | snapchat(color: 0xFFFFC91B, text: "snapchat"), 32 | soundcloud(color: 0xFFFF5700, text: "soundcloud"), 33 | spotify(color: 0xFF2EBD59, text: "spotify"), 34 | squarespace(color: 0xFF1C1C1C, text: "squarespace"), 35 | tumblr(color: 0xFF2c4762, text: "tumblr"), 36 | twitch(color: 0xFF6441A5, text: "twitch"), 37 | twitter(color: 0xFF00aced, text: "twitter"), 38 | vevo(color: 0xFFED1A3B, text: "vevo"), 39 | vimeo(color: 0xFF1ab7ea, text: "vimeo"), 40 | vine(color: 0xFF00BF8F, text: "vine"), 41 | vk(color: 0xFF45668e, text: "vk"), 42 | vsco(color: 0xFF83878A, text: "vsco"), 43 | wechat(color: 0xFF00c80f, text: "wechat"), 44 | whatsapp(color: 0xFF25D366, text: "whatsapp"), 45 | yelp(color: 0xFFB90C04, text: "yelp"), 46 | youtube(color: 0xFFff3333, text: "youtube"); 47 | 48 | Color toColor() { 49 | return Color(color); 50 | } 51 | 52 | final int color; 53 | final String text; 54 | const VRChatExternalServices({required this.color, required this.text}); 55 | } 56 | 57 | VRChatExternalServices byVrchatExternalServices(Uri uri) { 58 | for (VRChatExternalServices key in VRChatExternalServices.values) { 59 | if (uri.toString().contains(key.text)) return key; 60 | } 61 | return VRChatExternalServices.none; 62 | } 63 | -------------------------------------------------------------------------------- /lib/api/assets/instance_type.dart: -------------------------------------------------------------------------------- 1 | enum VRChatInstanceType { 2 | public, 3 | hidden, 4 | friends, 5 | private, 6 | group; 7 | } 8 | 9 | enum VRChatInstanceIdOther { 10 | traveling, 11 | private, 12 | offline; 13 | } 14 | -------------------------------------------------------------------------------- /lib/api/assets/lang.dart: -------------------------------------------------------------------------------- 1 | enum VRChatLanguage { 2 | eng(text: "English"), 3 | kor(text: "한국어"), 4 | rus(text: "Русский"), 5 | spa(text: "Español"), 6 | por(text: "Português"), 7 | zho(text: "中文"), 8 | deu(text: "Deutsch"), 9 | jpn(text: "日本語"), 10 | fra(text: "Français"), 11 | swe(text: "Svenska"), 12 | nld(text: "Nederlands"), 13 | pol(text: "Polski"), 14 | dan(text: "Dansk"), 15 | nor(text: "Norsk"), 16 | ita(text: "Italiano"), 17 | tha(text: "ภาษาไทย"), 18 | fin(text: "Suomi"), 19 | hun(text: "Magyar"), 20 | ces(text: "Čeština"), 21 | tur(text: "Türkçe"), 22 | ara(text: "العربية"), 23 | ron(text: "Română"), 24 | vie(text: "Tiếng Việt"), 25 | ase(text: "American Sign Language"), 26 | bfi(text: "British Sign Language"), 27 | dse(text: "Dutch Sign Language"), 28 | fsl(text: "French Sign Language"), 29 | kvk(text: "Korean Sign Language"); 30 | 31 | final String text; 32 | const VRChatLanguage({required this.text}); 33 | } 34 | -------------------------------------------------------------------------------- /lib/api/assets/region.dart: -------------------------------------------------------------------------------- 1 | // Project imports: 2 | import 'package:vrc_manager/api/assets/assets.dart'; 3 | 4 | enum VRChatRegion { 5 | us(text: "/www/images/Region_US.png"), 6 | use(text: "/www/images/Region_US.png"), 7 | eu(text: "/www/images/Region_EU.png"), 8 | jp(text: "/www/images/Region_JP.png"); 9 | 10 | final String text; 11 | Uri toUri() { 12 | return VRChatAssets.assets.resolve(text); 13 | } 14 | 15 | const VRChatRegion({required this.text}); 16 | } 17 | -------------------------------------------------------------------------------- /lib/api/assets/status.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | enum VRChatStatusData { 5 | active(text: "active", color: 0xFF51e57e), 6 | joinMe(text: "join me", color: 0xFF42CAFF), 7 | busy(text: "busy", color: 0xFF5b0b0b), 8 | askMe(text: "ask me", color: 0xFFe88134), 9 | offline(text: "offline", color: 0xFF808080); 10 | 11 | Color toColor() { 12 | return Color(color); 13 | } 14 | 15 | final String text; 16 | final int color; 17 | const VRChatStatusData({required this.text, required this.color}); 18 | } 19 | 20 | VRChatStatusData byVrchatStatusData(String text) { 21 | for (VRChatStatusData status in VRChatStatusData.values) { 22 | if (text == status.text) return status; 23 | } 24 | return VRChatStatusData.offline; 25 | } 26 | -------------------------------------------------------------------------------- /lib/api/data_class_ext.dart: -------------------------------------------------------------------------------- 1 | // Project imports: 2 | import 'package:vrc_manager/api/data_class.dart'; 3 | 4 | class VRChatWorldInstance { 5 | final VRChatWorld _world; 6 | final VRChatInstance _instance; 7 | 8 | VRChatWorld get world => _world; 9 | VRChatInstance get instance => _instance; 10 | 11 | VRChatWorldInstance(this._world, this._instance); 12 | } 13 | 14 | class VRChatUserFriendsStatus { 15 | final VRChatUser _user; 16 | final VRChatFriendStatus _status; 17 | 18 | VRChatUser get user => _user; 19 | VRChatFriendStatus get status => _status; 20 | 21 | VRChatUserFriendsStatus(this._user, this._status); 22 | } 23 | -------------------------------------------------------------------------------- /lib/assets.dart: -------------------------------------------------------------------------------- 1 | class Assets { 2 | static Uri svg = Uri.directory("assets/svg/"); 3 | // cspell:disable-next-line 4 | static String charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; 5 | 6 | static String userAgentBase = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"; 7 | static String userAgent(String version) => '$userAgentBase VRCManager/$version ($repository)'; 8 | 9 | static Uri github = Uri.https("github.com"); 10 | static Uri twitter = Uri.https("twitter.com"); 11 | static Uri googlePlay = Uri.https("play.google.com"); 12 | 13 | static Uri repository = Assets.github.resolve("/fa0311/vrc_manager"); 14 | static Uri issues = Assets.github.resolve("/fa0311/vrc_manager/issues/new/choose"); 15 | static Uri contact = Assets.twitter.resolve("/faa0311"); 16 | static Uri rate = Assets.googlePlay.replace(path: "/store/apps/details", queryParameters: {"id": "com.yuki0311.vrc_manager"}); 17 | static Uri release = Assets.github.resolve("/fa0311/vrc_manager/releases"); 18 | static Uri report = Assets.github.replace(path: "/fa0311/vrc_manager/issues/new", queryParameters: {"template": "redirected-from-app.yml"}); 19 | static Uri userPolicy = Assets.github.resolve("/fa0311/vrc_manager/blob/master/docs/user_policies/ja.md"); 20 | } 21 | -------------------------------------------------------------------------------- /lib/assets/api/get.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | 3 | // Project imports: 4 | import 'package:vrc_manager/api/assets/instance_type.dart'; 5 | import 'package:vrc_manager/api/data_class.dart'; 6 | import 'package:vrc_manager/api/main.dart'; 7 | import 'package:vrc_manager/main.dart'; 8 | import 'package:vrc_manager/scenes/setting/logger.dart'; 9 | 10 | Future getWorld({ 11 | required VRChatAPI vrchatLoginSession, 12 | required VRChatFriends user, 13 | required Map locationMap, 14 | }) async { 15 | try { 16 | String wid = user.location.split(":")[0]; 17 | if (VRChatInstanceIdOther.values.any((id) => id.name == user.location) || locationMap.containsKey(wid)) return; 18 | locationMap[wid] = null; 19 | locationMap[wid] = await vrchatLoginSession.worlds(wid); 20 | } catch (e, trace) { 21 | logger.e(getMessage(e), error: e, stackTrace: trace); 22 | } 23 | } 24 | 25 | Future getWorldFromFavorite({ 26 | required VRChatAPI vrchatLoginSession, 27 | required VRChatFavoriteGroup favoriteGroup, 28 | required Map locationMap, 29 | }) async { 30 | try { 31 | String wid = favoriteGroup.id; 32 | if (VRChatInstanceIdOther.values.any((id) => id.name == wid) || locationMap.containsKey(wid)) return; 33 | locationMap[wid] = null; 34 | locationMap[wid] = await vrchatLoginSession.worlds(wid); 35 | } catch (e, trace) { 36 | logger.e(getMessage(e), error: e, stackTrace: trace); 37 | } 38 | } 39 | 40 | Future getInstance({ 41 | required VRChatAPI vrchatLoginSession, 42 | required VRChatFriends user, 43 | required Map instanceMap, 44 | }) async { 45 | try { 46 | if (VRChatInstanceIdOther.values.any((id) => id.name == user.location) || instanceMap.containsKey(user.location)) return; 47 | instanceMap[user.location] = null; 48 | instanceMap[user.location] = await vrchatLoginSession.instances(user.location); 49 | } catch (e, trace) { 50 | logger.e(getMessage(e), error: e, stackTrace: trace); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/assets/api/post.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | 3 | // Project imports: 4 | import 'package:vrc_manager/api/data_class.dart'; 5 | import 'package:vrc_manager/api/main.dart'; 6 | import 'package:vrc_manager/main.dart'; 7 | 8 | Future delete({ 9 | required VRChatAPI vrchatLoginSession, 10 | required VRChatFavoriteWorld world, 11 | required List favoriteWorld, 12 | }) async { 13 | try { 14 | await vrchatLoginSession.deleteFavorites(world.favoriteId); 15 | favoriteWorld.remove(world); 16 | } catch (e) { 17 | logger.e(e); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/assets/conditions.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/lib/assets/conditions.dart -------------------------------------------------------------------------------- /lib/assets/date.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | import 'package:intl/intl.dart'; 7 | 8 | String generalDateDifference(BuildContext context, DateTime time) { 9 | final Duration difference = DateTime.now().difference(time); 10 | if (difference.inDays > 7) return DateFormat.yMMMMd(AppLocalizations.of(context)!.localeName).format(time); 11 | if (difference.inDays > 0) return AppLocalizations.of(context)!.dateFormat1(difference.inDays, difference.inHours % 24); 12 | if (difference.inHours > 0) return AppLocalizations.of(context)!.dateFormat2(difference.inHours, difference.inMinutes % 60); 13 | if (difference.inMinutes > 0) return AppLocalizations.of(context)!.dateFormat3(difference.inMinutes); 14 | return AppLocalizations.of(context)!.dateFormat4(difference.inSeconds); 15 | } 16 | -------------------------------------------------------------------------------- /lib/assets/flutter/text_stream.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:io'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | // Package imports: 7 | import 'package:flutter_sharing_intent/flutter_sharing_intent.dart'; 8 | import 'package:flutter_sharing_intent/model/sharing_file.dart'; 9 | // Project imports: 10 | import 'package:vrc_manager/assets/flutter/url_parser.dart'; 11 | 12 | textStream({required BuildContext context, required bool forceExternal}) async { 13 | if (!Platform.isAndroid && !Platform.isIOS) return; 14 | await for (final file in FlutterSharingIntent().getMediaStream()) { 15 | for (final SharedFile sharedFile in file) { 16 | if (sharedFile.type == SharedMediaType.TEXT || sharedFile.type == SharedMediaType.URL) { 17 | String text = sharedFile.value!; 18 | Widget? value = await urlParser(url: Uri.parse(text), forceExternal: forceExternal); 19 | if (value == null) return; 20 | Navigator.of(context).popUntil((route) => route.isFirst); 21 | Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => value)); 22 | } 23 | } 24 | } 25 | } 26 | 27 | Future initTest() async { 28 | final sharedFile = await FlutterSharingIntent().getInitialSharing(); 29 | for (final SharedFile sharedFile in sharedFile) { 30 | if (sharedFile.type == SharedMediaType.TEXT || sharedFile.type == SharedMediaType.URL) { 31 | String text = sharedFile.value!; 32 | return text; 33 | } 34 | } 35 | return null; 36 | } 37 | -------------------------------------------------------------------------------- /lib/assets/flutter/url_parser.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Project imports: 5 | import 'package:vrc_manager/scenes/core/splash.dart'; 6 | import 'package:vrc_manager/scenes/sub/user.dart'; 7 | import 'package:vrc_manager/scenes/sub/world.dart'; 8 | import 'package:vrc_manager/widgets/share.dart'; 9 | 10 | Future urlParser({required Uri url, required bool forceExternal}) async { 11 | final List path = url.path.split("/"); 12 | final Map queryParameters = url.queryParameters; 13 | if (path.length < 2) { 14 | return null; 15 | } else if (path[2] == "launch") { 16 | return VRChatMobileSplash(child: VRChatMobileWorld(worldId: queryParameters["worldId"] ?? "")); 17 | } else if (path[2] == "world") { 18 | return VRChatMobileSplash(child: VRChatMobileWorld(worldId: path[3])); 19 | } else if (path[2] == "user") { 20 | return VRChatMobileSplash(child: VRChatMobileUser(userId: path[3])); 21 | } else { 22 | return await openInBrowser(url: url, forceExternal: forceExternal); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/assets/license.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:package_info_plus/package_info_plus.dart'; 6 | 7 | Future showLicense(BuildContext context) async { 8 | final PackageInfo info = await PackageInfo.fromPlatform(); 9 | 10 | showLicensePage( 11 | context: context, 12 | applicationName: info.appName, 13 | applicationVersion: info.version, 14 | applicationLegalese: '2022 yuki', 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /lib/assets/session.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | // Package imports: 6 | import 'package:http/http.dart' as http; 7 | 8 | class Session { 9 | Map headers = { 10 | 'cookie': '', 11 | 'user-agent': '', 12 | }; 13 | 14 | Future get(Uri url) async { 15 | http.Response response = await http.get(url, headers: headers); 16 | if (response.statusCode != 200) throw HttpException(response.body, uri: url); 17 | final dynamic body = json.decode(response.body); 18 | updateCookie(response); 19 | return body; 20 | } 21 | 22 | Future basic(Uri url, String username, String password) async { 23 | final headersAuth = Map.from(headers) 24 | ..addAll( 25 | { 26 | 'authorization': 'Basic ${base64Encode(utf8.encode('$username:$password'))}', 27 | }, 28 | ); 29 | http.Response response = await http.get(url, headers: headersAuth); 30 | if (response.statusCode != 200) throw HttpException(response.body, uri: url); 31 | final dynamic body = json.decode(response.body); 32 | updateCookie(response); 33 | return body; 34 | } 35 | 36 | Future post(Uri url, [Object? data]) async { 37 | http.Response response = await http.post(url, body: data ?? {}, headers: headers); 38 | if (response.statusCode != 200) throw HttpException(response.body, uri: url); 39 | final dynamic body = json.decode(response.body); 40 | updateCookie(response); 41 | return body; 42 | } 43 | 44 | Future put(Uri url, [Object? data]) async { 45 | http.Response response = await http.put(url, body: data ?? {}, headers: headers); 46 | if (response.statusCode != 200) throw HttpException(response.body, uri: url); 47 | final dynamic body = json.decode(response.body); 48 | updateCookie(response); 49 | return body; 50 | } 51 | 52 | Future delete(Uri url, [Object? data]) async { 53 | http.Response response = await http.delete(url, body: data ?? {}, headers: headers); 54 | if (response.statusCode != 200) throw HttpException(response.body, uri: url); 55 | final dynamic body = json.decode(response.body); 56 | updateCookie(response); 57 | return body; 58 | } 59 | 60 | updateCookie(http.Response response) { 61 | String? rawCookie = response.headers['set-cookie']; 62 | if (rawCookie != null) { 63 | Map cookieMap = decodeCookie(headers['cookie'] ?? ""); 64 | cookieMap.addAll(decodeCookie(rawCookie)); 65 | headers['cookie'] = encodeCookie(cookieMap); 66 | } 67 | } 68 | 69 | String encodeCookie(Map cookieMap) { 70 | String rawCookie = ""; 71 | for (String key in cookieMap.keys) { 72 | rawCookie += "$key=${cookieMap[key]}; "; 73 | } 74 | return rawCookie; 75 | } 76 | 77 | Map decodeCookie(String rawCookie) { 78 | Map cookieMap = {}; 79 | for (String row in rawCookie.split(';')) { 80 | List data = row.trim().replaceFirst('HttpOnly,', '').split('='); 81 | if (!row.contains('=')) continue; 82 | if (["Max-Age", "Path", "Expires"].contains(data[0])) continue; 83 | cookieMap[data[0]] = data[1]; 84 | } 85 | return cookieMap; 86 | } 87 | 88 | String getCookie() { 89 | return headers['cookie'] ?? ""; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/assets/sort/users.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:convert'; 3 | 4 | // Project imports: 5 | import 'package:vrc_manager/api/assets/instance_type.dart'; 6 | import 'package:vrc_manager/api/data_class.dart'; 7 | import 'package:vrc_manager/storage/grid_modal.dart'; 8 | import 'package:vrc_manager/widgets/grid_modal/config.dart'; 9 | 10 | List sortUsers(GridConfigNotifier config, List userList) { 11 | switch (config.sortMode) { 12 | case SortMode.name: 13 | sortByNameFromUser(userList); 14 | break; 15 | case SortMode.lastLogin: 16 | sortByLastLoginFromUser(userList); 17 | break; 18 | case SortMode.friendsInInstance: 19 | sortByLocationMapFromUser(userList); 20 | break; 21 | default: 22 | break; 23 | } 24 | if (config.descending) { 25 | return userList.reversed.toList(); 26 | } else { 27 | return userList; 28 | } 29 | } 30 | 31 | Map numberOfFriendsInLocation(List userList) { 32 | Map inLocation = {}; 33 | for (VRChatUser user in userList) { 34 | String location = user.location; 35 | inLocation[location] = (inLocation[location] ?? 0) + 1; 36 | } 37 | return inLocation; 38 | } 39 | 40 | sortByLocationMapFromUser(List userList) { 41 | Map inLocation = numberOfFriendsInLocation(userList); 42 | userList.sort((userA, userB) { 43 | String locationA = userA.location; 44 | String locationB = userB.location; 45 | if (locationA == locationB) return 0; 46 | if (locationA == VRChatInstanceIdOther.offline.name) return 1; 47 | if (locationB == VRChatInstanceIdOther.offline.name) return -1; 48 | if (locationA == VRChatInstanceIdOther.private.name) return 1; 49 | if (locationB == VRChatInstanceIdOther.private.name) return -1; 50 | if (locationA == VRChatInstanceIdOther.traveling.name) return 1; 51 | if (locationB == VRChatInstanceIdOther.traveling.name) return -1; 52 | if (inLocation[locationA]! > inLocation[locationB]!) return -1; 53 | if (inLocation[locationA]! < inLocation[locationB]!) return 1; 54 | 55 | List userBytesA = utf8.encode(locationA); 56 | List userBytesB = utf8.encode(locationB); 57 | 58 | for (int i = 0; i < userBytesA.length && i < userBytesB.length; i++) { 59 | if (userBytesA[i] < userBytesB[i]) return -1; 60 | if (userBytesA[i] > userBytesB[i]) return 1; 61 | } 62 | if (userBytesA.length < userBytesB.length) return -1; 63 | if (userBytesA.length > userBytesB.length) return 1; 64 | return 0; 65 | }); 66 | } 67 | 68 | sortByNameFromUser(List userList) { 69 | userList.sort((userA, userB) { 70 | List userBytesA = utf8.encode(userA.displayName.toLowerCase()); 71 | List userBytesB = utf8.encode(userB.displayName.toLowerCase()); 72 | for (int i = 0; i < userBytesA.length && i < userBytesB.length; i++) { 73 | if (userBytesA[i] < userBytesB[i]) return -1; 74 | if (userBytesA[i] > userBytesB[i]) return 1; 75 | } 76 | if (userBytesA.length < userBytesB.length) return -1; 77 | if (userBytesA.length > userBytesB.length) return 1; 78 | return 0; 79 | }); 80 | } 81 | 82 | sortByLastLoginFromUser(List userList) { 83 | userList.sort((userA, userB) { 84 | if (userA.lastLogin == null) return 1; 85 | if (userB.lastLogin == null) return -1; 86 | if (userA.lastLogin!.millisecondsSinceEpoch > userB.lastLogin!.millisecondsSinceEpoch) return -1; 87 | if (userA.lastLogin!.millisecondsSinceEpoch < userB.lastLogin!.millisecondsSinceEpoch) return 1; 88 | return 0; 89 | }); 90 | } 91 | -------------------------------------------------------------------------------- /lib/assets/sort/worlds.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:convert'; 3 | 4 | // Project imports: 5 | import 'package:vrc_manager/api/data_class.dart'; 6 | import 'package:vrc_manager/storage/grid_modal.dart'; 7 | import 'package:vrc_manager/widgets/grid_modal/config.dart'; 8 | 9 | List sortWorlds(GridConfigNotifier config, List worldList) { 10 | switch (config.sortMode) { 11 | case SortMode.name: 12 | sortByNameFromWorlds(worldList); 13 | break; 14 | case SortMode.updatedDate: 15 | sortByUpdatedDateFromWorlds(worldList); 16 | break; 17 | case SortMode.labsPublicationDate: 18 | sortByLabsPublicationDateFromWorlds(worldList); 19 | break; 20 | case SortMode.heat: 21 | sortByHeatFavoriteFromWorlds(worldList); 22 | break; 23 | case SortMode.capacity: 24 | sortByCapacityFromWorlds(worldList); 25 | break; 26 | case SortMode.occupants: 27 | sortByOccupantsFromWorlds(worldList); 28 | break; 29 | default: 30 | break; 31 | } 32 | if (config.descending) { 33 | return worldList.reversed.toList(); 34 | } else { 35 | return worldList; 36 | } 37 | } 38 | 39 | sortByNameFromWorlds(List worldList) { 40 | worldList.sort((userA, userB) { 41 | List userBytesA = utf8.encode(userA.name); 42 | List userBytesB = utf8.encode(userB.name); 43 | for (int i = 0; i < userBytesA.length && i < userBytesB.length; i++) { 44 | if (userBytesA[i] < userBytesB[i]) return -1; 45 | if (userBytesA[i] > userBytesB[i]) return 1; 46 | } 47 | if (userBytesA.length < userBytesB.length) return -1; 48 | if (userBytesA.length > userBytesB.length) return 1; 49 | return 0; 50 | }); 51 | } 52 | 53 | sortByUpdatedDateFromWorlds(List worldList) { 54 | worldList.sort((userA, userB) { 55 | if (userA.updatedAt.millisecondsSinceEpoch > userB.updatedAt.millisecondsSinceEpoch) return -1; 56 | if (userA.updatedAt.millisecondsSinceEpoch < userB.updatedAt.millisecondsSinceEpoch) return 1; 57 | return 0; 58 | }); 59 | } 60 | 61 | sortByLabsPublicationDateFromWorlds(List worldList) { 62 | worldList.sort((userA, userB) { 63 | if (userA.labsPublicationDate == null) return 1; 64 | if (userB.labsPublicationDate == null) return -1; 65 | if (userA.labsPublicationDate!.millisecondsSinceEpoch > userB.labsPublicationDate!.millisecondsSinceEpoch) return -1; 66 | if (userA.labsPublicationDate!.millisecondsSinceEpoch < userB.labsPublicationDate!.millisecondsSinceEpoch) return 1; 67 | return 0; 68 | }); 69 | } 70 | 71 | sortByHeatFavoriteFromWorlds(List worldList) { 72 | worldList.sort((userA, userB) { 73 | if (userA.heat > userB.heat) return -1; 74 | if (userA.heat < userB.heat) return 1; 75 | return 0; 76 | }); 77 | } 78 | 79 | sortByCapacityFromWorlds(List worldList) { 80 | worldList.sort((userA, userB) { 81 | if (userA.capacity > userB.capacity) return -1; 82 | if (userA.capacity < userB.capacity) return 1; 83 | return 0; 84 | }); 85 | } 86 | 87 | sortByOccupantsFromWorlds(List worldList) { 88 | worldList.sort((userA, userB) { 89 | if (userA.occupants > userB.occupants) return -1; 90 | if (userA.occupants < userB.occupants) return 1; 91 | return 0; 92 | }); 93 | } 94 | -------------------------------------------------------------------------------- /lib/assets/storage.dart: -------------------------------------------------------------------------------- 1 | // Package imports: 2 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 3 | import 'package:shared_preferences/shared_preferences.dart'; 4 | 5 | // Project imports: 6 | import 'package:vrc_manager/main.dart'; 7 | import 'package:vrc_manager/scenes/setting/logger.dart'; 8 | 9 | Future getLoginSession(String key, String id) async { 10 | try { 11 | const FlutterSecureStorage storage = FlutterSecureStorage(); 12 | return await storage.read(key: "$key$id"); 13 | } catch (e, trace) { 14 | logger.w(getMessage(e), error: e, stackTrace: trace); 15 | return null; 16 | } 17 | } 18 | 19 | Future setLoginSession(String key, String value, String id) async { 20 | try { 21 | const FlutterSecureStorage storage = FlutterSecureStorage(); 22 | return await storage.write(key: "$key$id", value: value); 23 | } catch (e, trace) { 24 | logger.w(getMessage(e), error: e, stackTrace: trace); 25 | } 26 | } 27 | 28 | Future removeLoginSession(String key, String id) async { 29 | try { 30 | const FlutterSecureStorage storage = FlutterSecureStorage(); 31 | return await storage.delete(key: "$key$id"); 32 | } catch (e, trace) { 33 | logger.w(getMessage(e), error: e, stackTrace: trace); 34 | } 35 | } 36 | 37 | Future getStorage(String key, {String id = ""}) async { 38 | try { 39 | final SharedPreferences storage = await SharedPreferences.getInstance(); 40 | return storage.getString("$key$id"); 41 | } catch (e, trace) { 42 | logger.w(getMessage(e), error: e, stackTrace: trace); 43 | return null; 44 | } 45 | } 46 | 47 | Future setStorage(String key, String value, {String id = ""}) async { 48 | try { 49 | final SharedPreferences storage = await SharedPreferences.getInstance(); 50 | return await storage.setString("$key$id", value); 51 | } catch (e, trace) { 52 | logger.w(getMessage(e), error: e, stackTrace: trace); 53 | return false; 54 | } 55 | } 56 | 57 | Future removeStorage(String key, {String id = ""}) async { 58 | try { 59 | final SharedPreferences storage = await SharedPreferences.getInstance(); 60 | return await storage.remove("$key$id"); 61 | } catch (e, trace) { 62 | logger.w(getMessage(e), error: e, stackTrace: trace); 63 | return false; 64 | } 65 | } 66 | 67 | Future> getStorageList(String key, {String id = ""}) async { 68 | try { 69 | final SharedPreferences storage = await SharedPreferences.getInstance(); 70 | return storage.getStringList("$key$id") ?? []; 71 | } catch (e, trace) { 72 | logger.w(getMessage(e), error: e, stackTrace: trace); 73 | return []; 74 | } 75 | } 76 | 77 | Future setStorageList(String key, List value, {String id = ""}) async { 78 | try { 79 | final SharedPreferences storage = await SharedPreferences.getInstance(); 80 | return await storage.setStringList("$key$id", value); 81 | } catch (e, trace) { 82 | logger.w(getMessage(e), error: e, stackTrace: trace); 83 | return false; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/assets/theme/enum.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/assets/theme/true_black.dart'; 9 | 10 | enum ThemeBrightness { 11 | light, 12 | dark, 13 | black, 14 | trueBlack, 15 | highContrastLight, 16 | highContrastDark; 17 | 18 | String toLocalization(BuildContext context) { 19 | switch (this) { 20 | case ThemeBrightness.light: 21 | return AppLocalizations.of(context)!.lightTheme; 22 | case ThemeBrightness.dark: 23 | return AppLocalizations.of(context)!.darkTheme; 24 | case ThemeBrightness.black: 25 | return AppLocalizations.of(context)!.blackTheme; 26 | case ThemeBrightness.trueBlack: 27 | return AppLocalizations.of(context)!.trueBlackTheme; 28 | case ThemeBrightness.highContrastLight: 29 | return AppLocalizations.of(context)!.highContrastLightTheme; 30 | case ThemeBrightness.highContrastDark: 31 | return AppLocalizations.of(context)!.highContrastDarkTheme; 32 | } 33 | } 34 | 35 | ThemeData toTheme() { 36 | switch (this) { 37 | case ThemeBrightness.light: 38 | return ThemeData(brightness: Brightness.light); 39 | case ThemeBrightness.dark: 40 | return ThemeData(brightness: Brightness.dark, primarySwatch: Colors.grey); 41 | case ThemeBrightness.black: 42 | return blackTheme(); 43 | case ThemeBrightness.trueBlack: 44 | return trueBlackTheme(); 45 | case ThemeBrightness.highContrastLight: 46 | return highContrastLightTheme(); 47 | case ThemeBrightness.highContrastDark: 48 | return highContrastDarkTheme(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/assets/theme/true_black.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | ThemeData blackTheme() { 5 | Color black = const Color.fromARGB(255, 0, 0, 0); 6 | Color grey = const Color.fromARGB(255, 20, 20, 20); 7 | return ThemeData( 8 | brightness: Brightness.dark, 9 | canvasColor: grey, 10 | cardColor: grey, 11 | dialogBackgroundColor: black, 12 | primaryColor: black, 13 | primaryColorDark: black, 14 | primaryColorLight: black, 15 | scaffoldBackgroundColor: black, 16 | secondaryHeaderColor: black, 17 | appBarTheme: AppBarTheme(color: grey), 18 | bottomAppBarTheme: BottomAppBarTheme(color: black), 19 | colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey, brightness: Brightness.dark).copyWith(background: black), 20 | ); 21 | } 22 | 23 | ThemeData trueBlackTheme() { 24 | Color black = const Color.fromARGB(255, 0, 0, 0); 25 | return ThemeData( 26 | brightness: Brightness.dark, 27 | canvasColor: black, 28 | cardColor: black, 29 | dialogBackgroundColor: black, 30 | highlightColor: const Color.fromARGB(150, 20, 20, 20), 31 | primaryColor: black, 32 | primaryColorDark: black, 33 | primaryColorLight: black, 34 | scaffoldBackgroundColor: black, 35 | secondaryHeaderColor: black, 36 | splashColor: const Color.fromARGB(255, 20, 20, 20), 37 | appBarTheme: AppBarTheme(color: black), 38 | bottomAppBarTheme: BottomAppBarTheme(color: black), 39 | colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey, brightness: Brightness.dark).copyWith(background: black), 40 | ); 41 | } 42 | 43 | ThemeData highContrastLightTheme() { 44 | return ThemeData( 45 | brightness: Brightness.light, 46 | colorScheme: const ColorScheme.highContrastLight(), 47 | ); 48 | } 49 | 50 | ThemeData highContrastDarkTheme() { 51 | return ThemeData( 52 | brightness: Brightness.dark, 53 | colorScheme: const ColorScheme.highContrastDark(), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /lib/l10n/code.dart: -------------------------------------------------------------------------------- 1 | enum LanguageCode { 2 | en("English"), 3 | ja("日本語"), 4 | es("Español"), 5 | pt("Português"), 6 | ru("Pусский"), 7 | th("ไทย"), 8 | zh("简体中文"); 9 | 10 | final String text; 11 | const LanguageCode(this.text); 12 | } 13 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/material.dart'; 4 | // Package imports: 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | import 'package:flutter_localizations/flutter_localizations.dart'; 7 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 8 | import 'package:logger/logger.dart'; 9 | // Project imports: 10 | import 'package:vrc_manager/scenes/core/splash.dart'; 11 | import 'package:vrc_manager/scenes/setting/logger.dart'; 12 | import 'package:vrc_manager/storage/accessibility.dart'; 13 | 14 | ConsoleOutputExt loggerOutput = ConsoleOutputExt(); 15 | Logger logger = LoggerExt( 16 | filter: ProductionFilter(), 17 | printer: PrettyPrinter(methodCount: 8), 18 | output: loggerOutput, 19 | level: kDebugMode ? Level.trace : Level.warning, 20 | ); 21 | 22 | main() { 23 | runApp(const ProviderScope(child: VRChatMobile())); 24 | } 25 | 26 | class VRChatMobile extends ConsumerWidget { 27 | const VRChatMobile({super.key}); 28 | 29 | @override 30 | Widget build(BuildContext context, WidgetRef ref) { 31 | AccessibilityConfigNotifier accessibilityConfig = ref.watch(accessibilityConfigProvider); 32 | 33 | return MaterialApp( 34 | title: 'VRChat Mobile Client', 35 | debugShowCheckedModeBanner: false, 36 | localizationsDelegates: const [ 37 | AppLocalizations.delegate, 38 | GlobalMaterialLocalizations.delegate, 39 | GlobalWidgetsLocalizations.delegate, 40 | GlobalCupertinoLocalizations.delegate, 41 | ], 42 | supportedLocales: AppLocalizations.supportedLocales, 43 | locale: Locale(accessibilityConfig.languageCode.name, ''), 44 | theme: accessibilityConfig.themeBrightness.toTheme(), 45 | darkTheme: accessibilityConfig.darkThemeBrightness.toTheme(), 46 | home: const VRChatMobileSplash(), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/scenes/main/friend_request.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | // Package imports: 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | // Project imports: 6 | import 'package:vrc_manager/api/data_class.dart'; 7 | import 'package:vrc_manager/api/main.dart'; 8 | import 'package:vrc_manager/main.dart'; 9 | import 'package:vrc_manager/scenes/core/splash.dart'; 10 | import 'package:vrc_manager/scenes/setting/logger.dart'; 11 | import 'package:vrc_manager/widgets/grid_modal/config.dart'; 12 | import 'package:vrc_manager/widgets/grid_view/extraction/render_grid/user.dart'; 13 | import 'package:vrc_manager/widgets/loading.dart'; 14 | import 'package:vrc_manager/widgets/scroll.dart'; 15 | 16 | class VRChatMobileFriendRequestData { 17 | List userList; 18 | VRChatMobileFriendRequestData({required this.userList}); 19 | } 20 | 21 | final vrchatMobileFriendsRequestProvider = FutureProvider((ref) async { 22 | VRChatAPI vrchatLoginSession = VRChatAPI( 23 | cookie: ref.watch(accountConfigProvider).loggedAccount?.cookie ?? "", 24 | userAgent: ref.watch(accountConfigProvider).userAgent, 25 | logger: logger, 26 | ); 27 | List futureList = []; 28 | List userList = []; 29 | int len; 30 | try { 31 | do { 32 | int offset = futureList.length; 33 | List notify = await vrchatLoginSession.notifications(type: "friendRequest", offset: offset); 34 | for (VRChatNotifications requestUser in notify) { 35 | futureList.add(vrchatLoginSession.users(requestUser.senderUserId).then((VRChatUser user) { 36 | userList.add(user); 37 | }).catchError((e, trace) { 38 | logger.e(getMessage(e), error: e, stackTrace: trace); 39 | })); 40 | } 41 | len = notify.length; 42 | } while (len > 0); 43 | } catch (e, trace) { 44 | logger.e(getMessage(e), error: e, stackTrace: trace); 45 | } 46 | await Future.wait(futureList); 47 | return VRChatMobileFriendRequestData(userList: userList); 48 | }); 49 | 50 | class VRChatMobileFriendRequest extends ConsumerWidget { 51 | const VRChatMobileFriendRequest({super.key}); 52 | 53 | @override 54 | Widget build(BuildContext context, WidgetRef ref) { 55 | AsyncValue data = ref.watch(vrchatMobileFriendsRequestProvider); 56 | VRChatFriendStatus status = VRChatFriendStatus(isFriend: false, incomingRequest: true, outgoingRequest: false); 57 | 58 | return data.when( 59 | loading: () => const Loading(), 60 | error: (e, trace) { 61 | logger.w(getMessage(e), error: e, stackTrace: trace); 62 | return ScrollWidget( 63 | onRefresh: () => ref.refresh(vrchatMobileFriendsRequestProvider.future), 64 | child: ErrorPage(loggerReport: ref.read(loggerReportProvider)), 65 | ); 66 | }, 67 | data: (data) => ScrollWidget( 68 | onRefresh: () => ref.refresh(vrchatMobileFriendsRequestProvider.future), 69 | child: ExtractionUser(id: GridModalConfigType.favoriteWorlds, userList: data.userList, status: status), 70 | ), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/scenes/setting/accessibility.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:io'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | // Package imports: 7 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 8 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 9 | // Project imports: 10 | import 'package:vrc_manager/storage/accessibility.dart'; 11 | import 'package:vrc_manager/widgets/config_modal/locale.dart'; 12 | import 'package:vrc_manager/widgets/config_modal/theme.dart'; 13 | import 'package:vrc_manager/widgets/modal.dart'; 14 | 15 | class VRChatMobileSettingsAccessibility extends ConsumerWidget { 16 | const VRChatMobileSettingsAccessibility({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context, WidgetRef ref) { 20 | AccessibilityConfigNotifier accessibilityConfig = ref.watch(accessibilityConfigProvider); 21 | return Scaffold( 22 | appBar: AppBar( 23 | title: Text(AppLocalizations.of(context)!.setting), 24 | ), 25 | body: SafeArea( 26 | child: SingleChildScrollView( 27 | child: Container( 28 | padding: const EdgeInsets.only(top: 10), 29 | child: Column( 30 | children: [ 31 | ListTile( 32 | title: Text(AppLocalizations.of(context)!.language), 33 | subtitle: Text(accessibilityConfig.languageCode.text), 34 | onTap: () => showModalBottomSheetStatelessWidget( 35 | context: context, 36 | builder: () => const LocaleModal(), 37 | ), 38 | ), 39 | ListTile( 40 | title: Text(AppLocalizations.of(context)!.deviceLightTheme), 41 | subtitle: Text(accessibilityConfig.themeBrightness.toLocalization(context)), 42 | onTap: () => showModalBottomSheetStatelessWidget( 43 | context: context, 44 | builder: () => const ThemeBrightnessModal(dark: false), 45 | ), 46 | ), 47 | ListTile( 48 | title: Text(AppLocalizations.of(context)!.deviceDarkTheme), 49 | subtitle: Text(accessibilityConfig.darkThemeBrightness.toLocalization(context)), 50 | onTap: () => showModalBottomSheetStatelessWidget( 51 | context: context, 52 | builder: () => const ThemeBrightnessModal(dark: true), 53 | ), 54 | ), 55 | if (!Platform.isWindows) 56 | SwitchListTile( 57 | value: accessibilityConfig.forceExternalBrowser, 58 | title: Text(AppLocalizations.of(context)!.forceExternalBrowser), 59 | subtitle: Text(AppLocalizations.of(context)!.forceExternalBrowserDetails), 60 | onChanged: (bool e) => accessibilityConfig.setForceExternalBrowser(e), 61 | ), 62 | SwitchListTile( 63 | value: accessibilityConfig.debugMode, 64 | title: Text(AppLocalizations.of(context)!.debugMode), 65 | onChanged: (bool e) => accessibilityConfig.setDebugMode(e), 66 | ) 67 | ], 68 | ), 69 | ), 70 | ), 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/scenes/setting/token.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | 8 | // Project imports: 9 | import 'package:vrc_manager/api/data_class.dart'; 10 | import 'package:vrc_manager/api/main.dart'; 11 | import 'package:vrc_manager/main.dart'; 12 | import 'package:vrc_manager/scenes/core/splash.dart'; 13 | import 'package:vrc_manager/scenes/setting/logger.dart'; 14 | import 'package:vrc_manager/widgets/future/button.dart'; 15 | 16 | final tokenControllerProvider = StateProvider.autoDispose((ref) { 17 | return TextEditingController(text: ref.watch(accountConfigProvider).loggedAccount?.cookie ?? ""); 18 | }); 19 | 20 | class VRChatMobileTokenSetting extends ConsumerWidget { 21 | final bool offline; 22 | const VRChatMobileTokenSetting({super.key, this.offline = true}); 23 | 24 | @override 25 | Widget build(BuildContext context, WidgetRef ref) { 26 | TextEditingController tokenController = ref.watch(tokenControllerProvider); 27 | 28 | return Scaffold( 29 | appBar: AppBar( 30 | title: Text(AppLocalizations.of(context)!.token), 31 | ), 32 | body: SafeArea( 33 | child: SingleChildScrollView( 34 | child: Container( 35 | padding: const EdgeInsets.all(32.0), 36 | child: Column( 37 | children: [ 38 | TextField( 39 | controller: tokenController, 40 | maxLines: null, 41 | decoration: InputDecoration(labelText: AppLocalizations.of(context)!.cookie), 42 | ), 43 | FutureButton( 44 | type: ButtonType.elevatedButton, 45 | onPressed: () async { 46 | await ref.read(accountConfigProvider).loggedAccount!.setCookie(tokenController.text); 47 | await ref.read(accountConfigProvider).login(ref.read(accountConfigProvider).loggedAccount!); 48 | ScaffoldMessenger.of(context).showSnackBar( 49 | SnackBar(content: Text(AppLocalizations.of(context)!.saved)), 50 | ); 51 | }, 52 | child: Text(AppLocalizations.of(context)!.save), 53 | ), 54 | FutureButton( 55 | type: ButtonType.elevatedButton, 56 | child: Text(AppLocalizations.of(context)!.login), 57 | onPressed: () async { 58 | await VRChatAPI( 59 | cookie: tokenController.text, 60 | userAgent: ref.watch(accountConfigProvider).userAgent, 61 | logger: logger, 62 | ).user().then((VRChatUserSelfOverload response) { 63 | ScaffoldMessenger.of(context).showSnackBar( 64 | SnackBar(content: Text(AppLocalizations.of(context)!.success)), 65 | ); 66 | }).catchError((e, trace) { 67 | logger.w(getMessage(e), error: e, stackTrace: trace); 68 | ScaffoldMessenger.of(context).showSnackBar( 69 | SnackBar(content: Text(AppLocalizations.of(context)!.failed)), 70 | ); 71 | }); 72 | }, 73 | ), 74 | ], 75 | ), 76 | ), 77 | ), 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/scenes/sub/json_viewer.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:convert'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | // Package imports: 7 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 8 | import 'package:flutter_json_viewer/flutter_json_viewer.dart'; 9 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 10 | // Project imports: 11 | import 'package:vrc_manager/widgets/modal.dart'; 12 | import 'package:vrc_manager/widgets/modal/share.dart'; 13 | 14 | class VRChatMobileJsonViewer extends ConsumerWidget { 15 | final dynamic content; 16 | const VRChatMobileJsonViewer({super.key, required this.content}); 17 | 18 | @override 19 | Widget build(BuildContext context, WidgetRef ref) { 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: Text(AppLocalizations.of(context)!.jsonViewer), 23 | actions: [ 24 | IconButton( 25 | icon: const Icon(Icons.share), 26 | onPressed: () { 27 | showModalBottomSheetStatelessWidget( 28 | context: context, 29 | builder: () => SingleChildScrollView( 30 | child: Column(children: [CopyListTileWidget(text: jsonEncode(content))]), 31 | ), 32 | ); 33 | }, 34 | ) 35 | ], 36 | ), 37 | body: Container( 38 | color: Theme.of(context).colorScheme.onPrimary, 39 | child: SafeArea( 40 | child: ListView( 41 | children: [ 42 | JsonViewer(content), 43 | ], 44 | ), 45 | ), 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/scenes/sub/world.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | // Package imports: 4 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | // Project imports: 7 | import 'package:vrc_manager/api/data_class.dart'; 8 | import 'package:vrc_manager/api/main.dart'; 9 | import 'package:vrc_manager/main.dart'; 10 | import 'package:vrc_manager/scenes/core/splash.dart'; 11 | import 'package:vrc_manager/scenes/setting/logger.dart'; 12 | import 'package:vrc_manager/widgets/drawer.dart'; 13 | import 'package:vrc_manager/widgets/loading.dart'; 14 | import 'package:vrc_manager/widgets/modal.dart'; 15 | import 'package:vrc_manager/widgets/modal/world.dart'; 16 | import 'package:vrc_manager/widgets/scroll.dart'; 17 | import 'package:vrc_manager/widgets/world.dart'; 18 | 19 | class VRChatMobileWorldData { 20 | VRChatWorld world; 21 | 22 | VRChatMobileWorldData({ 23 | required this.world, 24 | }); 25 | } 26 | 27 | final vrchatMobileWorldProvider = FutureProvider.family((ref, worldId) async { 28 | VRChatAPI vrchatLoginSession = VRChatAPI( 29 | cookie: ref.watch(accountConfigProvider).loggedAccount?.cookie ?? "", 30 | userAgent: ref.watch(accountConfigProvider).userAgent, 31 | logger: logger, 32 | ); 33 | 34 | late VRChatWorld world; 35 | await vrchatLoginSession.worlds(worldId).then((value) => world = value); 36 | return VRChatMobileWorldData(world: world); 37 | }); 38 | 39 | class VRChatMobileWorld extends ConsumerWidget { 40 | const VRChatMobileWorld({super.key, required this.worldId}); 41 | final String worldId; 42 | 43 | @override 44 | Widget build(BuildContext context, WidgetRef ref) { 45 | AsyncValue data = ref.watch(vrchatMobileWorldProvider(worldId)); 46 | 47 | return Scaffold( 48 | appBar: AppBar( 49 | title: Text(AppLocalizations.of(context)!.world), 50 | actions: data.when( 51 | loading: () => null, 52 | error: (e, trace) { 53 | logger.w(getMessage(e), error: e, stackTrace: trace); 54 | return null; 55 | }, 56 | data: (data) => [ 57 | IconButton( 58 | icon: const Icon(Icons.more_vert), 59 | onPressed: () { 60 | showModalBottomSheetStatelessWidget( 61 | context: context, 62 | builder: () => WorldDetailsModalBottom(world: data.world), 63 | ); 64 | }, 65 | ) 66 | ], 67 | ), 68 | ), 69 | drawer: Navigator.of(context).canPop() ? null : const NormalDrawer(), 70 | body: SafeArea( 71 | child: Consumer( 72 | builder: (context, ref, child) { 73 | return data.when( 74 | loading: () => const Loading(), 75 | error: (e, trace) { 76 | logger.w(getMessage(e), error: e, stackTrace: trace); 77 | return ScrollWidget( 78 | onRefresh: () => ref.refresh((vrchatMobileWorldProvider(worldId).future)), 79 | child: ErrorPage(loggerReport: ref.read(loggerReportProvider)), 80 | ); 81 | }, 82 | data: (data) => ScrollWidget( 83 | onRefresh: () => ref.refresh((vrchatMobileWorldProvider(worldId).future)), 84 | child: Container( 85 | padding: const EdgeInsets.only(top: 10, right: 30, left: 30), 86 | child: WorldProfile(world: data.world), 87 | ), 88 | ), 89 | ); 90 | }, 91 | ), 92 | ), 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/storage/accessibility.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/assets/storage.dart'; 9 | import 'package:vrc_manager/assets/theme/enum.dart'; 10 | import 'package:vrc_manager/l10n/code.dart'; 11 | 12 | final accessibilityConfigProvider = ChangeNotifierProvider((ref) => AccessibilityConfigNotifier()); 13 | 14 | class AccessibilityConfigNotifier extends ChangeNotifier { 15 | LanguageCode languageCode = LanguageCode.en; 16 | ThemeBrightness themeBrightness = ThemeBrightness.light; 17 | ThemeBrightness darkThemeBrightness = ThemeBrightness.dark; 18 | bool forceExternalBrowser = false; 19 | bool debugMode = false; 20 | 21 | AccessibilityConfigNotifier() { 22 | Future.wait([ 23 | getStorage("language_code").then((String? value) => languageCode = LanguageCode.values.byName(value ?? languageCode.name)), 24 | getStorage("theme_brightness").then((String? value) => themeBrightness = ThemeBrightness.values.byName(value ?? themeBrightness.name)), 25 | getStorage("dark_theme_brightness").then((String? value) => darkThemeBrightness = ThemeBrightness.values.byName(value ?? darkThemeBrightness.name)), 26 | getStorage("force_external_browser").then((String? value) => forceExternalBrowser = (value == "true")), 27 | getStorage("debug_mode").then((String? value) => debugMode = (value == "true")), 28 | ]).whenComplete(() => notifyListeners()); 29 | } 30 | 31 | Future setLanguageCode(LanguageCode value) async { 32 | languageCode = value; 33 | notifyListeners(); 34 | return await setStorage("language_code", value.name); 35 | } 36 | 37 | Future setThemeBrightness(ThemeBrightness value) async { 38 | themeBrightness = value; 39 | notifyListeners(); 40 | return await setStorage("theme_brightness", themeBrightness.name); 41 | } 42 | 43 | Future setDarkThemeBrightness(ThemeBrightness value) async { 44 | darkThemeBrightness = value; 45 | notifyListeners(); 46 | return await setStorage("dark_theme_brightness", darkThemeBrightness.name); 47 | } 48 | 49 | Future setForceExternalBrowser(bool value) async { 50 | forceExternalBrowser = value; 51 | notifyListeners(); 52 | return await setStorage("force_external_browser", forceExternalBrowser ? "true" : "false"); 53 | } 54 | 55 | Future setDebugMode(bool value) async { 56 | debugMode = value; 57 | notifyListeners(); 58 | return await setStorage("debug_mode", debugMode ? "true" : "false"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/storage/grid_modal.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/assets/storage.dart'; 9 | import 'package:vrc_manager/widgets/grid_modal/config.dart'; 10 | 11 | final gridConfigProvider = ChangeNotifierProvider.family((ref, id) => GridConfigNotifier(id: id)); 12 | 13 | class GridConfigNotifier extends ChangeNotifier { 14 | GridModalConfigType id; 15 | SortMode sortMode = SortMode.normal; 16 | DisplayMode displayMode = DisplayMode.normal; 17 | bool descending = false; 18 | bool joinable = false; 19 | bool worldDetails = false; 20 | bool removeButton = false; 21 | 22 | GridConfigNotifier({required this.id}) { 23 | Future.wait([ 24 | getStorage("sort", id: id.name).then((String? value) => sortMode = SortMode.values.byName(value ?? sortMode.name)), 25 | getStorage("display_mode", id: id.name).then((String? value) => displayMode = DisplayMode.values.byName(value ?? displayMode.name)), 26 | getStorage("descending", id: id.name).then((String? value) => descending = (value == "true")), 27 | getStorage("joinable", id: id.name).then((String? value) => joinable = (value == "true")), 28 | getStorage("world_details", id: id.name).then((String? value) => worldDetails = (value == "true")), 29 | getStorage("remove_button", id: id.name).then((String? value) => removeButton = (value == "true")), 30 | ]).whenComplete(() => notifyListeners()); 31 | } 32 | 33 | Future setSort(SortMode value) async { 34 | sortMode = value; 35 | notifyListeners(); 36 | return await setStorage("sort", value.name, id: id.name); 37 | } 38 | 39 | Future setDisplayMode(DisplayMode value) async { 40 | displayMode = value; 41 | notifyListeners(); 42 | return await setStorage("display_mode", displayMode.name, id: id.name); 43 | } 44 | 45 | Future setDescending(bool value) async { 46 | descending = value; 47 | notifyListeners(); 48 | return await setStorage("descending", descending ? "true" : "false", id: id.name); 49 | } 50 | 51 | Future setJoinable(bool value) async { 52 | joinable = value; 53 | notifyListeners(); 54 | return await setStorage("joinable", joinable ? "true" : "false", id: id.name); 55 | } 56 | 57 | Future setWorldDetails(bool value) async { 58 | worldDetails = value; 59 | notifyListeners(); 60 | return await setStorage("world_details", worldDetails ? "true" : "false", id: id.name); 61 | } 62 | 63 | Future setRemoveButton(bool value) async { 64 | removeButton = value; 65 | notifyListeners(); 66 | return await setStorage("remove_button", removeButton ? "true" : "false", id: id.name); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/storage/user_policy.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/assets/storage.dart'; 9 | 10 | final userPolicyConfigProvider = ChangeNotifierProvider((ref) => UserPolicyConfigNotifier()); 11 | 12 | class UserPolicyConfigNotifier extends ChangeNotifier { 13 | bool agree = true; 14 | bool isFirst = true; 15 | 16 | Future init() async { 17 | isFirst = false; 18 | await getStorage("agreed_user_policy").then((String? value) => agree = (value == "true")); 19 | } 20 | 21 | Future setAgree(bool value) async { 22 | agree = value; 23 | notifyListeners(); 24 | return await setStorage("agreed_user_policy", agree ? "true" : "false"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/widgets/config_modal/locale.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 6 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 | 8 | // Project imports: 9 | import 'package:vrc_manager/l10n/code.dart'; 10 | import 'package:vrc_manager/storage/accessibility.dart'; 11 | 12 | class LocaleModal extends ConsumerWidget { 13 | const LocaleModal({super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | final accessibilityConfig = ref.watch(accessibilityConfigProvider); 18 | 19 | return SingleChildScrollView( 20 | child: Column( 21 | children: [ 22 | for (LanguageCode value in LanguageCode.values) 23 | ListTile( 24 | title: Text(value.text), 25 | trailing: accessibilityConfig.languageCode == value ? const Icon(Icons.check) : null, 26 | subtitle: Text( 27 | AppLocalizations.of(context)!.translatorDetails( 28 | lookupAppLocalizations(Locale(value.name, "")).contributor, 29 | ), 30 | ), 31 | onTap: () { 32 | accessibilityConfig.setLanguageCode(value); 33 | }, 34 | ), 35 | ], 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/widgets/config_modal/theme.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/assets/theme/enum.dart'; 9 | import 'package:vrc_manager/storage/accessibility.dart'; 10 | 11 | class ThemeBrightnessModal extends ConsumerWidget { 12 | final bool dark; 13 | 14 | const ThemeBrightnessModal({super.key, required this.dark}); 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | AccessibilityConfigNotifier accessibilityConfig = ref.watch(accessibilityConfigProvider); 19 | 20 | return SingleChildScrollView( 21 | child: Column( 22 | children: [ 23 | for (ThemeBrightness value in ThemeBrightness.values) 24 | ListTile( 25 | title: Text(value.toLocalization(context)), 26 | trailing: (dark ? accessibilityConfig.darkThemeBrightness : accessibilityConfig.themeBrightness) == value ? const Icon(Icons.check) : null, 27 | onTap: () => dark ? accessibilityConfig.setDarkThemeBrightness(value) : accessibilityConfig.setThemeBrightness(value), 28 | ), 29 | ], 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/widgets/future/button.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/scheduler.dart'; 4 | 5 | enum ButtonType { 6 | textButton, 7 | outlinedButton, 8 | elevatedButton; 9 | } 10 | 11 | class FutureButton extends StatefulWidget { 12 | final Widget child; 13 | final Future Function() onPressed; 14 | final ButtonType type; 15 | const FutureButton({super.key, required this.child, required this.onPressed, this.type = ButtonType.textButton}); 16 | 17 | @override 18 | FutureButtonState createState() => FutureButtonState(); 19 | } 20 | 21 | class FutureButtonState extends State { 22 | late bool state; 23 | late Size size; 24 | late GlobalKey key; 25 | 26 | @override 27 | void initState() { 28 | state = false; 29 | key = GlobalKey(); 30 | super.initState(); 31 | } 32 | 33 | Future onPressed() async { 34 | try { 35 | setState(() => state = true); 36 | await widget.onPressed(); 37 | } finally { 38 | setState(() => state = false); 39 | } 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | if (state) { 45 | return SizedBox( 46 | width: size.width, 47 | height: size.height, 48 | child: Padding( 49 | padding: EdgeInsets.symmetric(vertical: (size.height - 20) / 2, horizontal: (size.width - 20) / 2), 50 | child: const CircularProgressIndicator(), 51 | ), 52 | ); 53 | } 54 | 55 | SchedulerBinding.instance.addPostFrameCallback((_) { 56 | size = key.currentContext!.size!; 57 | }); 58 | 59 | switch (widget.type) { 60 | case ButtonType.textButton: 61 | return TextButton( 62 | key: key, 63 | onPressed: onPressed, 64 | child: widget.child, 65 | ); 66 | case ButtonType.outlinedButton: 67 | return OutlinedButton( 68 | key: key, 69 | onPressed: onPressed, 70 | child: widget.child, 71 | ); 72 | case ButtonType.elevatedButton: 73 | return ElevatedButton( 74 | key: key, 75 | onPressed: onPressed, 76 | child: widget.child, 77 | ); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/widgets/future/tile.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | class FutureTile extends StatefulWidget { 5 | final Widget? title; 6 | final Widget? subtitle; 7 | final Widget? leading; 8 | final Widget? trailing; 9 | 10 | final Future Function() onTap; 11 | const FutureTile({super.key, this.title, this.subtitle, this.leading, this.trailing, required this.onTap}); 12 | 13 | @override 14 | FutureTileState createState() => FutureTileState(); 15 | } 16 | 17 | class FutureTileState extends State { 18 | late bool state; 19 | late GlobalKey key; 20 | 21 | @override 22 | void initState() { 23 | state = false; 24 | key = GlobalKey(); 25 | super.initState(); 26 | } 27 | 28 | Future onTap() async { 29 | try { 30 | setState(() => state = true); 31 | await widget.onTap(); 32 | } finally { 33 | setState(() => state = false); 34 | } 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return ListTile( 40 | key: key, 41 | onTap: onTap, 42 | title: widget.title, 43 | subtitle: widget.subtitle, 44 | leading: widget.leading, 45 | trailing: state 46 | ? const Padding( 47 | padding: EdgeInsets.only(right: 2, top: 2), 48 | child: SizedBox(width: 20, height: 20, child: CircularProgressIndicator()), 49 | ) 50 | : widget.trailing, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/widgets/grid_view/extraction/consumer.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/widgets.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | // Project imports: 8 | import 'package:vrc_manager/storage/grid_modal.dart'; 9 | import 'package:vrc_manager/widgets/grid_modal/config.dart'; 10 | import 'package:vrc_manager/widgets/grid_view/template/template.dart'; 11 | 12 | abstract class ConsumerGridWidget extends ConsumerWidget { 13 | final GridModalConfigType id; 14 | const ConsumerGridWidget({super.key, required this.id}); 15 | 16 | List normal(BuildContext context, WidgetRef ref, GridConfigNotifier config, ConsumerGridStyle style); 17 | 18 | List simple(BuildContext context, WidgetRef ref, GridConfigNotifier config, ConsumerGridStyle style); 19 | 20 | List textOnly(BuildContext context, WidgetRef ref, GridConfigNotifier config, ConsumerGridStyle style); 21 | 22 | @override 23 | Widget build(BuildContext context, WidgetRef ref) { 24 | GridConfigNotifier config = ref.watch(gridConfigProvider(id)); 25 | 26 | switch (config.displayMode) { 27 | case DisplayMode.normal: 28 | final style = ConsumerGridStyle( 29 | title: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 30 | details: const TextStyle(fontSize: 15), 31 | ); 32 | return RenderGrid( 33 | width: 600, 34 | height: config.worldDetails ? 235 : 130, 35 | children: normal(context, ref, config, style), 36 | ); 37 | case DisplayMode.simple: 38 | final style = ConsumerGridStyle( 39 | title: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), 40 | details: const TextStyle(fontSize: 10), 41 | ); 42 | return RenderGrid( 43 | width: 320, 44 | height: config.worldDetails ? 119 : 64, 45 | children: simple(context, ref, config, style), 46 | ); 47 | case DisplayMode.textOnly: 48 | final style = ConsumerGridStyle( 49 | title: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), 50 | details: const TextStyle(fontSize: 10), 51 | ); 52 | return RenderGrid( 53 | width: 200, 54 | height: config.worldDetails ? 27 : 20, 55 | children: textOnly(context, ref, config, style), 56 | ); 57 | } 58 | } 59 | } 60 | 61 | class ConsumerGridStyle { 62 | final TextStyle title; 63 | final TextStyle details; 64 | ConsumerGridStyle({required this.title, required this.details}); 65 | } 66 | -------------------------------------------------------------------------------- /lib/widgets/loading.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | class Loading extends ConsumerWidget { 8 | const Loading({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | return Container( 13 | alignment: Alignment.topCenter, 14 | child: const Padding( 15 | padding: EdgeInsets.only(top: 40), 16 | child: CircularProgressIndicator(), 17 | ), 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/widgets/modal.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | Future showModalBottomSheetConsumer({ 8 | required BuildContext context, 9 | required ConsumerBuilder builder, 10 | }) { 11 | assert(debugCheckHasMediaQuery(context)); 12 | assert(debugCheckHasMaterialLocalizations(context)); 13 | 14 | return showModalBottomSheet( 15 | context: context, 16 | shape: const RoundedRectangleBorder( 17 | borderRadius: BorderRadius.vertical(top: Radius.circular(15)), 18 | ), 19 | builder: (BuildContext context) { 20 | return Consumer( 21 | builder: builder, 22 | ); 23 | }, 24 | ); 25 | } 26 | 27 | Future showModalBottomSheetStatelessWidget({ 28 | required BuildContext context, 29 | required Widget Function() builder, 30 | }) { 31 | assert(debugCheckHasMediaQuery(context)); 32 | assert(debugCheckHasMaterialLocalizations(context)); 33 | 34 | return showModalBottomSheet( 35 | context: context, 36 | shape: const RoundedRectangleBorder( 37 | borderRadius: BorderRadius.vertical(top: Radius.circular(15)), 38 | ), 39 | builder: (BuildContext context) => builder(), 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /lib/widgets/region.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | // Package imports: 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | // Project imports: 7 | import 'package:vrc_manager/api/assets/region.dart'; 8 | import 'package:vrc_manager/scenes/core/splash.dart'; 9 | 10 | class RegionWidget extends ConsumerWidget { 11 | final VRChatRegion region; 12 | final double size; 13 | const RegionWidget({required this.region, this.size = 15, super.key}); 14 | 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | return SizedBox( 18 | width: size, 19 | height: size, 20 | child: CachedNetworkImage( 21 | imageUrl: region.toUri().toString(), 22 | fit: BoxFit.fitWidth, 23 | progressIndicatorBuilder: (context, url, downloadProgress) => const CircularProgressIndicator(), 24 | errorWidget: (context, url, error) => const Icon(Icons.error), 25 | httpHeaders: { 26 | "user-agent": ref.watch(accountConfigProvider).userAgent, 27 | }, 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/widgets/scroll.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 6 | 7 | class ScrollWidget extends ConsumerWidget { 8 | final RefreshCallback onRefresh; 9 | final ScrollNotificationPredicate notificationPredicate; 10 | final Widget child; 11 | 12 | const ScrollWidget({ 13 | super.key, 14 | required this.onRefresh, 15 | required this.child, 16 | this.notificationPredicate = defaultScrollNotificationPredicate, 17 | }); 18 | 19 | @override 20 | Widget build(BuildContext context, WidgetRef ref) { 21 | return RefreshIndicator( 22 | onRefresh: onRefresh, 23 | notificationPredicate: notificationPredicate, 24 | child: SizedBox( 25 | height: double.infinity, 26 | width: double.infinity, 27 | child: SingleChildScrollView( 28 | keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, 29 | physics: const AlwaysScrollableScrollPhysics(), 30 | child: child, 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/widgets/share.dart: -------------------------------------------------------------------------------- 1 | // Dart imports: 2 | import 'dart:io'; 3 | 4 | // Flutter imports: 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/services.dart'; 7 | 8 | // Package imports: 9 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 10 | import 'package:fluttertoast/fluttertoast.dart'; 11 | import 'package:url_launcher/url_launcher.dart'; 12 | 13 | // Project imports: 14 | import 'package:vrc_manager/api/assets/assets.dart'; 15 | import 'package:vrc_manager/scenes/web/web_view.dart'; 16 | 17 | Future openInBrowser({required Uri url, required bool forceExternal}) async { 18 | if (Platform.isAndroid || Platform.isIOS) { 19 | if (!forceExternal || url.host == VRChatAssets.vrchat.host) { 20 | return VRChatMobileWebView(initUrl: url); 21 | } else { 22 | if (await canLaunchUrl(url)) { 23 | await launchUrl(url, mode: LaunchMode.externalApplication); 24 | } 25 | } 26 | } else { 27 | if (await canLaunchUrl(url)) { 28 | await launchUrl(url); 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | Future copyToClipboard(BuildContext context, String text) async { 35 | final ClipboardData data = ClipboardData(text: text); 36 | await Clipboard.setData(data); 37 | if (Platform.isAndroid || Platform.isIOS) { 38 | Fluttertoast.showToast(msg: AppLocalizations.of(context)!.copied); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/widgets/status.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | // Package imports: 4 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 | // Project imports: 6 | import 'package:vrc_manager/api/assets/status.dart'; 7 | 8 | // Project imports: 9 | class StatusWidget extends ConsumerWidget { 10 | final VRChatStatusData status; 11 | final double diameter; 12 | const StatusWidget({required this.status, this.diameter = 20, super.key}); 13 | 14 | @override 15 | Widget build(BuildContext context, WidgetRef ref) { 16 | return Container( 17 | width: diameter, 18 | height: diameter, 19 | decoration: BoxDecoration( 20 | shape: BoxShape.circle, 21 | color: status.toColor(), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/widgets/world.dart: -------------------------------------------------------------------------------- 1 | // Flutter imports: 2 | import 'package:flutter/material.dart'; 3 | 4 | // Package imports: 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 7 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 8 | 9 | // Project imports: 10 | import 'package:vrc_manager/api/data_class.dart'; 11 | import 'package:vrc_manager/assets/date.dart'; 12 | import 'package:vrc_manager/scenes/core/splash.dart'; 13 | 14 | class OnTheWebsite extends ConsumerWidget { 15 | final bool half; 16 | const OnTheWebsite({super.key, this.half = false}); 17 | 18 | @override 19 | Widget build(BuildContext context, WidgetRef ref) { 20 | return Container( 21 | alignment: Alignment.center, 22 | height: half ? 50 : 100, 23 | child: Text(AppLocalizations.of(context)!.onTheWebsite, style: TextStyle(fontSize: half ? 10 : 15)), 24 | ); 25 | } 26 | } 27 | 28 | class WorldProfile extends ConsumerWidget { 29 | final VRChatWorld world; 30 | const WorldProfile({super.key, required this.world}); 31 | 32 | @override 33 | Widget build(BuildContext context, WidgetRef ref) { 34 | return Column( 35 | children: [ 36 | SizedBox( 37 | height: 250, 38 | child: CachedNetworkImage( 39 | imageUrl: world.imageUrl, 40 | fit: BoxFit.fitWidth, 41 | progressIndicatorBuilder: (context, url, downloadProgress) => const SizedBox( 42 | width: 250.0, 43 | child: Padding( 44 | padding: EdgeInsets.all(30), 45 | child: CircularProgressIndicator( 46 | strokeWidth: 10, 47 | ), 48 | ), 49 | ), 50 | errorWidget: (context, url, error) => const SizedBox( 51 | width: 250.0, 52 | child: Icon(Icons.error), 53 | ), 54 | httpHeaders: { 55 | "user-agent": ref.watch(accountConfigProvider).userAgent, 56 | }, 57 | ), 58 | ), 59 | Text(world.name, 60 | style: const TextStyle( 61 | fontWeight: FontWeight.bold, 62 | fontSize: 20, 63 | )), 64 | ConstrainedBox( 65 | constraints: const BoxConstraints(maxHeight: 200), 66 | child: SingleChildScrollView(child: Text(world.description ?? "")), 67 | ), 68 | Text( 69 | AppLocalizations.of(context)!.occupants(world.occupants), 70 | ), 71 | Text( 72 | AppLocalizations.of(context)!.privateOccupants(world.privateOccupants), 73 | ), 74 | Text( 75 | AppLocalizations.of(context)!.favorites(world.favorites), 76 | ), 77 | Text( 78 | AppLocalizations.of(context)!.createdAt( 79 | generalDateDifference(context, world.createdAt), 80 | ), 81 | ), 82 | Text( 83 | AppLocalizations.of(context)!.updatedAt( 84 | generalDateDifference(context, world.updatedAt), 85 | ), 86 | ), 87 | ], 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: vrc_manager 2 | description: VRCManager 3 | 4 | publish_to: "none" 5 | version: 5.1.0+25 6 | 7 | environment: 8 | sdk: ">=2.17.0 <4.0.0" 9 | dependencies: 10 | cached_network_image: ^3.2.1 11 | cupertino_icons: ^1.0.4 12 | device_info_plus: ^10.1.2 13 | domain_verification_manager: ^1.0.0 14 | flutter: 15 | sdk: flutter 16 | flutter_inappwebview: ^6.1.0+1 17 | flutter_json_viewer: ^1.0.1 18 | flutter_localizations: 19 | sdk: flutter 20 | flutter_riverpod: ^2.0.2 21 | flutter_secure_storage: ^9.2.2 22 | flutter_svg: ^2.0.6 23 | fluttertoast: ^8.0.9 24 | http: ^1.1.0 25 | intl: ^0.19.0 26 | logger: ^2.0.1 27 | package_info_plus: ^8.0.2 28 | flutter_sharing_intent: ^1.1.1 29 | share_plus: ^10.0.2 30 | shared_preferences: ^2.0.15 31 | url_launcher: ^6.1.2 32 | webview_flutter: <4.0.0 33 | 34 | dev_dependencies: 35 | flutter_launcher_icons: ^0.14.0 36 | flutter_lints: ^4.0.0 37 | flutter_test: 38 | sdk: flutter 39 | import_sorter: ^4.6.0 40 | 41 | flutter: 42 | assets: 43 | - assets/svg/ 44 | generate: true 45 | uses-material-design: true 46 | 47 | flutter_icons: 48 | android: true 49 | ios: true 50 | remove_alpha_ios: true 51 | image_path: "assets/img/icon.png" 52 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/build.ps1: -------------------------------------------------------------------------------- 1 | flutter build windows 2 | Start-Process "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Z:\Project\flutter\vrc_manager\windows\setup.iss" -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void RegisterPlugins(flutter::PluginRegistry* registry) { 15 | FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); 17 | FlutterSecureStorageWindowsPluginRegisterWithRegistrar( 18 | registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); 19 | SharePlusWindowsPluginCApiRegisterWithRegistrar( 20 | registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); 21 | UrlLauncherWindowsRegisterWithRegistrar( 22 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 23 | } 24 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | flutter_inappwebview_windows 7 | flutter_secure_storage_windows 8 | share_plus 9 | url_launcher_windows 10 | ) 11 | 12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 13 | ) 14 | 15 | set(PLUGIN_BUNDLED_LIBRARIES) 16 | 17 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 19 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 22 | endforeach(plugin) 23 | 24 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 25 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 27 | endforeach(ffi_plugin) 28 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.yuki0311" "\0" 93 | VALUE "FileDescription", "vrc_manager" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "vrc_manager" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.yuki0311. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "vrc_manager.exe" "\0" 98 | VALUE "ProductName", "vrc_manager" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"vrc_manager", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fa0311/vrc_manager/8f0a11a84c646804c2a68a1b6d0754d617f2b00f/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | unsigned int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length == 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/setup.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "VRCManager" 5 | #define MyAppVersion "5.0.3" 6 | #define MyAppPublisher "yuki" 7 | #define MyAppURL "https://yuki0311.com/" 8 | #define MyAppExeName "vrc_manager.exe" 9 | #define MyAppAssocName MyAppName + " File" 10 | #define MyAppAssocExt ".myp" 11 | #define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt 12 | 13 | [Setup] 14 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 15 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 16 | AppId={{1B55B29A-0DD9-4CEB-AEC3-B40F2CD877B9} 17 | AppName={#MyAppName} 18 | AppVersion={#MyAppVersion} 19 | ;AppVerName={#MyAppName} {#MyAppVersion} 20 | AppPublisher={#MyAppPublisher} 21 | AppPublisherURL={#MyAppURL} 22 | AppSupportURL={#MyAppURL} 23 | AppUpdatesURL={#MyAppURL} 24 | DefaultDirName={autopf}\{#MyAppName} 25 | ChangesAssociations=yes 26 | DisableProgramGroupPage=yes 27 | LicenseFile=Z:\Project\flutter\vrc_manager\LICENSE 28 | ; Uncomment the following line to run in non administrative install mode (install for current user only.) 29 | ;PrivilegesRequired=lowest 30 | OutputDir=Z:\Project\flutter\vrc_manager\build\windows 31 | OutputBaseFilename=VRChatMC-Setup 32 | SetupIconFile=Z:\Project\flutter\vrc_manager\windows\runner\resources\app_icon.ico 33 | Compression=lzma 34 | SolidCompression=yes 35 | WizardStyle=modern 36 | 37 | [Languages] 38 | Name: "english"; MessagesFile: "compiler:Default.isl" 39 | Name: "japanese"; MessagesFile: "compiler:Languages\Japanese.isl" 40 | 41 | [Tasks] 42 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 43 | 44 | [Files] 45 | Source: "Z:\Project\flutter\vrc_manager\build\windows\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs 46 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 47 | 48 | [Registry] 49 | Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue 50 | Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey 51 | Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0" 52 | Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1""" 53 | Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".myp"; ValueData: "" 54 | 55 | [Icons] 56 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 57 | Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 58 | 59 | [Run] 60 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent 61 | 62 | --------------------------------------------------------------------------------