├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── debug-build.yml │ ├── ktlint.yml │ └── release-build.yml ├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── kotlinc.xml ├── migrations.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── kotlin │ │ └── dev │ │ └── chungjungsoo │ │ └── gptmobile │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_gpt_mobile-playstore.png │ ├── kotlin │ │ └── dev │ │ │ └── chungjungsoo │ │ │ └── gptmobile │ │ │ ├── data │ │ │ ├── ModelConstants.kt │ │ │ ├── database │ │ │ │ ├── ChatDatabase.kt │ │ │ │ ├── dao │ │ │ │ │ ├── ChatRoomDao.kt │ │ │ │ │ └── MessageDao.kt │ │ │ │ └── entity │ │ │ │ │ ├── ChatRoom.kt │ │ │ │ │ └── Message.kt │ │ │ ├── datastore │ │ │ │ ├── SettingDataSource.kt │ │ │ │ └── SettingDataSourceImpl.kt │ │ │ ├── dto │ │ │ │ ├── APIModel.kt │ │ │ │ ├── ApiState.kt │ │ │ │ ├── Platform.kt │ │ │ │ ├── ThemeSetting.kt │ │ │ │ └── anthropic │ │ │ │ │ ├── common │ │ │ │ │ ├── ContentType.kt │ │ │ │ │ ├── ImageContent.kt │ │ │ │ │ ├── ImageSource.kt │ │ │ │ │ ├── ImageSourceType.kt │ │ │ │ │ ├── MediaType.kt │ │ │ │ │ ├── MessageContent.kt │ │ │ │ │ ├── MessageRole.kt │ │ │ │ │ └── TextContent.kt │ │ │ │ │ ├── request │ │ │ │ │ ├── InputMessage.kt │ │ │ │ │ ├── MessageRequest.kt │ │ │ │ │ └── RequestMetadata.kt │ │ │ │ │ └── response │ │ │ │ │ ├── ContentBlock.kt │ │ │ │ │ ├── ContentBlockType.kt │ │ │ │ │ ├── ContentDeltaResponseChunk.kt │ │ │ │ │ ├── ContentStartResponseChunk.kt │ │ │ │ │ ├── ContentStopResponseChunk.kt │ │ │ │ │ ├── ErrorDetail.kt │ │ │ │ │ ├── ErrorResponseChunk.kt │ │ │ │ │ ├── EventType.kt │ │ │ │ │ ├── MessageDeltaResponseChunk.kt │ │ │ │ │ ├── MessageResponse.kt │ │ │ │ │ ├── MessageResponseChunk.kt │ │ │ │ │ ├── MessageStartResponseChunk.kt │ │ │ │ │ ├── MessageStopResponseChunk.kt │ │ │ │ │ ├── PingResponseChunk.kt │ │ │ │ │ ├── StopReason.kt │ │ │ │ │ ├── StopReasonDelta.kt │ │ │ │ │ ├── Usage.kt │ │ │ │ │ └── UsageDelta.kt │ │ │ ├── model │ │ │ │ ├── ApiType.kt │ │ │ │ ├── DynamicTheme.kt │ │ │ │ └── ThemeMode.kt │ │ │ ├── network │ │ │ │ ├── AnthropicAPI.kt │ │ │ │ ├── AnthropicAPIImpl.kt │ │ │ │ └── NetworkClient.kt │ │ │ └── repository │ │ │ │ ├── ChatRepository.kt │ │ │ │ ├── ChatRepositoryImpl.kt │ │ │ │ ├── SettingRepository.kt │ │ │ │ └── SettingRepositoryImpl.kt │ │ │ ├── di │ │ │ ├── ChatRepositoryModule.kt │ │ │ ├── DataStoreModule.kt │ │ │ ├── DatabaseModule.kt │ │ │ ├── NetworkModule.kt │ │ │ ├── SettingDataSourceModule.kt │ │ │ └── SettingRepositoryModule.kt │ │ │ ├── presentation │ │ │ ├── GPTMobileApp.kt │ │ │ ├── common │ │ │ │ ├── NavigationGraph.kt │ │ │ │ ├── PlatformCheckBoxItem.kt │ │ │ │ ├── PrimaryLongButton.kt │ │ │ │ ├── RadioItem.kt │ │ │ │ ├── Route.kt │ │ │ │ ├── SettingItem.kt │ │ │ │ ├── ThemeSettingProvider.kt │ │ │ │ ├── ThemeViewModel.kt │ │ │ │ └── TokenInputField.kt │ │ │ ├── icons │ │ │ │ ├── Done.kt │ │ │ │ └── GptMobileStartScreen.kt │ │ │ ├── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── ui │ │ │ │ ├── chat │ │ │ │ ├── ChatBubble.kt │ │ │ │ ├── ChatScreen.kt │ │ │ │ └── ChatViewModel.kt │ │ │ │ ├── home │ │ │ │ ├── HomeScreen.kt │ │ │ │ └── HomeViewModel.kt │ │ │ │ ├── main │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainViewModel.kt │ │ │ │ ├── setting │ │ │ │ ├── AboutScreen.kt │ │ │ │ ├── LicenseScreen.kt │ │ │ │ ├── PlatformSettingDialogs.kt │ │ │ │ ├── PlatformSettingScreen.kt │ │ │ │ ├── SettingScreen.kt │ │ │ │ └── SettingViewModel.kt │ │ │ │ ├── setup │ │ │ │ ├── SelectModelScreen.kt │ │ │ │ ├── SelectPlatformScreen.kt │ │ │ │ ├── SetupAPIUrlScreen.kt │ │ │ │ ├── SetupAppBar.kt │ │ │ │ ├── SetupCompleteScreen.kt │ │ │ │ ├── SetupViewModel.kt │ │ │ │ └── TokenInputScreen.kt │ │ │ │ └── startscreen │ │ │ │ └── StartScreen.kt │ │ │ └── util │ │ │ ├── DefaultHashMap.kt │ │ │ ├── MapStringResources.kt │ │ │ ├── PinnedExitUntilCollapsedScrollBehavior.kt │ │ │ ├── ScrollStateSaver.kt │ │ │ ├── StateFlowExtensions.kt │ │ │ └── Strings.kt │ └── res │ │ ├── drawable │ │ ├── ic_bug_report.xml │ │ ├── ic_chart.xml │ │ ├── ic_copy.xml │ │ ├── ic_f_droid.xml │ │ ├── ic_feedback.xml │ │ ├── ic_github.xml │ │ ├── ic_gpt_mobile_foreground.xml │ │ ├── ic_gpt_mobile_monochrome_foreground.xml │ │ ├── ic_info.xml │ │ ├── ic_instructions.xml │ │ ├── ic_key.xml │ │ ├── ic_license.xml │ │ ├── ic_link.xml │ │ ├── ic_model.xml │ │ ├── ic_play_store.xml │ │ ├── ic_round_arrow_right.xml │ │ ├── ic_rounded_chat.xml │ │ ├── ic_send.xml │ │ ├── ic_temperature.xml │ │ └── splash_icon_inset.xml │ │ ├── mipmap-anydpi │ │ └── ic_gpt_mobile.xml │ │ ├── resources.properties │ │ ├── values-ko-rKR │ │ └── strings.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values │ │ ├── ic_gpt_mobile_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── kotlin │ └── dev │ └── chungjungsoo │ └── gptmobile │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── logo.png └── screenshots.png ├── metadata └── en-US │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ └── 8.png │ ├── short_description.txt │ └── title.txt └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{kt,kts}] 2 | ktlint_code_style = android_studio 3 | end_of_line = lf 4 | ij_kotlin_allow_trailing_comma = false 5 | ij_kotlin_allow_trailing_comma_on_call_site = false 6 | ij_kotlin_imports_layout = * 7 | ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.** 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = unset 12 | ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 13 | ktlint_code_style = android_studio 14 | ktlint_function_naming_ignore_when_annotated_with = [unset] 15 | ktlint_function_signature_body_expression_wrapping = default 16 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 17 | ktlint_ignore_back_ticked_identifier = false 18 | max_line_length = off 19 | ktlint_function_naming_ignore_when_annotated_with=Composable 20 | compose_allowed_state_holder_names = .*ViewModel,.*Presenter,.*Component 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Taewan-P 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots or Screen Recording** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/workflows/debug-build.yml: -------------------------------------------------------------------------------- 1 | name: Debug build APK 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - "main" 7 | paths-ignore: 8 | - ".gitignore" 9 | - "**.md" 10 | - "LICENSE" 11 | - ".idea/**" 12 | - ".github/**" 13 | - ".editorconfig" 14 | - "images/**" 15 | - "metadata/**" 16 | 17 | jobs: 18 | run: 19 | name: Build debug APK 20 | runs-on: macos-14 21 | 22 | permissions: 23 | pull-requests: write 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | ref: ${{ github.event.pull_request.head.ref }} 29 | repository: ${{ github.event.pull_request.head.repo.full_name }} 30 | 31 | - name: Setup JDK 32 | uses: actions/setup-java@v4 33 | with: 34 | distribution: "oracle" 35 | java-version: "17" 36 | cache: "gradle" 37 | 38 | - name: Build debug APK 39 | run: | 40 | ./gradlew assembleDebug 41 | 42 | - name: Upload debug APK 43 | uses: actions/upload-artifact@v4 44 | id: artifact-upload-step 45 | with: 46 | name: debug-${{ github.sha }} 47 | path: ${{ github.workspace }}/app/build/outputs/apk/debug/app-debug.apk 48 | 49 | - name: Comment APK Link 50 | uses: thollander/actions-comment-pull-request@v2 51 | with: 52 | comment_tag: apk 53 | message: | 54 | Build complete! Here's the debug APK: ${{ steps.artifact-upload-step.outputs.artifact-url }} 55 | -------------------------------------------------------------------------------- /.github/workflows/ktlint.yml: -------------------------------------------------------------------------------- 1 | name: Kotlin Lint Check 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - "main" 7 | paths-ignore: 8 | - ".gitignore" 9 | - "**.md" 10 | - "LICENSE" 11 | - ".idea/**" 12 | - ".github/**" 13 | - ".editorconfig" 14 | - "images/**" 15 | - "metadata/**" 16 | 17 | jobs: 18 | ktlint: 19 | name: Check Kotlin Code Format 20 | runs-on: ubuntu-latest 21 | 22 | steps: 23 | - name: Clone repo 24 | uses: actions/checkout@v4 25 | 26 | - name: Run ktlint 27 | uses: ScaCap/action-ktlint@master 28 | with: 29 | filter_mode: file 30 | github_token: ${{ secrets.github_token }} 31 | reporter: github-check 32 | -------------------------------------------------------------------------------- /.github/workflows/release-build.yml: -------------------------------------------------------------------------------- 1 | name: Generate Release Version 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | name: Build Release APK 9 | runs-on: macos-14 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Setup JDK 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: "temurin" 19 | java-version: "17" 20 | cache: "gradle" 21 | 22 | - name: Setup Android SDK 23 | uses: android-actions/setup-android@v3 24 | 25 | - name: Install Latest Build Tools 26 | run: | 27 | build_tools_list=$(sdkmanager --list | sed -n 's/.*\(build-tools;[0-9.][0-9.a-zA-Z-]*\).*/\1/p') 28 | stable_build_tools_list=$(echo "$build_tools_list" | grep -v "\-rc") 29 | latest_stable_version=$(echo "$stable_build_tools_list" | sort -V | tail -n 1) 30 | latest_version_number=$(echo $latest_stable_version | sed 's/.*;//') 31 | sdkmanager "$latest_stable_version" 32 | echo "$ANDROID_SDK_ROOT/build-tools/$latest_version_number" >> $GITHUB_PATH 33 | 34 | - name: Build Unsigned Release APK 35 | run: ./gradlew assembleRelease 36 | 37 | - name: Build Unsigned Release App Bundle 38 | run: ./gradlew bundleRelease 39 | 40 | - name: Generate Signing Key 41 | env: 42 | KEYSTORE_B64: ${{ secrets.APP_KEYSTORE }} 43 | run: | 44 | echo $KEYSTORE_B64 | base64 -d > keystore.jks 45 | cp keystore.jks ${{ github.workspace }}/app/build/outputs/bundle/release/keystore.jks 46 | working-directory: ${{ github.workspace }}/app/build/outputs/apk/release 47 | 48 | - name: Sign Release APK 49 | env: 50 | SIGNING_PASSWORD: ${{ secrets.KEY_PASSWORD }} 51 | run: | 52 | apksigner sign --ks keystore.jks --alignment-preserved true --ks-pass env:SIGNING_PASSWORD --out app-release.apk app-release-unsigned.apk 53 | working-directory: ${{ github.workspace }}/app/build/outputs/apk/release 54 | 55 | - name: Sign Release App Bundle 56 | env: 57 | SIGNING_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} 58 | SIGNING_PASSWORD: ${{ secrets.KEY_PASSWORD }} 59 | run: | 60 | jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 -keystore keystore.jks -keypass $SIGNING_PASSWORD -storepass $SIGNING_PASSWORD app-release.aab $SIGNING_KEY_ALIAS 61 | working-directory: ${{ github.workspace }}/app/build/outputs/bundle/release 62 | 63 | - name: Create Job Summary 64 | run: | 65 | unsigned_hash=$(md5 -q ./apk/release/app-release-unsigned.apk) 66 | signed_hash=$(md5 -q ./apk/release/app-release.apk) 67 | signed_aab_hash=$(md5 -q ./bundle/release/app-release.aab) 68 | certificate_hash=$(apksigner verify --print-certs ./apk/release/app-release.apk | grep "SHA-256" | awk -F': ' '{print $2}') 69 | echo "### Release Build Results" >> $GITHUB_STEP_SUMMARY 70 | echo "" >> $GITHUB_STEP_SUMMARY 71 | echo "**Unsigned APK md5**: \`$unsigned_hash\`" >> $GITHUB_STEP_SUMMARY 72 | echo "**Signed APK md5**: \`$signed_hash\`" >> $GITHUB_STEP_SUMMARY 73 | echo "**Signed AAB md5**: \`$signed_aab_hash\`" >> $GITHUB_STEP_SUMMARY 74 | echo "**Certificate SHA-256**: \`$certificate_hash\`" >> $GITHUB_STEP_SUMMARY 75 | working-directory: ${{ github.workspace }}/app/build/outputs 76 | 77 | - name: Upload Signed APK 78 | uses: actions/upload-artifact@v4 79 | id: artifact-upload-step 80 | with: 81 | name: release-${{ github.sha }} 82 | path: | 83 | ${{ github.workspace }}/app/build/outputs/apk/release/app-release.apk 84 | ${{ github.workspace }}/app/build/outputs/bundle/release/app-release.aab 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle 2 | *.iml 3 | .gradle 4 | 5 | # Android 6 | /local.properties 7 | 8 | # Android Studio 9 | /.idea/caches 10 | /.idea/libraries 11 | /.idea/modules.xml 12 | /.idea/workspace.xml 13 | /.idea/navEditor.xml 14 | /.idea/assetWizardSettings.xml 15 | /.idea/inspectionProfiles/Project_Default.xml 16 | /.idea/appInsightsSettings.xml 17 | /.idea/deploymentTargetSelector.xml 18 | /.idea/other.xml 19 | 20 | # MacOS 21 | .DS_Store 22 | 23 | # Build 24 | /build 25 | /captures 26 | .externalNativeBuild 27 | .cxx 28 | app/release/** 29 | app/build/** 30 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | GPTMobile -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 21 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | # GPT Mobile 6 | 7 | ### Chat Assistant for Android that supports chatting with multiple models at once. 8 | 9 | [![GitHub all releases](https://img.shields.io/github/downloads/Taewan-P/gpt_mobile/total?label=Downloads&logo=github)](https://github.com/Taewan-P/gpt_mobile/releases/) 10 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/Taewan-P/gpt_mobile?color=black&label=Stable&logo=github)](https://github.com/Taewan-P/gpt_mobile/releases/latest/) 11 | 12 |
13 | 14 | 15 | ## Screenshots 16 | 17 |
18 | 19 | 20 | 21 |
22 | 23 | ## Demos 24 | 25 | 26 | |