├── .github ├── FUNDING.yml └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml └── vcs.xml ├── .kotlin └── errors │ └── errors-1734691941995.log ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Makefile ├── README.md ├── apk-outputs └── .gitignore ├── app ├── .gitignore ├── build.gradle ├── keys │ └── .gitignore ├── proguard-rules.pro ├── release │ ├── app-release.aab │ └── output-metadata.json ├── schemas │ ├── com.afkanerd.DefaultSMS.Models.Datastore │ │ └── 8.json │ ├── com.afkanerd.deku.Datastore │ │ ├── 1.json │ │ ├── 10.json │ │ ├── 11.json │ │ ├── 12.json │ │ ├── 13.json │ │ ├── 14.json │ │ ├── 15.json │ │ ├── 16.json │ │ ├── 17.json │ │ ├── 18.json │ │ ├── 19.json │ │ ├── 2.json │ │ ├── 20.json │ │ ├── 21.json │ │ ├── 22.json │ │ ├── 23.json │ │ ├── 24.json │ │ ├── 3.json │ │ ├── 4.json │ │ ├── 5.json │ │ ├── 6.json │ │ ├── 7.json │ │ ├── 8.json │ │ └── 9.json │ ├── com.afkanerd.deku.DefaultSMS.Models.Database.Datastore │ │ ├── 10.json │ │ ├── 11.json │ │ ├── 12.json │ │ ├── 13.json │ │ ├── 14.json │ │ └── 9.json │ ├── com.afkanerd.deku.DefaultSMS.Models.Datastore │ │ ├── 8.json │ │ └── 9.json │ ├── com.afkanerd.deku.Migrate16To17 │ │ └── 17.json │ └── com.example.swob_deku.Models.Datastore │ │ ├── 1.json │ │ ├── 2.json │ │ ├── 3.json │ │ ├── 4.json │ │ ├── 5.json │ │ ├── 6.json │ │ ├── 7.json │ │ └── 8.json └── src │ ├── androidTest │ └── java │ │ └── java │ │ └── com │ │ └── afkanerd │ │ └── deku │ │ ├── DefaultSMS │ │ ├── Commons │ │ │ └── HelpersTest.java │ │ ├── DatabaseTest.java │ │ └── ThreadedConversationsTest.java │ │ ├── FTPTest.kt │ │ ├── QueueListener │ │ └── RMQConnectionTest.java │ │ ├── RoomMigrationTest.java │ │ └── SMTPTest.kt │ ├── debug │ ├── ic_launcher-playstore.png │ └── res │ │ ├── values │ │ ├── ic_launcher_background.xml │ │ └── strings.xml │ │ └── xml │ │ └── network_security_config.xml │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── afkanerd │ │ │ └── deku │ │ │ ├── Datastore.java │ │ │ ├── DefaultSMS │ │ │ ├── AboutActivity.kt │ │ │ ├── AdaptersViewModels │ │ │ │ ├── ContactsViewModel.kt │ │ │ │ ├── ConversationPagingSource.java │ │ │ │ ├── ConversationsViewModel.kt │ │ │ │ └── SearchViewModel.kt │ │ │ ├── BroadcastReceivers │ │ │ │ ├── IncomingDataSMSBroadcastReceiver.kt │ │ │ │ ├── IncomingTextSMSBroadcastReceiver.kt │ │ │ │ ├── IncomingTextSMSReplyMuteActionBroadcastReceiver.kt │ │ │ │ └── MMSReceiverBroadcastReceiver.java │ │ │ ├── Commons │ │ │ │ ├── ContactDetailsData.kt │ │ │ │ ├── DataHelper.java │ │ │ │ └── Helpers.java │ │ │ ├── DAO │ │ │ │ ├── ConversationDao.kt │ │ │ │ ├── ConversationsThreadsEncryptionDao.java │ │ │ │ └── ThreadsConfigurationsDao.kt │ │ │ ├── Extensions │ │ │ │ ├── Activities.kt │ │ │ │ ├── ColorUtil.kt │ │ │ │ └── LazyListState.kt │ │ │ ├── Modals │ │ │ │ ├── ConversationSecureRequestModal.kt │ │ │ │ ├── ConversationsContactModalFragment.kt │ │ │ │ ├── ConversationsSecureRequestModalSheetFragment.kt │ │ │ │ └── FailedMessageRetryModal.kt │ │ │ ├── Models │ │ │ │ ├── Archive.java │ │ │ │ ├── Compression.java │ │ │ │ ├── Contacts.java │ │ │ │ ├── Conversations │ │ │ │ │ ├── Conversation.kt │ │ │ │ │ ├── ConversationsThreadsEncryption.java │ │ │ │ │ ├── ThreadedConversations.java │ │ │ │ │ └── ThreadedConversationsHandler.java │ │ │ │ ├── DatastoreHandler.kt │ │ │ │ ├── DevMode.kt │ │ │ │ ├── E2EEHandler.kt │ │ │ │ ├── Encryption.kt │ │ │ │ ├── NativeSMSDB.java │ │ │ │ ├── Notifications.kt │ │ │ │ ├── NotificationsHandler.java │ │ │ │ ├── SIMHandler.java │ │ │ │ ├── SMSDatabaseWrapper.java │ │ │ │ ├── SMSHandler.kt │ │ │ │ ├── SMSPduLevel.java │ │ │ │ ├── SettingsHandler.kt │ │ │ │ ├── ThreadsConfigurations.kt │ │ │ │ ├── ThreadsSearch.kt │ │ │ │ └── Transmissions.kt │ │ │ ├── Settings │ │ │ │ └── SettingsFragment.kt │ │ │ ├── SettingsActivity.kt │ │ │ └── ui │ │ │ │ ├── Components │ │ │ │ ├── ConvenientMethods.kt │ │ │ │ ├── Conversations.kt │ │ │ │ ├── ConversationsComponents.kt │ │ │ │ ├── SecureConversationModal.kt │ │ │ │ └── ThreadsConversation.kt │ │ │ │ ├── ComposeNewMain.kt │ │ │ │ ├── ContactDetails.kt │ │ │ │ ├── ConversationsMain.kt │ │ │ │ ├── DefaultCheckMain.kt │ │ │ │ ├── LogcatMain.kt │ │ │ │ ├── ThreadsConversationMain.kt │ │ │ │ ├── ThreadsSearchMain.kt │ │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ ├── Images │ │ │ └── Images │ │ │ │ ├── ImageHandler.java │ │ │ │ └── ImageViewActivity.java │ │ │ ├── MainActivity.kt │ │ │ ├── Modules │ │ │ ├── Network.kt │ │ │ ├── SemaphoreManager.kt │ │ │ ├── Subroutines.kt │ │ │ └── ThreadingPoolExecutor.java │ │ │ ├── NotificationsInitializer.kt │ │ │ ├── RemoteListeners │ │ │ ├── Models │ │ │ │ ├── RemoteListener │ │ │ │ │ ├── RemoteListenerQueuesViewModel.kt │ │ │ │ │ ├── RemoteListenersQueuesDao.kt │ │ │ │ │ └── RemoteListenersViewModel.kt │ │ │ │ ├── RemoteListenerDAO.kt │ │ │ │ ├── RemoteListeners.kt │ │ │ │ ├── RemoteListenersHandler.kt │ │ │ │ └── RemoteListenersQueues.kt │ │ │ ├── RMQ │ │ │ │ ├── RMQConnectionHandler.kt │ │ │ │ ├── RMQConnectionWorker.kt │ │ │ │ ├── RMQLongRunningConnectionWorker.kt │ │ │ │ └── RMQWorkManager.kt │ │ │ ├── RemoteListenerConnectionService.kt │ │ │ ├── RemoteListenerConnectionServiceInitializer.kt │ │ │ ├── components │ │ │ │ ├── Permissions.kt │ │ │ │ ├── RemoteListenerCards.kt │ │ │ │ └── RemoteListenerQueuesCards.kt │ │ │ ├── modals │ │ │ │ ├── RemoteListenerAddQueuesModal.kt │ │ │ │ ├── RemoteListenerModal.kt │ │ │ │ ├── RemoteListenerReadPhoneStatePermissionModal.kt │ │ │ │ └── RemoteListenerSMSPermissionsModal.kt │ │ │ └── ui │ │ │ │ ├── RMQAdd.kt │ │ │ │ ├── RMQMain.kt │ │ │ │ └── RMQQueues.kt │ │ │ ├── Router │ │ │ ├── FTP.java │ │ │ ├── GatewayServers │ │ │ │ ├── GatewayServer.java │ │ │ │ ├── GatewayServerAddModalFragment.kt │ │ │ │ ├── GatewayServerDAO.java │ │ │ │ ├── GatewayServerHandler.java │ │ │ │ ├── GatewayServerListingActivity.java │ │ │ │ ├── GatewayServerRecyclerAdapter.java │ │ │ │ ├── GatewayServerRoutedActivity.kt │ │ │ │ ├── GatewayServerRouterRecyclerAdapter.kt │ │ │ │ ├── GatewayServerRouterViewModel.java │ │ │ │ └── GatewayServerViewModel.kt │ │ │ ├── Models │ │ │ │ ├── RouterHandler.kt │ │ │ │ ├── RouterItem.kt │ │ │ │ └── RouterWorkManager.kt │ │ │ ├── SMTP.java │ │ │ └── Settings │ │ │ │ ├── GatewayServerSettingsActivity.kt │ │ │ │ └── GatewayServerSettingsFragment.kt │ │ │ └── screens.kt │ └── res │ │ ├── drawable-hdpi │ │ └── ic_stat_name.png │ │ ├── drawable-mdpi │ │ └── ic_stat_name.png │ │ ├── drawable-xhdpi │ │ └── ic_stat_name.png │ │ ├── drawable-xxhdpi │ │ └── ic_stat_name.png │ │ ├── drawable-xxxhdpi │ │ └── ic_stat_name.png │ │ ├── drawable │ │ ├── baseline_account_circle_24.xml │ │ ├── baseline_add_link_24.xml │ │ ├── baseline_route_24.xml │ │ ├── compose_message_drawable.xml │ │ ├── github_mark.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── round_add_circle_outline_24.xml │ │ ├── round_computer_24.xml │ │ ├── round_content_copy_24.xml │ │ ├── round_delete_24.xml │ │ ├── round_language_24.xml │ │ ├── round_stop_circle_24.xml │ │ ├── rounded_forward_24.xml │ │ └── undraw_team_work_i1f3.xml │ │ ├── font │ │ ├── roboto.ttf │ │ ├── roboto_bold.ttf │ │ ├── roboto_condensed_regular.ttf │ │ └── roboto_medium_italic.ttf │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_gateway_server_settings.xml │ │ ├── activity_gateway_servers_listing_activitiy.xml │ │ ├── activity_image_view.xml │ │ ├── activity_router.xml │ │ ├── activity_settings.xml │ │ ├── failed_messages_modal_sheet.xml │ │ ├── fragment_gateway_sever_add_routing_format_layout.xml │ │ ├── fragment_modal_secure_request.xml │ │ ├── fragment_modalsheet_gateway_server.xml │ │ ├── fragment_modalsheet_gateway_server_ftp_add_layout.xml │ │ ├── fragment_modalsheet_gateway_server_http_add_layout.xml │ │ ├── fragment_modalsheet_gateway_server_smtp_add_layout.xml │ │ ├── fragment_modalsheet_secure_request_layout.xml │ │ ├── gateway_server_listing_layout.xml │ │ ├── gateway_server_routed_messages_layout.xml │ │ ├── layout_conversation_contact_card_modalsheet.xml │ │ └── layout_conversation_contact_modal.xml │ │ ├── menu │ │ ├── gateway_client_listing_menu.xml │ │ ├── gateway_client_project_listing_menu.xml │ │ ├── gateway_server_add_menu.xml │ │ ├── gateway_server_listing_menu.xml │ │ ├── gateway_server_routed_list_menu.xml │ │ └── gateway_server_routed_menu_items_selected.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_foreground.webp │ │ └── ic_launcher_round.webp │ │ ├── raw │ │ ├── .gitignore │ │ ├── app_example.properties │ │ ├── ftp_example.properties │ │ └── smtp_example.properties │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-night │ │ ├── colors.xml │ │ ├── theme_overlays.xml │ │ └── themes.xml │ │ ├── values-pl │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-v23 │ │ └── font_certs.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── key_request_initiated.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ ├── theme_overlays.xml │ │ └── themes.xml │ │ └── xml │ │ ├── communication_encryption_preferences.xml │ │ ├── developer_preferences.xml │ │ ├── gateway_server_settings_preferences.xml │ │ ├── locales_config.xml │ │ ├── network_security_config.xml │ │ └── settings_preferences.xml │ └── test │ └── java │ └── com │ └── afkanerd │ └── deku │ ├── DefaultSMS │ └── Commons │ │ ├── JavaMethodsTest.java │ │ └── PhonenumberParsingTest.java │ ├── RandomTest.java │ └── TemplateTest.kt ├── build.gradle ├── bump_version.py ├── ci ├── .gitignore └── ci_cd.sh ├── commit-checks └── Dockerfile ├── deku_chat.png ├── deku_chat_details.png ├── deku_default.png ├── deku_home.png ├── deku_secure_request.png ├── fastlane ├── Appfile ├── Fastfile └── metadata │ └── android │ ├── en-US │ ├── changelogs │ │ ├── 0.17.0.txt │ │ ├── 0.22.0.txt │ │ ├── 0.33.0.txt │ │ ├── 0.34.0.txt │ │ ├── 0.35.0.txt │ │ ├── 0.36.0.txt │ │ ├── 0.37.0.txt │ │ ├── 0.38.0.txt │ │ ├── 0.39.0.txt │ │ ├── 0.40.0.txt │ │ ├── 0.41.0.txt │ │ ├── 0.42.0.txt │ │ ├── 0.43.0.txt │ │ ├── 0.44.0.txt │ │ ├── 0.45.0.txt │ │ ├── 0.46.0.txt │ │ └── 1.0.0.txt │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1_en-US.png │ │ │ ├── 2_en-US.png │ │ │ ├── 3_en-US.png │ │ │ ├── 4_en-US.png │ │ │ ├── 5_en-US.png │ │ │ └── 6_en-US.png │ ├── short_description.txt │ ├── title.txt │ └── video.txt │ └── ru │ ├── full_description.txt │ └── short_description.txt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystore.properties.example ├── pre-push.sample ├── publish-maven.sh ├── release.properties.example ├── release.py ├── requirements.txt ├── scripts ├── data.json ├── do_build.yaml ├── import.sh └── populate_db_debug.py ├── settings.gradle ├── track.py └── version.properties /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 3 | patreon: # Replace with a single Patreon username e.g., user1 4 | open_collective: dekusms 5 | ko_fi: # Replace with a single Ko-fi username e.g., user1 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | polar: # Replace with a single Polar username e.g., user1 8 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username e.g., user1 9 | thanks_dev: # Replace with a single thanks.dev username e.g., u/gh/user1 10 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 11 | liberapay: # Replace with a single Liberapay username e.g., user1 12 | issuehunt: # Replace with a single IssueHunt username e.g., user1 13 | otechie: # Replace with a single Otechie username e.g., user1 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Run SSH command 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: 7 | - closed 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Run SSH command 14 | uses: appleboy/ssh-action@master 15 | timeout-minutes: 60 16 | with: 17 | command_timeout: "60m" 18 | host: ${{ secrets.HOST }} 19 | username: ${{ secrets.USERNAME }} 20 | password: ${{ secrets.PASSWORD }} 21 | port: ${{ secrets.PORT }} 22 | script: | 23 | if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.merged }}" == true ]]; then 24 | if [[ "${{ github.event.pull_request.base.ref }}" == "staging" ]]; then 25 | cd /root/digitalocean_ci_cd_app_releases && ./start.sh 26 | elif [[ "${{ github.event.pull_request.base.ref }}" == "master" ]]; then 27 | cd /root/digitalocean_ci_cd_app_releases && ./start.sh cp && ./start.sh build 28 | fi 29 | else 30 | echo "PR is not merged into '${{ github.event.pull_request.base.ref }}'. Workflow will not run." 31 | exit 1 32 | fi 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/* 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | *.aab 17 | *.apk 18 | local.properties 19 | keystore.properties 20 | *.jks 21 | ks.passwd 22 | venv/* 23 | /release.properties 24 | *.sw* 25 | gradle.properties 26 | *.tmp.sh 27 | *.logs 28 | /app/.idea/.gitignore 29 | /app/.idea/appInsightsSettings.xml 30 | /app/.idea/caches/deviceStreaming.xml 31 | /app/.idea/gradle.xml 32 | /app/.idea/migrations.xml 33 | /app/.idea/misc.xml 34 | /app/.idea/runConfigurations.xml 35 | /app/.idea/vcs.xml 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "smswithoutborders_libsignal-doubleratchet"] 2 | path = smswithoutborders_libsignal-doubleratchet 3 | url = https://github.com/smswithoutborders/lib_signal_double_ratchet_java.git 4 | [submodule "reproducible-apk-tools"] 5 | path = reproducible-apk-tools 6 | url = https://github.com/obfusk/reproducible-apk-tools.git 7 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | swob_server -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 AS base 2 | 3 | RUN apt update && apt install -y openjdk-17-jdk openjdk-17-jre android-sdk sdkmanager 4 | 5 | WORKDIR /android 6 | 7 | COPY . . 8 | 9 | ENV ANDROID_HOME "/usr/lib/android-sdk/" 10 | ENV PATH "${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/" 11 | # ENV GRADLE_OPTS "-Xmx2048m" 12 | 13 | RUN yes | sdkmanager --licenses 14 | 15 | ENV PASS="" 16 | ENV MIN_SDK="" 17 | 18 | # CMD ./gradlew assembleDebug 19 | FROM base as apk-builder 20 | CMD ./gradlew assembleRelease && \ 21 | apksigner sign --ks app/keys/app-release-key.jks \ 22 | --ks-pass pass:$PASS \ 23 | --in app/build/outputs/apk/release/app-release-unsigned.apk \ 24 | --out app/build/outputs/apk/release/app-release.apk 25 | 26 | FROM base as bundle-builder 27 | CMD ./gradlew assemble bundleRelease && \ 28 | apksigner sign --ks app/keys/app-release-key.jks \ 29 | --ks-pass pass:$PASS \ 30 | --in app/build/outputs/bundle/release/app-release.aab \ 31 | --out app/build/outputs/bundle/release/app-bundle.aab \ 32 | --min-sdk-version $MIN_SDK 33 | 34 | # CMD cp app/build/outputs/apk/debug/app-debug.apk /apkbuilds/ 35 | # CMD sha256sum app/build/outputs/apk/debug/app-debug.apk 36 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" 4 | -------------------------------------------------------------------------------- /apk-outputs/.gitignore: -------------------------------------------------------------------------------- 1 | *.apk 2 | *.idsig 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/keys/.gitignore: -------------------------------------------------------------------------------- 1 | *.jks 2 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # Suppress logging 24 | -assumenosideeffects class android.util.Log { 25 | public static int d(...); 26 | public static int e(...); 27 | public static int i(...); 28 | public static int v(...); 29 | public static int w(...); 30 | } -------------------------------------------------------------------------------- /app/release/app-release.aab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/release/app-release.aab -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.afkanerd.deku", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 49, 15 | "versionName": "0.37.0", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/schemas/com.afkanerd.deku.Datastore/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "61c1eb9e2dec971cca3b2e611ae9ada0", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "date", 25 | "columnName": "date", 26 | "affinity": "INTEGER", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "id", 31 | "columnName": "id", 32 | "affinity": "INTEGER", 33 | "notNull": true 34 | } 35 | ], 36 | "primaryKey": { 37 | "autoGenerate": true, 38 | "columnNames": [ 39 | "id" 40 | ] 41 | }, 42 | "indices": [ 43 | { 44 | "name": "index_GatewayServer_URL", 45 | "unique": true, 46 | "columnNames": [ 47 | "URL" 48 | ], 49 | "orders": [], 50 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 51 | } 52 | ], 53 | "foreignKeys": [] 54 | }, 55 | { 56 | "tableName": "Archive", 57 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `messageId` TEXT, `threadId` TEXT)", 58 | "fields": [ 59 | { 60 | "fieldPath": "id", 61 | "columnName": "id", 62 | "affinity": "INTEGER", 63 | "notNull": true 64 | }, 65 | { 66 | "fieldPath": "messageId", 67 | "columnName": "messageId", 68 | "affinity": "TEXT", 69 | "notNull": false 70 | }, 71 | { 72 | "fieldPath": "threadId", 73 | "columnName": "threadId", 74 | "affinity": "TEXT", 75 | "notNull": false 76 | } 77 | ], 78 | "primaryKey": { 79 | "autoGenerate": true, 80 | "columnNames": [ 81 | "id" 82 | ] 83 | }, 84 | "indices": [ 85 | { 86 | "name": "index_Archive_messageId", 87 | "unique": true, 88 | "columnNames": [ 89 | "messageId" 90 | ], 91 | "orders": [], 92 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_messageId` ON `${TABLE_NAME}` (`messageId`)" 93 | } 94 | ], 95 | "foreignKeys": [] 96 | } 97 | ], 98 | "views": [], 99 | "setupQueries": [ 100 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 101 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '61c1eb9e2dec971cca3b2e611ae9ada0')" 102 | ] 103 | } 104 | } -------------------------------------------------------------------------------- /app/schemas/com.afkanerd.deku.Datastore/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 2, 5 | "identityHash": "196ef51e51221e63b5a6749bee414e01", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '196ef51e51221e63b5a6749bee414e01')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.afkanerd.deku.Datastore/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 3, 5 | "identityHash": "196ef51e51221e63b5a6749bee414e01", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '196ef51e51221e63b5a6749bee414e01')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.afkanerd.deku.Datastore/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 4, 5 | "identityHash": "02583ec4556cf041353e91203ec5f135", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `protocol` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "protocol", 19 | "columnName": "protocol", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02583ec4556cf041353e91203ec5f135')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.example.swob_deku.Models.Datastore/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "61c1eb9e2dec971cca3b2e611ae9ada0", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "date", 25 | "columnName": "date", 26 | "affinity": "INTEGER", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "id", 31 | "columnName": "id", 32 | "affinity": "INTEGER", 33 | "notNull": true 34 | } 35 | ], 36 | "primaryKey": { 37 | "autoGenerate": true, 38 | "columnNames": [ 39 | "id" 40 | ] 41 | }, 42 | "indices": [ 43 | { 44 | "name": "index_GatewayServer_URL", 45 | "unique": true, 46 | "columnNames": [ 47 | "URL" 48 | ], 49 | "orders": [], 50 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 51 | } 52 | ], 53 | "foreignKeys": [] 54 | }, 55 | { 56 | "tableName": "Archive", 57 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `messageId` TEXT, `threadId` TEXT)", 58 | "fields": [ 59 | { 60 | "fieldPath": "id", 61 | "columnName": "id", 62 | "affinity": "INTEGER", 63 | "notNull": true 64 | }, 65 | { 66 | "fieldPath": "messageId", 67 | "columnName": "messageId", 68 | "affinity": "TEXT", 69 | "notNull": false 70 | }, 71 | { 72 | "fieldPath": "threadId", 73 | "columnName": "threadId", 74 | "affinity": "TEXT", 75 | "notNull": false 76 | } 77 | ], 78 | "primaryKey": { 79 | "autoGenerate": true, 80 | "columnNames": [ 81 | "id" 82 | ] 83 | }, 84 | "indices": [ 85 | { 86 | "name": "index_Archive_messageId", 87 | "unique": true, 88 | "columnNames": [ 89 | "messageId" 90 | ], 91 | "orders": [], 92 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_messageId` ON `${TABLE_NAME}` (`messageId`)" 93 | } 94 | ], 95 | "foreignKeys": [] 96 | } 97 | ], 98 | "views": [], 99 | "setupQueries": [ 100 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 101 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '61c1eb9e2dec971cca3b2e611ae9ada0')" 102 | ] 103 | } 104 | } -------------------------------------------------------------------------------- /app/schemas/com.example.swob_deku.Models.Datastore/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 2, 5 | "identityHash": "196ef51e51221e63b5a6749bee414e01", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '196ef51e51221e63b5a6749bee414e01')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.example.swob_deku.Models.Datastore/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 3, 5 | "identityHash": "196ef51e51221e63b5a6749bee414e01", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `method` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "method", 19 | "columnName": "method", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '196ef51e51221e63b5a6749bee414e01')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.example.swob_deku.Models.Datastore/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 4, 5 | "identityHash": "02583ec4556cf041353e91203ec5f135", 6 | "entities": [ 7 | { 8 | "tableName": "GatewayServer", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`URL` TEXT, `protocol` TEXT, `format` TEXT, `date` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "URL", 13 | "columnName": "URL", 14 | "affinity": "TEXT", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "protocol", 19 | "columnName": "protocol", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "format", 25 | "columnName": "format", 26 | "affinity": "TEXT", 27 | "notNull": false 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "INTEGER", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "id", 37 | "columnName": "id", 38 | "affinity": "INTEGER", 39 | "notNull": true 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "id" 46 | ] 47 | }, 48 | "indices": [ 49 | { 50 | "name": "index_GatewayServer_URL", 51 | "unique": true, 52 | "columnNames": [ 53 | "URL" 54 | ], 55 | "orders": [], 56 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_GatewayServer_URL` ON `${TABLE_NAME}` (`URL`)" 57 | } 58 | ], 59 | "foreignKeys": [] 60 | }, 61 | { 62 | "tableName": "Archive", 63 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`threadId` INTEGER NOT NULL, PRIMARY KEY(`threadId`))", 64 | "fields": [ 65 | { 66 | "fieldPath": "threadId", 67 | "columnName": "threadId", 68 | "affinity": "INTEGER", 69 | "notNull": true 70 | } 71 | ], 72 | "primaryKey": { 73 | "autoGenerate": false, 74 | "columnNames": [ 75 | "threadId" 76 | ] 77 | }, 78 | "indices": [ 79 | { 80 | "name": "index_Archive_threadId", 81 | "unique": true, 82 | "columnNames": [ 83 | "threadId" 84 | ], 85 | "orders": [], 86 | "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Archive_threadId` ON `${TABLE_NAME}` (`threadId`)" 87 | } 88 | ], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02583ec4556cf041353e91203ec5f135')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/java/com/afkanerd/deku/DefaultSMS/DatabaseTest.java: -------------------------------------------------------------------------------- 1 | package java.com.afkanerd.deku.DefaultSMS; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.provider.Telephony; 6 | import android.util.Log; 7 | 8 | import androidx.test.ext.junit.runners.AndroidJUnit4; 9 | import androidx.test.platform.app.InstrumentationRegistry; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import java.util.Arrays; 15 | 16 | 17 | @RunWith(AndroidJUnit4.class) 18 | public class DatabaseTest { 19 | 20 | Context context; 21 | 22 | public DatabaseTest() { 23 | context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | } 25 | @Test 26 | public void testThreads() { 27 | // [snippet, thread_id, msg_count] 28 | Cursor cursor = context.getContentResolver().query( 29 | Telephony.Sms.CONTENT_URI, 30 | null, 31 | null, 32 | null, 33 | null 34 | ); 35 | String[] columnNames = cursor.getColumnNames(); 36 | Log.d(getClass().getName(), Arrays.toString(columnNames)); 37 | 38 | if(cursor.moveToFirst()) { 39 | do { 40 | for(String columnName : columnNames) 41 | Log.d(getClass().getName(), columnName + ": " + 42 | cursor.getString(cursor.getColumnIndex(columnName))); 43 | Log.d(getClass().getName(), "\n"); 44 | } while(cursor.moveToNext()); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/androidTest/java/java/com/afkanerd/deku/DefaultSMS/ThreadedConversationsTest.java: -------------------------------------------------------------------------------- 1 | package java.com.afkanerd.deku.DefaultSMS; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import android.content.Context; 6 | 7 | import androidx.test.ext.junit.runners.AndroidJUnit4; 8 | import androidx.test.platform.app.InstrumentationRegistry; 9 | 10 | import com.afkanerd.deku.DefaultSMS.DAO.ConversationDao; 11 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation; 12 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.ThreadedConversations; 13 | 14 | import org.junit.Test; 15 | import org.junit.runner.RunWith; 16 | 17 | import java.util.List; 18 | 19 | @RunWith(AndroidJUnit4.class) 20 | public class ThreadedConversationsTest { 21 | 22 | Context context; 23 | 24 | public ThreadedConversationsTest() { 25 | context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/androidTest/java/java/com/afkanerd/deku/RoomMigrationTest.java: -------------------------------------------------------------------------------- 1 | package java.com.afkanerd.deku; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.room.testing.MigrationTestHelper; 6 | import androidx.sqlite.db.SupportSQLiteDatabase; 7 | import androidx.sqlite.db.SupportSQLiteStatement; 8 | import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory; 9 | import androidx.test.ext.junit.runners.AndroidJUnit4; 10 | import androidx.test.platform.app.InstrumentationRegistry; 11 | 12 | import com.afkanerd.deku.Datastore; 13 | 14 | import org.junit.Rule; 15 | import org.junit.Test; 16 | import org.junit.runner.RunWith; 17 | 18 | import java.io.IOException; 19 | 20 | @RunWith(AndroidJUnit4.class) 21 | 22 | public class RoomMigrationTest { 23 | private static final String TEST_DB = Datastore.databaseName; 24 | 25 | @Rule 26 | public MigrationTestHelper helper; 27 | 28 | Context context; 29 | public RoomMigrationTest() { 30 | this.context = InstrumentationRegistry.getInstrumentation().getTargetContext(); 31 | helper = new MigrationTestHelper(InstrumentationRegistry.getInstrumentation(), 32 | Datastore.class.getCanonicalName(), new FrameworkSQLiteOpenHelperFactory()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/androidTest/java/java/com/afkanerd/deku/SMTPTest.kt: -------------------------------------------------------------------------------- 1 | package java.com.afkanerd.deku 2 | 3 | import android.content.Context 4 | import androidx.test.filters.SmallTest 5 | import androidx.test.platform.app.InstrumentationRegistry 6 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation 7 | import org.junit.Before 8 | import org.junit.Test 9 | import java.util.Properties 10 | import com.afkanerd.deku.DefaultSMS.R 11 | import com.afkanerd.deku.Router.Models.RouterItem 12 | import kotlinx.serialization.encodeToString 13 | import kotlinx.serialization.json.Json 14 | import java.util.Date 15 | import javax.mail.Message 16 | import javax.mail.Session 17 | import javax.mail.Transport 18 | import javax.mail.internet.MimeMessage 19 | 20 | 21 | @SmallTest 22 | class SMTPTest { 23 | val properties: Properties = Properties() 24 | lateinit var context: Context 25 | 26 | @Before 27 | fun init() { 28 | context = InstrumentationRegistry.getInstrumentation().targetContext 29 | // val inputStream = context.resources.openRawResource(R.raw.smtp) 30 | // properties.load(inputStream) 31 | properties.put("mail.smtp.host", properties.getProperty("host")) 32 | properties.put("mail.smtp.port", properties.getProperty("port")) 33 | properties.put("mail.debug", "true"); 34 | 35 | properties.put("mail.smtp.auth", "true"); 36 | properties.put("mail.smtp.starttls.enable", "true") 37 | } 38 | 39 | 40 | @Test 41 | fun smtpTest() { 42 | 43 | val session = Session.getInstance(properties, null) 44 | with(session) { 45 | val msg = MimeMessage(session) 46 | msg.setFrom(properties.getProperty("username")) 47 | msg.setRecipients(Message.RecipientType.TO, 48 | "developers@dekusms.com") 49 | msg.subject= "Deku Development" 50 | msg.sentDate= Date() 51 | msg.setText("Hi Deku devs,\nHere is our first sample mail from Android studio\n" + 52 | "Thanks\n" + Date()) 53 | Transport.send(msg, 54 | properties.getProperty("username"), properties.getProperty("password")) 55 | } 56 | } 57 | 58 | @Test 59 | fun testJson() { 60 | val conversation = Conversation() 61 | conversation.address = "test_address" 62 | conversation.id = 1 63 | conversation.text = "hello world" 64 | 65 | val routerItem = RouterItem(conversation) 66 | routerItem.tag = "sample_tag" 67 | 68 | println(routerItem.serializeJson()) 69 | } 70 | } -------------------------------------------------------------------------------- /app/src/debug/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/debug/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/debug/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #E6E6E7 4 | -------------------------------------------------------------------------------- /app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/debug/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/AboutActivity.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import android.view.Menu 7 | import android.view.MenuItem 8 | import android.view.View 9 | import android.widget.TextView 10 | import androidx.appcompat.app.AppCompatActivity 11 | import androidx.appcompat.widget.Toolbar 12 | import com.afkanerd.deku.MainActivity 13 | 14 | class AboutActivity : AppCompatActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_about) 18 | 19 | val toolbar = findViewById(R.id.about_toolbar) 20 | setSupportActionBar(toolbar) 21 | 22 | val ab = supportActionBar 23 | ab!!.title = getString(R.string.about_deku) 24 | ab.setDisplayHomeAsUpEnabled(true) 25 | ab.setHomeButtonEnabled(true) 26 | 27 | setVersion() 28 | 29 | setClickListeners() 30 | } 31 | 32 | private fun setVersion() { 33 | val textView = findViewById(R.id.about_version_text) 34 | textView.text = BuildConfig.VERSION_NAME 35 | } 36 | 37 | private fun setClickListeners() { 38 | val textView = findViewById(R.id.about_github_link) 39 | textView.setOnClickListener(object : View.OnClickListener { 40 | override fun onClick(view: View?) { 41 | val url = getString(R.string.about_deku_github_url) 42 | val shareIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) 43 | startActivity(shareIntent) 44 | } 45 | }) 46 | } 47 | 48 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 49 | return super.onCreateOptionsMenu(menu) 50 | } 51 | 52 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 53 | if(item.itemId == android.R.id.home) { 54 | startActivity( 55 | Intent(applicationContext, MainActivity::class.java).apply { 56 | setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_TASK_ON_HOME) 57 | } 58 | ) 59 | finish() 60 | } 61 | return super.onOptionsItemSelected(item) 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/AdaptersViewModels/SearchViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.AdaptersViewModels 2 | 3 | import android.content.Context 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | import com.afkanerd.deku.Datastore 8 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation 9 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.ThreadedConversations 10 | import com.afkanerd.deku.Modules.ThreadingPoolExecutor 11 | import kotlinx.coroutines.CoroutineScope 12 | import kotlinx.coroutines.Dispatchers 13 | import kotlinx.coroutines.launch 14 | import java.util.ArrayList 15 | 16 | class SearchViewModel : ViewModel() { 17 | var liveData: MutableLiveData> = MutableLiveData() 18 | 19 | var threadId: String? = null 20 | 21 | fun get(): LiveData> { 22 | return liveData 23 | } 24 | 25 | fun search(context: Context, input: String) { 26 | if(input.isBlank()) { 27 | liveData.value = mutableListOf() 28 | } 29 | else { 30 | CoroutineScope(Dispatchers.Default).launch { 31 | val datastore = Datastore.getDatastore(context).conversationDao() 32 | val results = if(!threadId.isNullOrBlank()) 33 | datastore.getAllThreadingSearch(input, threadId!!) 34 | else datastore.getAllThreadingSearch(input) 35 | liveData.postValue(results) 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/BroadcastReceivers/MMSReceiverBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.BroadcastReceivers; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | import com.afkanerd.deku.DefaultSMS.BuildConfig; 9 | 10 | 11 | public class MMSReceiverBroadcastReceiver extends BroadcastReceiver { 12 | @Override 13 | public void onReceive(Context context, Intent intent) { 14 | 15 | if(BuildConfig.DEBUG) 16 | Log.d(getClass().getName(), "New MMS received.."); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Commons/ContactDetailsData.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Commons 2 | 3 | data class ContactDetailsData( 4 | val phoneNumber: String, 5 | val contactPhotoUri: String?, 6 | val isContact: Boolean, 7 | val isEncryptionEnabled: Boolean, 8 | val contactName: String, 9 | val id: String? 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Commons/DataHelper.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Commons; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class DataHelper { 6 | 7 | public static String arrayToString(int[] intArray) { 8 | StringBuilder stringBuilder = new StringBuilder(); 9 | if (intArray.length > 0) { 10 | stringBuilder.append(intArray[0]); 11 | for (int i = 1; i < intArray.length; i++) { 12 | stringBuilder.append(intArray[i]); 13 | } 14 | } 15 | return stringBuilder.toString(); 16 | } 17 | 18 | public static String byteToBinary(byte[] bytes) { 19 | String binary = ""; 20 | for(byte b: bytes) 21 | binary += Integer.toBinaryString(b); 22 | return binary; 23 | } 24 | 25 | public static int[] getNibbleFromByte(byte b) { 26 | return new int[]{(b & 0x0F), ((b >> 4) & 0x0F)}; 27 | } 28 | 29 | public static String getHexOfByte(byte[] bytes) { 30 | StringBuilder buffer = new StringBuilder(); 31 | for (byte aByte : bytes) { 32 | buffer.append(Character.forDigit((aByte >> 4) & 0xF, 16)); 33 | buffer.append(Character.forDigit((aByte & 0xF), 16)); 34 | } 35 | return buffer.toString(); 36 | } 37 | 38 | public static int[] bytesToNibbleArray(byte[] bytes) { 39 | int ints[] = new int[bytes.length * 2]; 40 | for(int i=0, j=0;i fetchLiveData(String keystoreAlias); 20 | 21 | @Insert(onConflict = OnConflictStrategy.REPLACE) 22 | long insert(ConversationsThreadsEncryption conversationsThreadsEncryption); 23 | 24 | @Update 25 | int update(ConversationsThreadsEncryption conversationsThreadsEncryption); 26 | 27 | @Query("SELECT * FROM ConversationsThreadsEncryption WHERE keystoreAlias = :keystoreAlias") 28 | ConversationsThreadsEncryption findByKeystoreAlias(String keystoreAlias); 29 | 30 | @Query("SELECT * FROM ConversationsThreadsEncryption") 31 | List getAll(); 32 | 33 | @Query("SELECT * FROM ConversationsThreadsEncryption WHERE keystoreAlias = :keystoreAlias") 34 | ConversationsThreadsEncryption fetch(String keystoreAlias); 35 | 36 | @Query("DELETE FROM ConversationsThreadsEncryption WHERE keystoreAlias = :keystoreAlias") 37 | int delete(String keystoreAlias); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/DAO/ThreadsConfigurationsDao.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.DAO 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation 8 | import com.afkanerd.deku.DefaultSMS.Models.ThreadsConfigurations 9 | 10 | @Dao 11 | interface ThreadsConfigurationsDao { 12 | 13 | @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) 14 | fun insert(threadsConfigurations: ThreadsConfigurations): Long 15 | 16 | @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) 17 | fun insert(threadsConfigurations: MutableList) 18 | 19 | @Query("SELECT * FROM ThreadsConfigurations WHERE threadId = :threadId") 20 | fun get(threadId: String): ThreadsConfigurations? 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Extensions/Activities.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Extensions 2 | 3 | import android.content.Context 4 | import android.content.ContextWrapper 5 | import androidx.activity.ComponentActivity 6 | 7 | 8 | fun Context.getActivity(): ComponentActivity? = when (this) { 9 | is ComponentActivity -> this 10 | is ContextWrapper -> baseContext.getActivity() 11 | else -> null 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Extensions/ColorUtil.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Extensions 2 | 3 | import androidx.annotation.ColorInt 4 | import androidx.core.graphics.ColorUtils 5 | import kotlin.math.absoluteValue 6 | 7 | @ColorInt 8 | fun String.toHslColor(saturation: Float = 0.5f, lightness: Float = 0.4f): Int { 9 | val hue = fold(0) { acc, char -> char.code + acc * 37 } % 360 10 | return ColorUtils.HSLToColor(floatArrayOf(hue.absoluteValue.toFloat(), saturation, lightness)) 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Extensions/LazyListState.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Extensions 2 | 3 | import androidx.compose.foundation.lazy.LazyListState 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.derivedStateOf 6 | import androidx.compose.runtime.mutableStateOf 7 | import androidx.compose.runtime.remember 8 | import androidx.compose.runtime.getValue 9 | import androidx.compose.runtime.setValue 10 | 11 | @Composable 12 | fun LazyListState.isScrollingUp(): Boolean { 13 | var previousIndex by remember(this) { mutableStateOf(firstVisibleItemIndex) } 14 | var previousScrollOffset by remember(this) { mutableStateOf(firstVisibleItemScrollOffset) } 15 | 16 | return remember(this) { 17 | derivedStateOf { 18 | if(previousIndex != firstVisibleItemIndex) { 19 | previousIndex > firstVisibleItemIndex 20 | } else { 21 | previousScrollOffset >= firstVisibleItemScrollOffset 22 | }.also { 23 | previousIndex = firstVisibleItemIndex 24 | previousScrollOffset = firstVisibleItemScrollOffset 25 | } 26 | } 27 | }.value 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Modals/ConversationSecureRequestModal.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Modals 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.afkanerd.deku.DefaultSMS.Models.E2EEHandler 6 | import com.afkanerd.deku.DefaultSMS.R 7 | import com.google.android.material.bottomsheet.BottomSheetBehavior 8 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 9 | import com.google.android.material.button.MaterialButton 10 | 11 | class ConversationSecureRequestModal(private val requestCallback: Runnable) : 12 | BottomSheetDialogFragment(R.layout.fragment_modal_secure_request){ 13 | 14 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 15 | super.onViewCreated(view, savedInstanceState) 16 | 17 | val bottomSheet = view.findViewById(R.id.conversation_secure_request_modal) 18 | val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) 19 | 20 | bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED 21 | 22 | view.findViewById(R.id.conversation_secure_request_btn).setOnClickListener { 23 | it.isEnabled = false 24 | requestCallback.run() 25 | activity?.recreate() 26 | dismiss() 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Modals/ConversationsContactModalFragment.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Modals 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import android.os.Bundle 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.ImageButton 11 | import android.widget.TextView 12 | import android.widget.Toast 13 | import androidx.core.content.ContextCompat.getSystemService 14 | import com.afkanerd.deku.DefaultSMS.R 15 | import com.google.android.material.bottomsheet.BottomSheetBehavior 16 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 17 | 18 | class ConversationsContactModalFragment(val contactName: String, val address: String) : 19 | BottomSheetDialogFragment(R.layout.layout_conversation_contact_card_modalsheet) { 20 | 21 | private lateinit var bottomSheetBehavior: BottomSheetBehavior 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | val bottomSheet = view.findViewById(R.id.conversation_contact_sheet_modal) 26 | bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) 27 | bottomSheetBehavior.isFitToContents = true 28 | bottomSheetBehavior.isDraggable = true 29 | bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED 30 | 31 | view.findViewById(R.id.conversation_contact_modal_contact_text) 32 | .text = contactName 33 | 34 | view.findViewById(R.id.conversation_contact_modal_details_address) 35 | .text = address 36 | 37 | view.findViewById(R.id.conversation_contact_modal_copy_icon) 38 | .setOnClickListener { copy() } 39 | } 40 | 41 | companion object { 42 | const val TAG = "ModalBottomSheet" 43 | } 44 | 45 | private fun copy() { 46 | val clipData = ClipData.newPlainText(contactName, address) 47 | val clipboard = context?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 48 | clipboard.setPrimaryClip(clipData) 49 | 50 | Toast.makeText(context, getString(R.string.conversation_copied), Toast.LENGTH_SHORT).show() 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Modals/ConversationsSecureRequestModalSheetFragment.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Modals 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Bundle 6 | import android.view.View 7 | import android.widget.TextView 8 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.ThreadedConversations 9 | import com.afkanerd.deku.DefaultSMS.R 10 | import com.google.android.material.bottomsheet.BottomSheetBehavior 11 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 12 | import com.google.android.material.button.MaterialButton 13 | 14 | class ConversationsSecureRequestModalSheetFragment(val contactName: String?, 15 | private val acceptRunnable: Runnable?) 16 | : BottomSheetDialogFragment(R.layout.fragment_modalsheet_secure_request_layout) { 17 | 18 | init { 19 | if(contactName.isNullOrBlank() || acceptRunnable == null) 20 | dismiss() 21 | } 22 | 23 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 24 | super.onViewCreated(view, savedInstanceState) 25 | 26 | val bottomSheet = view.findViewById(R.id.conversation_secure_request_layout) 27 | val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) 28 | 29 | bottomSheetBehavior.isFitToContents = true 30 | bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED 31 | view.findViewById(R.id.conversation_secure_request_agree_read_more_btn) 32 | .setOnClickListener { clickKnowMoreDekuSecurity(it) } 33 | 34 | val requestAddress = view.findViewById(R.id.conversation_secure_request_modal_text) 35 | requestAddress.text = requestAddress.text.replace(Regex("John"), contactName!!) 36 | 37 | view.findViewById(R.id.conversation_secure_request_agree_btn).setOnClickListener { 38 | acceptRunnable!!.run() 39 | activity?.recreate() 40 | dismiss() 41 | } 42 | } 43 | 44 | companion object { 45 | const val TAG = "ModalBottomSheet" 46 | } 47 | 48 | private fun clickKnowMoreDekuSecurity(view: View?) { 49 | val url = getString(R.string.conversations_secure_conversation_request_information_deku_encryption_link) 50 | val shareIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) 51 | view?.context?.startActivity(shareIntent) 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Modals/FailedMessageRetryModal.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Modals 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import android.widget.TextView 6 | import com.afkanerd.deku.DefaultSMS.R 7 | import com.google.android.material.bottomsheet.BottomSheetBehavior 8 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 9 | import com.google.android.material.button.MaterialButton 10 | 11 | class FailedMessageRetryModal(private val onClickedRunnable: Runnable) : 12 | BottomSheetDialogFragment(R.layout.failed_messages_modal_sheet) { 13 | 14 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 15 | super.onViewCreated(view, savedInstanceState) 16 | 17 | val bottomSheet = view.findViewById(R.id.conversation_failed_messages_modal) 18 | val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) 19 | 20 | bottomSheetBehavior.isFitToContents = true 21 | bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED 22 | 23 | view.findViewById(R.id.conversation_failed_message_retry_btn) 24 | .setOnClickListener { 25 | dismiss() 26 | onClickedRunnable.run() 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/Archive.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.room.Entity; 5 | import androidx.room.PrimaryKey; 6 | 7 | @Entity 8 | public class Archive { 9 | @NonNull 10 | @PrimaryKey 11 | public String thread_id; 12 | 13 | public boolean is_archived; 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/Conversations/ConversationsThreadsEncryption.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models.Conversations; 2 | 3 | import androidx.room.Entity; 4 | import androidx.room.Index; 5 | import androidx.room.PrimaryKey; 6 | 7 | @Entity(indices = {@Index(value={"keystoreAlias"}, unique=true)}) 8 | public class ConversationsThreadsEncryption { 9 | 10 | @PrimaryKey(autoGenerate = true) 11 | private long id; 12 | 13 | // DHs comes from here 14 | private String keystoreAlias; 15 | 16 | // DHr comes from here 17 | private String publicKey; 18 | 19 | private String states; 20 | 21 | // would most likely use this for backward compatibility 22 | private long exchangeDate; 23 | 24 | public void setStates(String states) { 25 | this.states = states; 26 | } 27 | 28 | public String getStates() { 29 | return this.states; 30 | } 31 | 32 | public String getKeystoreAlias() { 33 | return keystoreAlias; 34 | } 35 | 36 | public void setKeystoreAlias(String keystoreAlias) { 37 | this.keystoreAlias = keystoreAlias; 38 | } 39 | 40 | public long getId() { 41 | return id; 42 | } 43 | 44 | public void setId(long id) { 45 | this.id = id; 46 | } 47 | 48 | public String getPublicKey() { 49 | return publicKey; 50 | } 51 | 52 | public void setPublicKey(String publicKey) { 53 | this.publicKey = publicKey; 54 | } 55 | 56 | public long getExchangeDate() { 57 | return exchangeDate; 58 | } 59 | 60 | public void setExchangeDate(long exchangeDate) { 61 | this.exchangeDate = exchangeDate; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/Conversations/ThreadedConversationsHandler.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models.Conversations; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.provider.Telephony; 8 | 9 | import com.afkanerd.deku.DefaultSMS.Commons.Helpers; 10 | import com.afkanerd.deku.DefaultSMS.Models.NativeSMSDB; 11 | 12 | public class ThreadedConversationsHandler { 13 | 14 | public static ThreadedConversations get(Context context, String address) { 15 | final String defaultUserCountry = Helpers.getUserCountry(context); 16 | long threadId = Telephony.Threads.getOrCreateThreadId(context, address); 17 | ThreadedConversations threadedConversations = new ThreadedConversations(); 18 | threadedConversations.setAddress(Helpers.getFormatCompleteNumber(address, defaultUserCountry)); 19 | threadedConversations.setThread_id(String.valueOf(threadId)); 20 | return threadedConversations; 21 | } 22 | 23 | public static ThreadedConversations get(Context context, 24 | ThreadedConversations threadedConversations) throws InterruptedException { 25 | // final ThreadedConversations[] threadedConversations1 = {threadedConversations}; 26 | // Thread thread = new Thread(new Runnable() { 27 | // @Override 28 | // public void run() { 29 | // threadedConversations1[0] = threadedConversationsDao 30 | // .get(threadedConversations.getThread_id()); 31 | // } 32 | // }); 33 | // thread.start(); 34 | // thread.join(); 35 | try(Cursor cursor = 36 | NativeSMSDB.fetchByThreadId(context, threadedConversations.getThread_id())) { 37 | if(cursor.moveToFirst()) 38 | threadedConversations = ThreadedConversations.build(cursor); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | } 42 | return threadedConversations; 43 | 44 | // return threadedConversations1[0]; 45 | } 46 | 47 | public static void call(Context context, String address) { 48 | Intent callIntent = new Intent(Intent.ACTION_DIAL); 49 | callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 50 | callIntent.setData(Uri.parse("tel:" + address)); 51 | 52 | context.startActivity(callIntent); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/DatastoreHandler.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.preferencesDataStore 7 | 8 | object DatastoreHandler { 9 | val Context.conversationConfigurationsData: 10 | DataStore by preferencesDataStore(name = "configurations") 11 | 12 | fun getDatastore(context: Context): DataStore { 13 | return context.conversationConfigurationsData 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/DevMode.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | object DevMode { 4 | val viewLogCat = "viewLogCat" 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/Encryption.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | open class Encryption { 4 | 5 | var id: Int = 0 6 | 7 | var state: ByteArray? = null 8 | 9 | var peerPublicKey: ByteArray? = null 10 | 11 | var publicKey: ByteArray? = null 12 | 13 | var peerAddress: String? = null 14 | 15 | var version: Int = 0 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/SMSDatabaseWrapper.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.provider.Telephony; 6 | import android.util.Base64; 7 | import android.util.Log; 8 | 9 | import com.afkanerd.deku.DefaultSMS.Commons.Helpers; 10 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation; 11 | 12 | public class SMSDatabaseWrapper extends NativeSMSDB.Outgoing { 13 | 14 | public static void send_data(Context context, Conversation conversation) throws Exception { 15 | String transmissionAddress = Helpers.getFormatForTransmission(conversation.getAddress(), 16 | Helpers.getUserCountry(context)); 17 | String[] nativeOutputs = NativeSMSDB.Outgoing._send_data(context, conversation.getMessage_id(), 18 | transmissionAddress, Base64.decode(conversation.getData(), Base64.DEFAULT), 19 | conversation.getSubscription_id(), null); 20 | } 21 | 22 | public static void send_text(Context context, Conversation conversation, Bundle bundle) throws Exception { 23 | String transmissionAddress = Helpers.getFormatForTransmission(conversation.getAddress(), 24 | Helpers.getUserCountry(context)); 25 | String[] nativeOutputs = NativeSMSDB.Outgoing._send_text(context, conversation.getMessage_id(), 26 | transmissionAddress, conversation.getText(), 27 | conversation.getSubscription_id(), bundle); 28 | } 29 | 30 | public static void saveDraft(Context context, Conversation conversation) { 31 | Log.d(SMSDatabaseWrapper.class.getName(), "Saving draft: " + conversation.getText()); 32 | String[] outputs = NativeSMSDB.Outgoing.register_drafts(context, conversation.getMessage_id(), 33 | conversation.getAddress(), conversation.getText(), conversation.getSubscription_id()); 34 | } 35 | 36 | public static void deleteDraft(Context context, String threadId) { 37 | NativeSMSDB.deleteTypeForThread(context, 38 | String.valueOf(Telephony.TextBasedSmsColumns.MESSAGE_TYPE_DRAFT), threadId); 39 | } 40 | 41 | public static void deleteAllDraft(Context context) { 42 | NativeSMSDB.deleteAllType(context, 43 | String.valueOf(Telephony.TextBasedSmsColumns.MESSAGE_TYPE_DRAFT)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/SettingsHandler.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | import android.content.Context 4 | import androidx.preference.PreferenceManager 5 | 6 | object SettingsHandler { 7 | fun alertNotEncryptedCommunicationDisabled(context: Context): Boolean { 8 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 9 | return sharedPreferences.getBoolean("encryption_disable", false) 10 | } 11 | 12 | fun canSwipe(context: Context): Boolean { 13 | val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) 14 | return sharedPreferences.getBoolean("swipe_actions", true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/ThreadsConfigurations.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | import androidx.room.Entity 4 | import androidx.room.Index 5 | import androidx.room.PrimaryKey 6 | import kotlinx.serialization.EncodeDefault 7 | import kotlinx.serialization.ExperimentalSerializationApi 8 | 9 | @Entity(indices = [Index(value = ["threadId"], unique = true)]) 10 | open class ThreadsConfigurations { 11 | @PrimaryKey(autoGenerate = true) var id: Long = 0 12 | var threadId: String? = null 13 | var isMute = false 14 | var isArchive = false 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/ThreadsSearch.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | data class ThreadsSearch( 4 | val count: Int, 5 | val threadId: String, 6 | val text: String, 7 | val date: String 8 | ) 9 | 10 | data class ThreadsCount( 11 | val archivedCount: Int, 12 | val unreadCount: Int, 13 | val draftsCount: Int, 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Models/Transmissions.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Models 2 | 3 | import android.app.PendingIntent 4 | import android.content.Context 5 | import android.os.Build 6 | import android.telephony.SmsManager 7 | import com.afkanerd.deku.DefaultSMS.BuildConfig 8 | 9 | object Transmissions { 10 | private const val DATA_TRANSMISSION_PORT: Short = 8200 11 | @Throws(Exception::class) 12 | fun sendTextSMS( 13 | context: Context, 14 | destinationAddress: String, 15 | text: String, 16 | sentIntent: PendingIntent?, 17 | deliveryIntent: PendingIntent?, 18 | subscriptionId: Int 19 | ) { 20 | if (text.isEmpty()) return 21 | 22 | val smsManager = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) 23 | context.getSystemService(SmsManager::class.java) 24 | .createForSubscriptionId(subscriptionId) 25 | else SmsManager.getSmsManagerForSubscriptionId(subscriptionId) 26 | 27 | try { 28 | val dividedMessage = smsManager.divideMessage(text) 29 | if (dividedMessage.size < 2) smsManager.sendTextMessage( 30 | destinationAddress, 31 | null, 32 | text, 33 | sentIntent, 34 | deliveryIntent 35 | ) 36 | else { 37 | val sentPendingIntents = ArrayList() 38 | val deliveredPendingIntents = ArrayList() 39 | 40 | for (i in 0 until dividedMessage.size - 1) { 41 | sentPendingIntents.add(null) 42 | deliveredPendingIntents.add(null) 43 | } 44 | 45 | sentPendingIntents.add(sentIntent) 46 | deliveredPendingIntents.add(deliveryIntent) 47 | 48 | smsManager.sendMultipartTextMessage( 49 | destinationAddress, 50 | null, 51 | dividedMessage, 52 | sentPendingIntents, 53 | deliveredPendingIntents 54 | ) 55 | } 56 | } catch (e: Exception) { 57 | throw Exception(e) 58 | } 59 | } 60 | 61 | @Throws(Exception::class) 62 | fun sendDataSMS( 63 | destinationAddress: String?, data: ByteArray?, 64 | sentIntent: PendingIntent?, deliveryIntent: PendingIntent?, 65 | subscriptionId: Int? 66 | ) { 67 | if (data == null) return 68 | 69 | val smsManager = SmsManager.getSmsManagerForSubscriptionId( 70 | subscriptionId!! 71 | ) 72 | try { 73 | smsManager.sendDataMessage( 74 | destinationAddress, 75 | null, 76 | DATA_TRANSMISSION_PORT, 77 | data, 78 | sentIntent, 79 | deliveryIntent 80 | ) 81 | } catch (e: Exception) { 82 | throw Exception(e) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/Settings/SettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Settings 2 | 3 | import android.content.Intent 4 | import android.preference.Preference 5 | import androidx.appcompat.app.AppCompatDelegate 6 | import androidx.navigation.fragment.NavHostFragment 7 | import androidx.navigation.fragment.findNavController 8 | import androidx.preference.PreferenceFragmentCompat 9 | import com.afkanerd.deku.DefaultSMS.Models.DevMode 10 | import com.afkanerd.deku.DefaultSMS.R 11 | import com.afkanerd.deku.MainActivity 12 | 13 | class SettingsFragment : PreferenceFragmentCompat() { 14 | override fun onCreatePreferences( 15 | savedInstanceState: android.os.Bundle?, 16 | rootKey: kotlin.String? 17 | ) { 18 | setPreferencesFromResource(com.afkanerd.deku.DefaultSMS.R.xml.settings_preferences, rootKey) 19 | 20 | val languagePreference = 21 | findPreference(getString(com.afkanerd.deku.DefaultSMS.R.string.settings_locale)) 22 | 23 | val devModeLogCatPreference = 24 | findPreference("dev_mode") 25 | 26 | languagePreference!!.onPreferenceChangeListener = object : 27 | androidx.preference.Preference.OnPreferenceChangeListener { 28 | override fun onPreferenceChange( 29 | preference: androidx.preference.Preference, 30 | newValue: kotlin.Any? 31 | ): kotlin.Boolean { 32 | val languageLocale = newValue as kotlin.String 33 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { 34 | context?.getSystemService( 35 | android.app.LocaleManager::class.java 36 | )?.applicationLocales = 37 | android.os.LocaleList(java.util.Locale.forLanguageTag(languageLocale)) 38 | } else { 39 | val appLocale = 40 | androidx.core.os.LocaleListCompat.forLanguageTags(languageLocale) 41 | AppCompatDelegate.setApplicationLocales(appLocale) 42 | } 43 | return true 44 | } 45 | } 46 | 47 | devModeLogCatPreference!!.onPreferenceClickListener = object: 48 | androidx.preference.Preference.OnPreferenceClickListener { 49 | override fun onPreferenceClick(preference: androidx.preference.Preference): Boolean { 50 | startActivity( 51 | Intent(requireContext(), MainActivity::class.java).apply { 52 | setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_TASK_ON_HOME) 53 | action = Intent.ACTION_VIEW 54 | putExtra("view", DevMode.viewLogCat) 55 | } 56 | ) 57 | requireActivity().finish() 58 | return true 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.view.MenuItem 6 | import android.view.View 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.appcompat.widget.Toolbar 9 | import androidx.navigation.findNavController 10 | import androidx.navigation.fragment.NavHostFragment 11 | import com.afkanerd.deku.DefaultSMS.Settings.SettingsFragment 12 | import com.afkanerd.deku.MainActivity 13 | 14 | class SettingsActivity : AppCompatActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_settings) 18 | 19 | val myToolbar = findViewById(R.id.settings_toolbar) as Toolbar? 20 | setSupportActionBar(myToolbar) 21 | supportActionBar!!.setDisplayHomeAsUpEnabled(true) 22 | 23 | supportActionBar!!.title = getString(R.string.settings_title) 24 | 25 | supportFragmentManager 26 | .beginTransaction() 27 | .replace(R.id.settings_fragment, SettingsFragment()) 28 | .commit() 29 | } 30 | 31 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 32 | if(item.itemId == android.R.id.home) { 33 | startActivity( 34 | Intent(applicationContext, MainActivity::class.java).apply { 35 | setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_TASK_ON_HOME) 36 | } 37 | ) 38 | finish() 39 | } 40 | return super.onOptionsItemSelected(item) 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/ui/Components/ConvenientMethods.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.ui.Components 2 | 3 | import android.content.ContentValues 4 | import android.content.Context 5 | import android.graphics.Bitmap 6 | import android.graphics.Bitmap.Config 7 | import android.graphics.PorterDuff.Mode 8 | import android.graphics.PorterDuffXfermode 9 | import android.graphics.Rect 10 | import android.graphics.RectF 11 | import android.provider.BlockedNumberContract 12 | import android.telecom.TelecomManager 13 | import android.widget.Toast 14 | import androidx.compose.ui.geometry.Offset 15 | import androidx.compose.ui.graphics.Canvas 16 | import androidx.compose.ui.graphics.ImageBitmap 17 | import androidx.compose.ui.graphics.Paint 18 | import androidx.compose.ui.graphics.asAndroidBitmap 19 | import androidx.compose.ui.graphics.asImageBitmap 20 | import androidx.core.content.ContextCompat.getString 21 | import androidx.core.content.ContextCompat.startActivity 22 | import com.afkanerd.deku.Datastore 23 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation 24 | import kotlinx.coroutines.CoroutineScope 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.launch 27 | import com.afkanerd.deku.DefaultSMS.R 28 | import java.text.DateFormat 29 | import java.text.SimpleDateFormat 30 | import java.util.Date 31 | import androidx.core.graphics.createBitmap 32 | 33 | object ConvenientMethods { 34 | 35 | fun blockContact(context: Context, address: String) { 36 | val contentValues = ContentValues(); 37 | contentValues.put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, address); 38 | val uri = context.contentResolver.insert(BlockedNumberContract.BlockedNumbers.CONTENT_URI, 39 | contentValues); 40 | 41 | Toast.makeText(context, getString(context, R.string.conversations_menu_block_toast), 42 | Toast.LENGTH_SHORT).show(); 43 | val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager 44 | startActivity(context, telecomManager.createManageBlockedNumbersIntent(), null); 45 | } 46 | 47 | fun getRoundedCornerImageBitmap(imageBitmap: ImageBitmap, pixels: Int): Bitmap { 48 | val bitmap = imageBitmap.asAndroidBitmap() 49 | val output = createBitmap(bitmap.width, bitmap.height) 50 | val canvas = android.graphics.Canvas(output) 51 | 52 | val color = 0xff424242.toInt() 53 | val paint = android.graphics.Paint() 54 | val rect = android.graphics.Rect(0, 0, bitmap.width, bitmap.height) 55 | val rectF = android.graphics.RectF(rect) 56 | val roundPx = pixels.toFloat() 57 | 58 | paint.isAntiAlias = true 59 | canvas.drawColor(android.graphics.Color.TRANSPARENT, android.graphics.PorterDuff.Mode.CLEAR) 60 | paint.color = color 61 | canvas.drawRoundRect(rectF, roundPx, roundPx, paint) 62 | 63 | paint.xfermode = android.graphics.PorterDuffXfermode(android.graphics.PorterDuff.Mode.SRC_IN) 64 | canvas.drawBitmap(bitmap, rect, rect, paint) 65 | 66 | return output 67 | } 68 | 69 | fun deriveMetaDate(conversation: Conversation): String{ 70 | val dateFormat: DateFormat = SimpleDateFormat("h:mm a"); 71 | return dateFormat.format(Date(conversation.date!!.toLong())); 72 | } 73 | 74 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/ui/LogcatMain.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.ui 2 | 3 | import androidx.compose.runtime.Composable 4 | 5 | @Composable 6 | fun LogcatMain() { 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/DefaultSMS/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.ui.theme 2 | 3 | import com.afkanerd.deku.DefaultSMS.R 4 | import androidx.compose.material3.Typography 5 | import androidx.compose.ui.text.TextStyle 6 | import androidx.compose.ui.text.font.FontFamily 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.unit.sp 9 | 10 | import androidx.compose.ui.text.googlefonts.GoogleFont 11 | import androidx.compose.ui.text.googlefonts.Font 12 | 13 | val provider = GoogleFont.Provider( 14 | providerAuthority = "com.google.android.gms.fonts", 15 | providerPackage = "com.google.android.gms", 16 | certificates = R.array.com_google_android_gms_fonts_certs 17 | ) 18 | 19 | val bodyFontFamily = FontFamily( 20 | Font( 21 | googleFont = GoogleFont("Public Sans"), 22 | fontProvider = provider, 23 | ) 24 | ) 25 | 26 | val displayFontFamily = FontFamily( 27 | Font( 28 | googleFont = GoogleFont("Public Sans"), 29 | fontProvider = provider, 30 | ) 31 | ) 32 | 33 | // Default Material 3 typography values 34 | val baseline = Typography() 35 | 36 | val AppTypography = Typography( 37 | displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily), 38 | displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily), 39 | displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily), 40 | headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily), 41 | headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily), 42 | headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily), 43 | titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily), 44 | titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily), 45 | titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily), 46 | bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily), 47 | bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily), 48 | bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily), 49 | labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily), 50 | labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily), 51 | labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily), 52 | ) 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Modules/SemaphoreManager.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Modules 2 | 3 | import kotlinx.coroutines.sync.Semaphore 4 | 5 | object SemaphoreManager { 6 | val resourceSemaphore = Semaphore(1) 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Modules/Subroutines.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Modules 2 | 3 | import android.app.role.RoleManager 4 | import android.content.Context 5 | import android.os.Build 6 | import android.provider.Telephony 7 | 8 | object Subroutines { 9 | 10 | fun isDefault(context: Context): Boolean { 11 | return if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 12 | (context.getSystemService(Context.ROLE_SERVICE) as RoleManager) 13 | .isRoleHeld(RoleManager.ROLE_SMS) 14 | } else { 15 | Telephony.Sms.getDefaultSmsPackage(context) == context.packageName 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Modules/ThreadingPoolExecutor.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Modules; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | 6 | public class ThreadingPoolExecutor { 7 | public static final ExecutorService executorService = Executors.newFixedThreadPool(4); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListener/RemoteListenerQueuesViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models.RemoteListener 2 | 3 | import android.content.Context 4 | import androidx.compose.runtime.getValue 5 | import androidx.compose.runtime.mutableStateOf 6 | import androidx.compose.runtime.setValue 7 | import androidx.lifecycle.LiveData 8 | import androidx.lifecycle.MutableLiveData 9 | import androidx.lifecycle.ViewModel 10 | import com.afkanerd.deku.Datastore 11 | import com.afkanerd.deku.RemoteListeners.Models.RemoteListenersQueues 12 | import com.afkanerd.deku.RemoteListeners.RMQ.RMQConnectionHandler 13 | import com.rabbitmq.client.Channel 14 | 15 | class RemoteListenerQueuesViewModel : ViewModel() { 16 | private lateinit var datastore: Datastore 17 | 18 | var remoteListenerQueues by mutableStateOf(null) 19 | private lateinit var liveData : LiveData> 20 | private lateinit var channelsLiveData : LiveData>> 22 | private lateinit var rmqConnectionHandlers: LiveData> 23 | 24 | fun get(context: Context, gatewayClientId: Long): LiveData>{ 25 | datastore = Datastore.getDatastore(context) 26 | if(!::liveData.isInitialized) { 27 | liveData = MutableLiveData() 28 | liveData = datastore.remoteListenersQueuesDao().fetchRemoteListenerQueue(gatewayClientId) 29 | } 30 | return liveData 31 | } 32 | 33 | fun getList(context: Context, gatewayClientId: Long): List { 34 | datastore = Datastore.getDatastore(context) 35 | return datastore.remoteListenersQueuesDao().fetchRemoteListenersQueues(gatewayClientId) 36 | } 37 | 38 | fun insert(remoteListenersQueues: RemoteListenersQueues) { 39 | datastore.remoteListenersQueuesDao().insert(remoteListenersQueues) 40 | } 41 | 42 | fun update(remoteListenersQueues: RemoteListenersQueues) { 43 | datastore.remoteListenersQueuesDao().update(remoteListenersQueues) 44 | } 45 | 46 | fun delete(context: Context, remoteListenerId: Long) { 47 | Datastore.getDatastore(context).remoteListenersQueuesDao().delete(remoteListenerId) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListener/RemoteListenersQueuesDao.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models.RemoteListener 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Delete 6 | import androidx.room.Insert 7 | import androidx.room.OnConflictStrategy 8 | import androidx.room.Query 9 | import androidx.room.Update 10 | import com.afkanerd.deku.RemoteListeners.Models.RemoteListenersQueues 11 | 12 | @Dao 13 | interface RemoteListenersQueuesDao { 14 | @Insert(onConflict = OnConflictStrategy.REPLACE) 15 | fun insert(remoteListenersQueuesList: List) 16 | 17 | @Insert(onConflict = OnConflictStrategy.REPLACE) 18 | fun insert(remoteListenersQueues: RemoteListenersQueues): Long 19 | 20 | @Query("SELECT * FROM RemoteListenersQueues WHERE id = :id") 21 | fun fetch(id: Long): RemoteListenersQueues? 22 | 23 | @Query("SELECT * FROM RemoteListenersQueues WHERE id = :id") 24 | fun fetchLiveData(id: Long): LiveData 25 | 26 | @Query("SELECT * FROM RemoteListenersQueues WHERE gatewayClientId = :gatewayClientId") 27 | fun fetchRemoteListenerQueue(gatewayClientId: Long): LiveData> 28 | 29 | @Query("SELECT * FROM RemoteListenersQueues WHERE gatewayClientId = :gatewayClientId") 30 | fun fetchRemoteListenersQueues(gatewayClientId: Long): List 31 | 32 | @Update 33 | fun update(remoteListenersQueues: RemoteListenersQueues) 34 | 35 | @Query("DELETE FROM RemoteListenersQueues WHERE gatewayClientId = :id") 36 | fun deleteRemoteListenerQueue(id: Long) 37 | 38 | @Query("DELETE FROM RemoteListenersQueues WHERE id = :id") 39 | fun delete(id: Long) 40 | 41 | @Delete 42 | fun delete(remoteListenerQueue: RemoteListenersQueues) 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListener/RemoteListenersViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models.RemoteListener 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.ServiceConnection 7 | import android.os.IBinder 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.lifecycle.LiveData 10 | import androidx.lifecycle.ViewModel 11 | import com.afkanerd.deku.Datastore 12 | import androidx.compose.runtime.setValue 13 | import androidx.compose.runtime.getValue 14 | import androidx.lifecycle.MutableLiveData 15 | import com.afkanerd.deku.RemoteListeners.Models.RemoteListeners 16 | import com.afkanerd.deku.RemoteListeners.RMQ.RMQConnectionHandler 17 | import com.afkanerd.deku.RemoteListeners.RemoteListenerConnectionService 18 | 19 | class RemoteListenersViewModel(context: Context? = null) : ViewModel() { 20 | private lateinit var remoteListenersList: LiveData> 21 | private var rmqConnectionHandlers: LiveData> = MutableLiveData() 22 | 23 | var remoteListener by mutableStateOf(null) 24 | 25 | private lateinit var datastore: Datastore 26 | 27 | var binder: RemoteListenerConnectionService.LocalBinder? = null 28 | 29 | /** Defines callbacks for service binding, passed to bindService(). **/ 30 | val connection = object : ServiceConnection { 31 | 32 | override fun onServiceConnected(className: ComponentName, service: IBinder) { 33 | // We've bound to LocalService, cast the IBinder and get LocalService instance. 34 | binder = service as RemoteListenerConnectionService.LocalBinder 35 | rmqConnectionHandlers = binder!!.getService().getRmqConnections() 36 | } 37 | 38 | override fun onServiceDisconnected(arg0: ComponentName) { 39 | } 40 | } 41 | 42 | init { 43 | context?.let { 44 | Intent(context, RemoteListenerConnectionService::class.java).also { intent -> 45 | context.bindService(intent, connection, Context.BIND_AUTO_CREATE) 46 | } 47 | } 48 | } 49 | 50 | fun getRmqConnections(): LiveData> { 51 | return rmqConnectionHandlers 52 | } 53 | 54 | fun get(context: Context): LiveData> { 55 | datastore = Datastore.getDatastore(context) 56 | if(!::remoteListenersList.isInitialized) { 57 | remoteListenersList = loadGatewayClients() 58 | } 59 | return remoteListenersList 60 | } 61 | 62 | private fun loadGatewayClients() : LiveData> { 63 | return datastore.remoteListenerDAO().fetch() 64 | } 65 | 66 | fun update(context: Context, remoteListeners: RemoteListeners) { 67 | Datastore.getDatastore(context).remoteListenerDAO().update(remoteListeners) 68 | } 69 | 70 | fun insert(context: Context, remoteListeners: RemoteListeners) { 71 | Datastore.getDatastore(context).remoteListenerDAO().insert(remoteListeners) 72 | } 73 | 74 | fun delete(remoteListeners: RemoteListeners) { 75 | datastore.remoteListenerDAO().delete(remoteListeners) 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListenerDAO.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Delete 6 | import androidx.room.Insert 7 | import androidx.room.OnConflictStrategy 8 | import androidx.room.Query 9 | import androidx.room.Update 10 | 11 | @Dao 12 | interface RemoteListenerDAO { 13 | @get:Query("SELECT * FROM RemoteListeners") 14 | val all: List 15 | 16 | @Query("SELECT * FROM RemoteListeners") 17 | fun fetch(): LiveData> 18 | 19 | @Query("SELECT * FROM RemoteListeners WHERE activated = 1") 20 | fun fetchActivated(): List 21 | 22 | @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) 23 | fun insert(remoteListeners: RemoteListeners): Long 24 | 25 | @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) 26 | fun insert(remoteListeners: List) 27 | 28 | @Delete 29 | fun delete(remoteListeners: RemoteListeners): Int 30 | 31 | @Delete 32 | fun delete(remoteListeners: List) 33 | 34 | @Query("SELECT * FROM RemoteListeners WHERE id=:id") 35 | fun fetch(id: Long): RemoteListeners 36 | 37 | @Update 38 | fun update(remoteListeners: RemoteListeners) 39 | 40 | @Update 41 | fun update(remoteListeners: List) 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListeners.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import androidx.room.ColumnInfo 5 | import androidx.room.Entity 6 | import androidx.room.Ignore 7 | import androidx.room.PrimaryKey 8 | 9 | @Entity 10 | class RemoteListeners { 11 | @Ignore 12 | var connectionStatus: String? = null 13 | 14 | @PrimaryKey(autoGenerate = true) 15 | var id: Long = 0 16 | 17 | var date: Long = 0 18 | 19 | var hostUrl: String? = null 20 | 21 | var username: String? = null 22 | 23 | var password: String? = null 24 | 25 | var port: Int = 0 26 | 27 | var friendlyConnectionName: String? = null 28 | 29 | var virtualHost: String? = null 30 | 31 | var connectionTimeout: Int = 10000 32 | 33 | var prefetch_count: Int = 1 34 | 35 | var heartbeat: Int = 30 36 | 37 | var protocol: String = "amqp" 38 | 39 | var projectName: String? = null 40 | 41 | var projectBinding: String? = null 42 | 43 | var projectBinding2: String? = null 44 | 45 | @ColumnInfo(defaultValue = "0") 46 | var activated: Boolean = false 47 | 48 | @ColumnInfo(defaultValue = "0") 49 | var state = 0 50 | 51 | companion object { 52 | val DIFF_CALLBACK: DiffUtil.ItemCallback = 53 | object : DiffUtil.ItemCallback() { 54 | override fun areItemsTheSame(oldItem: RemoteListeners, newItem: RemoteListeners): 55 | Boolean { 56 | return oldItem.id == newItem.id 57 | } 58 | override fun areContentsTheSame(oldItem: RemoteListeners, newItem: RemoteListeners): 59 | Boolean { 60 | return oldItem == newItem 61 | } 62 | } 63 | 64 | const val STATE_DISCONNECTED = 0 65 | const val STATE_RECONNECTING = 1 66 | const val STATE_CONNECTED = 2 67 | const val STATE_INITIALIZING = 3 68 | const val GATEWAY_CLIENT_ID = "GATEWAY_CLIENT_ID" 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/Models/RemoteListenersQueues.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.Models 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity 8 | class RemoteListenersQueues { 9 | @PrimaryKey(autoGenerate = true) 10 | var id: Long = 0 11 | var gatewayClientId: Long = 0 12 | 13 | var name: String? = null 14 | var binding1Name: String? = null 15 | var binding2Name: String? = null 16 | 17 | 18 | override fun equals(obj: Any?): Boolean { 19 | if (obj is RemoteListenersQueues) { 20 | val remoteListenersQueues = obj 21 | 22 | return remoteListenersQueues.id == this.id && 23 | remoteListenersQueues.name == this.name && 24 | remoteListenersQueues.binding1Name == this.binding1Name && 25 | remoteListenersQueues.binding2Name == this.binding2Name && 26 | remoteListenersQueues.gatewayClientId == this.gatewayClientId 27 | } 28 | return false 29 | } 30 | 31 | override fun hashCode(): Int { 32 | var result = id.hashCode() 33 | result = 31 * result + gatewayClientId.hashCode() 34 | result = 31 * result + (name?.hashCode() ?: 0) 35 | result = 31 * result + (binding1Name?.hashCode() ?: 0) 36 | result = 31 * result + (binding2Name?.hashCode() ?: 0) 37 | return result 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/RMQ/RMQLongRunningConnectionWorker.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.RMQ 2 | 3 | import android.app.Notification 4 | import android.app.PendingIntent 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.pm.ServiceInfo 8 | import android.os.Build 9 | import android.provider.Settings.Global.getString 10 | import androidx.annotation.RequiresApi 11 | import androidx.core.app.NotificationCompat 12 | import androidx.work.CoroutineWorker 13 | import androidx.work.ForegroundInfo 14 | import androidx.work.WorkerParameters 15 | import com.afkanerd.deku.DefaultSMS.R 16 | import com.afkanerd.deku.MainActivity 17 | import com.google.common.util.concurrent.Service 18 | 19 | 20 | class RMQLongRunningConnectionWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) { 21 | override suspend fun doWork(): Result { 22 | setForeground(createForegroundNotification()) 23 | while(true) { 24 | Thread.sleep(1000*10) 25 | break 26 | } 27 | return Result.success() 28 | } 29 | 30 | 31 | private fun createForegroundNotification() : ForegroundInfo{ 32 | val notificationIntent = Intent(applicationContext, MainActivity::class.java) 33 | val pendingIntent = PendingIntent 34 | .getActivity(applicationContext, 35 | 0, 36 | notificationIntent, 37 | PendingIntent.FLAG_IMMUTABLE) 38 | 39 | val title = "Long running..." 40 | val description = "" 41 | 42 | val notification = 43 | NotificationCompat.Builder( applicationContext, 44 | applicationContext.getString(R.string.running_gateway_clients_channel_id)) 45 | .setContentTitle(title) 46 | .setContentText("Status") 47 | .setSmallIcon(R.drawable.ic_stat_name) 48 | .setPriority(NotificationCompat.PRIORITY_LOW) 49 | .setSilent(true) 50 | .setOngoing(true) 51 | .setContentIntent(pendingIntent) 52 | .setStyle(NotificationCompat.BigTextStyle() 53 | .bigText(description)) 54 | .build() 55 | .apply { 56 | flags = Notification.FLAG_ONGOING_EVENT 57 | } 58 | 59 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 60 | ForegroundInfo(0, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC) 61 | } else { 62 | ForegroundInfo(0, notification) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/RMQ/RMQWorkManager.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners.RMQ 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import android.widget.Toast 6 | import androidx.work.Worker 7 | import androidx.work.WorkerParameters 8 | import com.afkanerd.deku.RemoteListeners.Models.RemoteListeners 9 | import kotlinx.coroutines.CoroutineScope 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.launch 12 | import java.io.IOException 13 | import java.net.UnknownHostException 14 | import java.util.concurrent.TimeoutException 15 | 16 | 17 | class RMQWorkManager( 18 | context: Context, 19 | workerParams: WorkerParameters, 20 | ) : Worker(context, workerParams) { 21 | override fun doWork(): Result { 22 | val remoteListenersId = inputData.getLong(RemoteListeners.GATEWAY_CLIENT_ID, -1) 23 | 24 | try { 25 | RMQConnectionWorker(applicationContext, remoteListenersId).start().let { 26 | if(!it.connection.isOpen) return Result.failure() 27 | } 28 | } catch(e: Exception) { 29 | e.printStackTrace() 30 | when(e) { 31 | is TimeoutException, is UnknownHostException -> { 32 | e.printStackTrace() 33 | return Result.retry() 34 | } 35 | is IOException -> { 36 | CoroutineScope(Dispatchers.Main).launch { 37 | Toast.makeText( 38 | applicationContext, 39 | e.cause?.message ?: "", 40 | Toast.LENGTH_LONG 41 | ).show() 42 | } 43 | return Result.failure() 44 | } 45 | else -> { 46 | Log.e(javaClass.name, "Exception connecting rmq", e) 47 | return Result.failure() 48 | } 49 | } 50 | } 51 | return Result.success() 52 | } 53 | 54 | override fun onStopped() { 55 | super.onStopped() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/RemoteListeners/RemoteListenerConnectionServiceInitializer.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.RemoteListeners 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.pm.PackageManager 7 | import android.os.Build 8 | import androidx.activity.result.contract.ActivityResultContracts 9 | import androidx.core.content.ContextCompat 10 | import androidx.startup.Initializer 11 | import androidx.work.WorkManagerInitializer 12 | import com.afkanerd.deku.Datastore 13 | import com.afkanerd.deku.NotificationsInitializer 14 | import com.afkanerd.deku.RemoteListeners.Models.RemoteListenersHandler 15 | import com.google.accompanist.permissions.ExperimentalPermissionsApi 16 | import com.google.accompanist.permissions.rememberPermissionState 17 | import kotlinx.coroutines.CoroutineScope 18 | import kotlinx.coroutines.Dispatchers 19 | import kotlinx.coroutines.launch 20 | 21 | class RemoteListenerConnectionServiceInitializer : Initializer { 22 | override fun create(context: Context): Intent { 23 | val intent = Intent(context, RemoteListenerConnectionService::class.java) 24 | 25 | if(ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS) == 26 | PackageManager.PERMISSION_GRANTED) { 27 | CoroutineScope(Dispatchers.Default).launch { 28 | Datastore.getDatastore(context).remoteListenerDAO().fetchActivated().apply { 29 | if(this.any { it.activated }) { 30 | try { 31 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 32 | context.startForegroundService(intent) 33 | } else { 34 | context.startService(intent) 35 | } 36 | } catch(e: Exception) { 37 | e.printStackTrace() 38 | } 39 | } 40 | } 41 | } 42 | } 43 | return intent 44 | } 45 | 46 | override fun dependencies(): List>> { 47 | return listOf( 48 | WorkManagerInitializer::class.java, 49 | NotificationsInitializer::class.java) 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/FTP.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router; 2 | 3 | public class FTP { 4 | public static String PROTOCOL = "FTP"; 5 | public String ftp_host; 6 | 7 | public String ftp_username; 8 | 9 | public String ftp_password; 10 | public String ftp_remote_path; 11 | 12 | public String ftp_working_directory; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/GatewayServers/GatewayServerDAO.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.GatewayServers; 2 | 3 | import androidx.lifecycle.LiveData; 4 | import androidx.room.Dao; 5 | import androidx.room.Delete; 6 | import androidx.room.Insert; 7 | import androidx.room.OnConflictStrategy; 8 | import androidx.room.Query; 9 | import androidx.room.Update; 10 | 11 | import java.util.List; 12 | 13 | @Dao 14 | public interface GatewayServerDAO { 15 | @Query("SELECT * FROM GatewayServer") 16 | LiveData> getAll(); 17 | 18 | @Query("SELECT * FROM GatewayServer WHERE id IN (:gatewayServerIds)") 19 | List fetch(List gatewayServerIds); 20 | 21 | @Query("SELECT * FROM GatewayServer") 22 | List getAllList(); 23 | 24 | @Query("SELECT * FROM GatewayServer WHERE id=:id") 25 | GatewayServer get(String id); 26 | 27 | 28 | @Insert(onConflict = OnConflictStrategy.REPLACE) 29 | long insert(GatewayServer gatewayServer); 30 | 31 | @Update 32 | void update(GatewayServer gatewayServer); 33 | 34 | @Delete 35 | void delete(GatewayServer gatewayServer); 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/GatewayServers/GatewayServerHandler.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.GatewayServers; 2 | 3 | import com.afkanerd.deku.Datastore; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class GatewayServerHandler { 9 | Datastore databaseConnector; 10 | 11 | public synchronized List getAll() throws InterruptedException { 12 | final List[] gatewayServerList = new List[]{new ArrayList<>()}; 13 | Thread thread = new Thread(new Runnable() { 14 | @Override 15 | public void run() { 16 | GatewayServerDAO gatewayServerDAO = databaseConnector.gatewayServerDAO(); 17 | gatewayServerList[0] = gatewayServerDAO.getAllList(); 18 | } 19 | }); 20 | thread.start(); 21 | thread.join(); 22 | 23 | return gatewayServerList[0]; 24 | } 25 | 26 | public GatewayServer get(long id) throws InterruptedException { 27 | final GatewayServer[] gatewayServer = {new GatewayServer()}; 28 | Thread thread = new Thread(new Runnable() { 29 | @Override 30 | public void run() { 31 | GatewayServerDAO gatewayServerDAO = databaseConnector.gatewayServerDAO(); 32 | gatewayServer[0] = gatewayServerDAO.get(String.valueOf(id)); 33 | } 34 | }); 35 | thread.start(); 36 | thread.join(); 37 | 38 | return gatewayServer[0]; 39 | } 40 | 41 | public void delete(long id) throws InterruptedException { 42 | Thread thread = new Thread(new Runnable() { 43 | @Override 44 | public void run() { 45 | GatewayServerDAO gatewayServerDAO = databaseConnector.gatewayServerDAO(); 46 | GatewayServer gatewayServer = new GatewayServer(); 47 | gatewayServer.setId(id); 48 | gatewayServerDAO.delete(gatewayServer); 49 | } 50 | }); 51 | thread.start(); 52 | thread.join(); 53 | } 54 | 55 | public void add(GatewayServer gatewayServer) throws InterruptedException { 56 | gatewayServer.setDate(System.currentTimeMillis()); 57 | Thread thread = new Thread(new Runnable() { 58 | @Override 59 | public void run() { 60 | GatewayServerDAO gatewayServerDAO = databaseConnector.gatewayServerDAO(); 61 | gatewayServerDAO.insert(gatewayServer); 62 | } 63 | }); 64 | thread.start(); 65 | thread.join(); 66 | } 67 | 68 | 69 | public void update(GatewayServer gatewayServer) throws InterruptedException { 70 | gatewayServer.setDate(System.currentTimeMillis()); 71 | 72 | Thread thread = new Thread(new Runnable() { 73 | @Override 74 | public void run() { 75 | GatewayServerDAO gatewayServerDAO = databaseConnector.gatewayServerDAO(); 76 | gatewayServerDAO.update(gatewayServer); 77 | } 78 | }); 79 | thread.start(); 80 | thread.join(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/GatewayServers/GatewayServerRouterRecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.GatewayServers 2 | 3 | import android.util.Pair 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | import androidx.lifecycle.MutableLiveData 9 | import androidx.recyclerview.widget.AsyncListDiffer 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.afkanerd.deku.DefaultSMS.Commons.Helpers 12 | import com.afkanerd.deku.DefaultSMS.R 13 | import com.afkanerd.deku.Router.Models.RouterItem 14 | import com.afkanerd.deku.Router.SMTP 15 | import com.google.android.material.card.MaterialCardView 16 | 17 | class GatewayServerRouterRecyclerAdapter : 18 | RecyclerView.Adapter() { 19 | 20 | val mDiffer = AsyncListDiffer(this, RouterItem.Companion.DIFF_CALLBACK()) 21 | 22 | val selectedItems: MutableLiveData> = MutableLiveData() 23 | 24 | override fun getItemId(position: Int): Long { 25 | return position.toLong() 26 | } 27 | 28 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 29 | val inflater = LayoutInflater.from(parent.context) 30 | val view = inflater.inflate(R.layout.gateway_server_routed_messages_layout, parent, 31 | false) 32 | return ViewHolder(view) 33 | } 34 | 35 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 36 | val routedMessage: Pair = mDiffer.currentList[position] 37 | holder.bind(routedMessage) 38 | } 39 | 40 | override fun getItemCount(): Int { 41 | return mDiffer.currentList.size 42 | } 43 | 44 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 45 | private var materialCardView: MaterialCardView 46 | private var address: TextView 47 | private var url: TextView 48 | private var body: TextView 49 | private var status: TextView 50 | private var date: TextView 51 | 52 | init { 53 | address = itemView.findViewById(R.id.routed_messages_address) 54 | url = itemView.findViewById(R.id.routed_messages_url) 55 | body = itemView.findViewById(R.id.routed_messages_body) 56 | status = itemView.findViewById(R.id.routed_messages_status) 57 | date = itemView.findViewById(R.id.routed_messages_date) 58 | materialCardView = itemView.findViewById(R.id.routed_messages_material_cardview) 59 | } 60 | 61 | fun bind(conversationGatewayServerPair: Pair) { 62 | val conversation = conversationGatewayServerPair.first 63 | conversationGatewayServerPair.second?.let { 64 | val gatewayServerUrl = when(it.getProtocol()) { 65 | SMTP.PROTOCOL -> it.smtp.smtp_host 66 | else -> it.url 67 | } 68 | val protocolAddress = it.getProtocol() + "/" + conversation.address 69 | address.text = protocolAddress 70 | url.text = gatewayServerUrl 71 | body.text = conversation.text 72 | status.text = conversation.routingStatus 73 | date.text = Helpers.formatDate(itemView.context, conversation.date!!.toLong()) 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/GatewayServers/GatewayServerRouterViewModel.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.GatewayServers; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.lifecycle.LiveData; 6 | import androidx.lifecycle.ViewModel; 7 | import androidx.work.WorkInfo; 8 | 9 | import com.afkanerd.deku.Router.Models.RouterHandler; 10 | 11 | import java.util.List; 12 | 13 | public class GatewayServerRouterViewModel extends ViewModel { 14 | private LiveData> messagesList; 15 | 16 | public LiveData> getMessages(Context context){ 17 | if(messagesList == null) { 18 | messagesList = loadSMSThreads(context); 19 | } 20 | return messagesList; 21 | } 22 | 23 | private LiveData> loadSMSThreads(Context context) { 24 | return RouterHandler.INSTANCE.getMessageIdsFromWorkManagers(context); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/GatewayServers/GatewayServerViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.GatewayServers 2 | 3 | import android.content.Context 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.ViewModel 7 | import com.afkanerd.deku.Datastore 8 | 9 | class GatewayServerViewModel : ViewModel() { 10 | private lateinit var gatewayServersList: LiveData> 11 | 12 | private lateinit var datastore: Datastore 13 | operator fun get(context: Context): LiveData> { 14 | if(!::gatewayServersList.isInitialized) { 15 | datastore = Datastore.getDatastore(context) 16 | gatewayServersList = MutableLiveData() 17 | loadGatewayServers() 18 | } 19 | return gatewayServersList 20 | } 21 | 22 | private fun loadGatewayServers() { 23 | gatewayServersList = datastore.gatewayServerDAO().all 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/Models/RouterItem.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.Models 2 | 3 | import android.database.Cursor 4 | import android.util.Pair 5 | import androidx.recyclerview.widget.DiffUtil 6 | import androidx.work.DelegatingWorkerFactory 7 | import com.afkanerd.deku.DefaultSMS.Models.Conversations.Conversation 8 | import com.afkanerd.deku.Router.GatewayServers.GatewayServer 9 | import com.google.gson.annotations.Expose 10 | import kotlinx.serialization.Serializable 11 | import kotlinx.serialization.encodeToString 12 | import kotlinx.serialization.json.Json 13 | 14 | class RouterItem(val conversation: Conversation) : Conversation(conversation) { 15 | var routingUniqueId: String? = null 16 | var url: String? = null 17 | var routingStatus: String? = null 18 | 19 | fun setConversationTag(tag: String) { 20 | conversation.tag = tag 21 | } 22 | 23 | fun serializeJson() : String { 24 | val json = Json { 25 | prettyPrint = true 26 | } 27 | return json.encodeToString(conversation) 28 | } 29 | 30 | companion object { 31 | fun build(cursor: Cursor?): RouterItem { 32 | return cursor?.let { Conversation.build(it) } as RouterItem 33 | } 34 | class DIFF_CALLBACK : DiffUtil.ItemCallback>() { 35 | override fun areItemsTheSame(oldItem: Pair, 36 | newItem: Pair): Boolean { 37 | // Logic to compare if two items represent the same data (e.g., by ID) 38 | return oldItem.first.routingUniqueId == newItem.first.routingUniqueId 39 | } 40 | 41 | override fun areContentsTheSame(oldItem: Pair, 42 | newItem: Pair): Boolean { 43 | // Logic to compare if content of two items are the same 44 | return oldItem == newItem 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/Models/RouterWorkManager.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.Models 2 | 3 | import android.content.Context 4 | import android.util.Log 5 | import androidx.work.Worker 6 | import androidx.work.WorkerParameters 7 | import com.afkanerd.deku.Datastore 8 | import com.afkanerd.deku.Modules.Network 9 | import com.afkanerd.deku.Router.FTP 10 | import com.afkanerd.deku.Router.SMTP 11 | import com.sun.mail.util.MailConnectException 12 | import kotlinx.serialization.encodeToString 13 | import kotlinx.serialization.json.Json 14 | import javax.mail.MessagingException 15 | import javax.mail.SendFailedException 16 | 17 | class RouterWorkManager (context: Context, workerParams: WorkerParameters) 18 | : Worker(context, workerParams) { 19 | override fun doWork(): Result { 20 | val gatewayServerId = inputData.getLong(GATEWAY_SERVER_ID, -1) 21 | val conversationId = inputData.getString(CONVERSATION_ID)!! 22 | 23 | val datastore = Datastore.getDatastore(applicationContext) 24 | val gatewayServer = datastore.gatewayServerDAO()[gatewayServerId.toString()] 25 | val conversation = datastore.conversationDao().getMessage(conversationId) 26 | 27 | val routerItem = RouterItem(conversation) 28 | routerItem.setConversationTag(gatewayServer.getTag()) 29 | 30 | val jsonStringBody = routerItem.serializeJson() 31 | println(jsonStringBody) 32 | 33 | when(gatewayServer.getProtocol()) { 34 | SMTP.PROTOCOL -> { 35 | try { 36 | RouterHandler.routeSmtpMessages(jsonStringBody, gatewayServer) 37 | } catch (e: Exception) { 38 | e.printStackTrace() 39 | if (e is MailConnectException) { return Result.retry() } 40 | return Result.failure() 41 | } 42 | } 43 | FTP.PROTOCOL -> { 44 | try { 45 | RouterHandler.routeFTPMessages(jsonStringBody, gatewayServer) 46 | } catch (e: Exception) { 47 | Log.e(javaClass.getName(), "Exception: ", e) 48 | return Result.failure() 49 | } 50 | } 51 | else -> { 52 | try { 53 | when(Network.jsonRequestPost(gatewayServer.url, jsonStringBody) 54 | .response.statusCode) { 55 | in 500..600 -> Result.retry() 56 | else -> Result.failure() 57 | } 58 | } catch(e: Exception) { 59 | Log.e(javaClass.name, "Exception routing", e) 60 | Result.retry() 61 | } 62 | } 63 | } 64 | 65 | return Result.success() 66 | } 67 | 68 | companion object { 69 | var GATEWAY_SERVER_ID = "GATEWAY_SERVER_ID" 70 | var CONVERSATION_ID = "CONVERSATION_ID" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/SMTP.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router; 2 | 3 | public class SMTP { 4 | public static String PROTOCOL = "SMTP"; 5 | 6 | public String smtp_host; 7 | 8 | public String smtp_username; 9 | 10 | public String smtp_password; 11 | 12 | public String smtp_from; 13 | public String smtp_recipient; 14 | 15 | public String smtp_subject; 16 | 17 | public int smtp_port = 587; 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/Settings/GatewayServerSettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.Settings 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.appcompat.widget.Toolbar 7 | import com.afkanerd.deku.DefaultSMS.R 8 | 9 | class GatewayServerSettingsActivity : AppCompatActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | setContentView(R.layout.activity_gateway_server_settings) 13 | 14 | val myToolbar = findViewById(R.id.gateway_server_toolbar) as Toolbar 15 | setSupportActionBar(myToolbar) 16 | supportActionBar!!.setDisplayHomeAsUpEnabled(true) 17 | 18 | supportActionBar!!.title = getString(R.string.gateway_server_settings_title) 19 | 20 | supportFragmentManager 21 | .beginTransaction() 22 | .replace(R.id.gateway_server_settings_fragment_container, 23 | GatewayServerSettingsFragment()) 24 | .commit() 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/Router/Settings/GatewayServerSettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.Router.Settings 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import com.afkanerd.deku.DefaultSMS.R 6 | 7 | class GatewayServerSettingsFragment : PreferenceFragmentCompat() { 8 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 9 | setPreferencesFromResource(R.xml.gateway_server_settings_preferences, rootKey) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/afkanerd/deku/screens.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | object HomeScreen 7 | @Serializable 8 | object ConversationsScreen 9 | @Serializable 10 | object ComposeNewMessageScreen 11 | @Serializable 12 | object SearchThreadScreen 13 | @Serializable 14 | object ContactDetailsScreen 15 | 16 | // Remote Listeners 17 | @Serializable 18 | object RemoteListenersScreen 19 | @Serializable 20 | object RemoteListenersQueuesScreen 21 | @Serializable 22 | object RemoteListenersAddScreen 23 | 24 | @Serializable 25 | object LogcatScreen 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/drawable-hdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/drawable-mdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/drawable-xhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/drawable-xxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_account_circle_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_add_link_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/baseline_route_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/compose_message_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/github_mark.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_add_circle_outline_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_computer_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_content_copy_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_delete_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_language_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/round_stop_circle_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_forward_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/font/roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/font/roboto.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/font/roboto_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_condensed_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/font/roboto_condensed_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/roboto_medium_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/font/roboto_medium_italic.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gateway_server_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gateway_servers_listing_activitiy.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 29 | 30 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_router.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 28 | 29 | 33 | 34 | 35 | 36 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/failed_messages_modal_sheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 24 | 25 | 29 | 30 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_gateway_sever_add_routing_format_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | 17 | 26 | 27 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_modal_secure_request.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 24 | 37 | 38 | 47 | 48 | 49 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_modalsheet_gateway_server.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_modalsheet_secure_request_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 26 | 27 | 36 | 37 | 44 | 45 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_conversation_contact_card_modalsheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_client_listing_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_client_project_listing_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 15 | 16 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_server_add_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_server_listing_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_server_routed_list_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/gateway_server_routed_menu_items_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/raw/.gitignore: -------------------------------------------------------------------------------- 1 | app.properties 2 | smtp.properties 3 | ftp.properties 4 | -------------------------------------------------------------------------------- /app/src/main/res/raw/app_example.properties: -------------------------------------------------------------------------------- 1 | username= 2 | password= 3 | host= 4 | virtualhost= 5 | port= 6 | exchange= -------------------------------------------------------------------------------- /app/src/main/res/raw/ftp_example.properties: -------------------------------------------------------------------------------- 1 | host= 2 | port= 3 | username= 4 | password= 5 | directory=/ 6 | remotePath=/ 7 | -------------------------------------------------------------------------------- /app/src/main/res/raw/smtp_example.properties: -------------------------------------------------------------------------------- 1 | username= 2 | password= 3 | email_from= 4 | email_to= 5 | host= 6 | port= -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @string/language_english 5 | @string/language_french 6 | @string/language_russian 7 | @string/language_german 8 | @string/language_polish 9 | 10 | 11 | @string/language_english_value 12 | @string/language_french_value 13 | @string/language_russian_value 14 | @string/language_german_value 15 | @string/language_polish_value 16 | 17 | 18 | 24 | 25 | #DB4437 26 | #E91E63 27 | #9C27B0 28 | #673AB7 29 | #3F51B5 30 | #4285F4 31 | #039BE5 32 | #0097A7 33 | #009688 34 | #0F9D58 35 | #689F38 36 | #EF6C00 37 | #FF5722 38 | #757575 39 | 40 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/values/key_request_initiated.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | 20 | 21 | 22 | 23 | 28 | 29 | 34 | 35 | 38 | 39 | 42 | 43 | 46 | 47 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/xml/communication_encryption_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/developer_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/xml/gateway_server_settings_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/xml/locales_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 16 | 17 | 18 | 20 | 21 | 26 | 27 | 28 | 29 | 31 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/test/java/com/afkanerd/deku/DefaultSMS/Commons/JavaMethodsTest.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku.DefaultSMS.Commons; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | 5 | import android.database.Cursor; 6 | 7 | import com.google.common.primitives.Bytes; 8 | 9 | import org.junit.Test; 10 | 11 | public class JavaMethodsTest { 12 | 13 | @Test 14 | public void byteConcatTest(){ 15 | byte[] byte1 = new byte[]{0x01}; 16 | byte[] byte2 = new byte[]{0x02}; 17 | 18 | byte[] expected = new byte[]{byte1[0], byte2[0]}; 19 | byte[] output = Bytes.concat(byte1, byte2); 20 | 21 | assertArrayEquals(expected, output); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/test/java/com/afkanerd/deku/RandomTest.java: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku; 2 | 3 | import static org.junit.Assert.assertArrayEquals; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import android.util.Log; 8 | 9 | import org.junit.Test; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class RandomTest { 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/test/java/com/afkanerd/deku/TemplateTest.kt: -------------------------------------------------------------------------------- 1 | package com.afkanerd.deku 2 | 3 | import org.junit.Test 4 | 5 | class TemplateTest { 6 | 7 | object Sample { 8 | var n = 0 9 | var v = 0 10 | } 11 | @Test fun sampleObjectThreadTest() { 12 | println(Sample.v) 13 | Sample.v++ 14 | val t = Thread { 15 | println(Sample.v) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext { 4 | // kotlin_version = '1.8.20-RC' 5 | // kotlin_version = '1.9.23' 6 | kotlin_version = '2.0.0' 7 | agp_version = '8.5.0' 8 | agp_version1 = '8.8.0' 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath "com.android.tools.build:gradle:$agp_version1" 16 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 17 | 18 | // NOTE: Do not place your application dependencies here; they belong 19 | // in the individual module build.gradle files 20 | } 21 | } 22 | 23 | plugins { 24 | // Existing plugins 25 | alias(libs.plugins.compose.compiler) apply false 26 | alias(libs.plugins.org.jetbrains.kotlin.android) apply false 27 | } 28 | 29 | tasks.register('clean', Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /bump_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import os 3 | import sys 4 | import requests 5 | 6 | def get_latest_tag(): 7 | """Get the latest tag from the repository.""" 8 | url = "https://api.github.com/repos/deku-messaging/Deku-SMS-Android/tags" 9 | response = requests.get(url) 10 | response.raise_for_status() 11 | 12 | return response.json()[0]["name"] 13 | 14 | def bump_version(filename, flavour): 15 | """ 16 | Bumps the version number in the specified file. 17 | 18 | Args: 19 | filename: The name of the file to update. 20 | 21 | Returns: 22 | The new version number. 23 | """ 24 | 25 | with open(filename, "r") as f: 26 | lines = f.readlines() 27 | 28 | releaseVersion = None 29 | stagingVersion = None 30 | nightlyVersion = None 31 | tagVersion = get_latest_tag() 32 | 33 | for line in lines: 34 | if line.startswith("releaseVersion="): 35 | releaseVersion = line.split("=")[1].strip() 36 | 37 | if line.startswith("stagingVersion="): 38 | stagingVersion = line.split("=")[1].strip() 39 | 40 | if line.startswith("nightlyVersion="): 41 | nightlyVersion = line.split("=")[1].strip() 42 | 43 | """ 44 | if line.startswith("tagVersion="): 45 | tagVersion = line.split("=")[1].strip() 46 | """ 47 | 48 | if releaseVersion is None: 49 | raise ValueError("Could not find releaseVersion in file") 50 | 51 | if stagingVersion is None: 52 | raise ValueError("Could not find stagingVersion in file") 53 | 54 | if nightlyVersion is None: 55 | raise ValueError("Could not find nightlyVersion in file") 56 | 57 | if tagVersion is None: 58 | raise ValueError("Could not find tagVersion in file") 59 | 60 | """ 61 | if flavour == "refs/heads/master": 62 | releaseVersion = int(releaseVersion) + 1 63 | stagingVersion = 0 64 | nightlyVersion = 0 65 | """ 66 | 67 | if flavour == "refs/heads/staging" or flavour == "refs/heads/master": 68 | stagingVersion = int(stagingVersion) + 1 69 | nightlyVersion = 0 70 | 71 | else: 72 | nightlyVersion = int(nightlyVersion) + 1 73 | 74 | tagVersion = int(tagVersion) + 1 75 | 76 | with open(filename, "w") as f: 77 | f.write("releaseVersion=" + str(releaseVersion) + "\n") 78 | f.write("stagingVersion=" + str(stagingVersion) + "\n") 79 | f.write("nightlyVersion=" + str(nightlyVersion) + "\n") 80 | f.write(f"versionName={str(releaseVersion)}.{str(stagingVersion)}.{str(nightlyVersion)}\n") 81 | f.write("tagVersion=" + str(tagVersion)) 82 | 83 | return releaseVersion, stagingVersion, nightlyVersion, tagVersion 84 | 85 | 86 | if __name__ == "__main__": 87 | filename = "version.properties" 88 | 89 | flavour = sys.argv[1] 90 | releaseVersion, stagingVersion, nightlyVersion, tagVersion = bump_version(filename, flavour) 91 | print("+ successful version bump: ", 92 | releaseVersion, stagingVersion, nightlyVersion, tagVersion) 93 | -------------------------------------------------------------------------------- /ci/.gitignore: -------------------------------------------------------------------------------- 1 | Deku-SMS-Android/ 2 | -------------------------------------------------------------------------------- /ci/ci_cd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | rm -rf Deku-SMS-Android 4 | git clone --recurse-submodules -j8 git@github.com:deku-messaging/Deku-SMS-Android.git 5 | cd Deku-SMS-Android && \ 6 | git checkout staging && \ 7 | git submodule update --init --recursive && \ 8 | cp /root/release.properties . && \ 9 | cp /root/app-release-key.jks app/keys/ && \ 10 | make clean && \ 11 | make release-cd jks_pass="$1" && cd .. \ 12 | -------------------------------------------------------------------------------- /commit-checks/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 AS base 2 | 3 | RUN apt update && \ 4 | apt install -y \ 5 | openjdk-17-jdk \ 6 | openjdk-17-jre \ 7 | android-sdk \ 8 | sdkmanager \ 9 | wget \ 10 | make \ 11 | git 12 | 13 | WORKDIR /android 14 | COPY . . 15 | 16 | ENV ANDROID_HOME "/usr/lib/android-sdk/" 17 | ENV PATH "${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/" 18 | 19 | RUN yes | sdkmanager --licenses 20 | 21 | ENV PASS="" 22 | ARG COMMIT="" 23 | ARG COMMIT_URL="" 24 | ARG RELEASE_URL="" 25 | 26 | RUN wget -O app.apk $RELEASE_URL 27 | RUN git clone $COMMIT_URL app 28 | RUN cd app && \ 29 | git checkout $COMMIT && \ 30 | make && \ 31 | git submodule update --init --recursive 32 | RUN cp *.jks app/app/keys/ 33 | 34 | CMD cd app && \ 35 | ./gradlew assembleRelease && \ 36 | apksigner sign --ks app/keys/app-release-key.jks \ 37 | --ks-pass pass:$PASS \ 38 | --in app/build/outputs/apk/release/app-release-unsigned.apk \ 39 | --out app/build/outputs/apk/release/app-release.apk && \ 40 | shasum ../app.apk && shasum app/build/outputs/apk/release/app-release.apk 41 | -------------------------------------------------------------------------------- /deku_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/deku_chat.png -------------------------------------------------------------------------------- /deku_chat_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/deku_chat_details.png -------------------------------------------------------------------------------- /deku_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/deku_default.png -------------------------------------------------------------------------------- /deku_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/deku_home.png -------------------------------------------------------------------------------- /deku_secure_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/deku_secure_request.png -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | json_key_file("/home/sh3rlock/Documents/smswithoutborders-308610-5473b76d0b41.json") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one 2 | package_name("com.afkanerd.deku") # e.g. com.krausefx.app 3 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | # For a list of all available plugins, check out 9 | # 10 | # https://docs.fastlane.tools/plugins/available-plugins 11 | # 12 | 13 | # Uncomment the line if you want fastlane to automatically update itself 14 | # update_fastlane 15 | 16 | default_platform(:android) 17 | 18 | platform :android do 19 | desc "Runs all the tests" 20 | lane :test do 21 | gradle(task: "test") 22 | end 23 | 24 | desc "Submit a new Beta Build to Crashlytics Beta" 25 | lane :beta do 26 | gradle(task: "clean assembleRelease") 27 | crashlytics 28 | 29 | # sh "your_script.sh" 30 | # You can also use other beta testing services here 31 | end 32 | 33 | desc "Deploy a new version to the Google Play" 34 | lane :deploy do 35 | gradle(task: "clean assembleRelease") 36 | upload_to_play_store 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.17.0.txt: -------------------------------------------------------------------------------- 1 | - New ability for users to remove encryption notifications 2 | - Incoming notifications can now be mark as read or replied to directly from the notification itself 3 | - Routing incoming messages can now be done better with base64 or all messages depending on settings 4 | - Encryption versions of messages can now be displayed in real time as the user types them -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.22.0.txt: -------------------------------------------------------------------------------- 1 | - fix: multi device support for linked devices 2 | - update: seperated workManagers routings into differents items 3 | - update: added About page 4 | - update: fixed issues with broadcast and version builds 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.33.0.txt: -------------------------------------------------------------------------------- 1 | - Request to encrypt conversation turned off by default 2 | - Removed Plain text tab from Homescreen 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.34.0.txt: -------------------------------------------------------------------------------- 1 | - Moved settings around to be more intuitive 2 | - Moved linked devices to Gateway client 3 | - Fixed false request for encryption 4 | - Removed encryption lock for shortcodes 5 | - Fixed dual sim sending button issue 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.35.0.txt: -------------------------------------------------------------------------------- 1 | - option to share message now included 2 | - draft options added to threads 3 | - fix issue with request for secure conversation pop 4 | - added indications for shortcodes 5 | - fix timestamps on messages 6 | - can view the details above a message 7 | - fixed some urls and msisdn recognition issues 8 | - updated db instances to be more stable 9 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.36.0.txt: -------------------------------------------------------------------------------- 1 | - update: removed tabs when no encrypted message is available 2 | - update: fr translation added 3 | - update: localization included in settings 4 | - fix: dates in conversations 5 | - fix: bug fixes that causes database locks 6 | - update: more stable conversation thread screens 7 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.37.0.txt: -------------------------------------------------------------------------------- 1 | - update: new icon 2 | - update: updated UI to hold drafts messages 3 | - fix: issues with drafts now showing in home screen 4 | - fix: issue with loading initial messages 5 | - update: added navbar to conversations thread screen 6 | - update: removed encryption requirements settings because no need for popup encryption message request 7 | - update: can clear all drafts 8 | - fix: broken highlighting for threads 9 | - update: message counter added 10 | - fix: auto suggestions for notifications 11 | - update: forward secrecry now available using Signal's double ratchet 12 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.38.0.txt: -------------------------------------------------------------------------------- 1 | - update: blocking added 2 | - update: added option to mute messages 3 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.39.0.txt: -------------------------------------------------------------------------------- 1 | - fix: RMQ connections now shoes messages in SMS inbox 2 | - fix: Routing to custom cloud breaks when accessing database now fixed 3 | - update: can export messages to a json file 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.40.0.txt: -------------------------------------------------------------------------------- 1 | - update: fixed broken issues with RMQ connections 2 | 3 | - update: once add GatewayClients and use that for all instances 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.41.0.txt: -------------------------------------------------------------------------------- 1 | - update: fix issue with dualsim in pixel devices 2 | 3 | - update: fix broken RMQ connections and reduce channel and connection loads 4 | 5 | - update: optimized for speed and better thread handling 6 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.42.0.txt: -------------------------------------------------------------------------------- 1 | - update: can send encrypted messages to self using ratchets (but single ratchet chain at this time) 2 | 3 | - update: improvement to the key exchange user experience 4 | 5 | - update: UI improvements to unify with logo 6 | 7 | - fixed: issue with loading threads from mobiles with 'Threads' database 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.43.0.txt: -------------------------------------------------------------------------------- 1 | - update: added ability to delete threads inside conversation menu 2 | 3 | - fix: issue with threads and conversations not reflecting a single point of truth 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.44.0.txt: -------------------------------------------------------------------------------- 1 | - update: contacts can now be clicked and copied from within conversations 2 | 3 | - update: Messages can now be forwarded to via SMTP in addition to HTTPS 4 | 5 | - update: Messages can now be forwarded to via FTP in addition to HTTPS 6 | 7 | - update: swipe action now added 8 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.45.0.txt: -------------------------------------------------------------------------------- 1 | - refactor: Gateway client connections 2 | 3 | - update: notify via sound when no Gateway client connected 4 | 5 | - update: improved the UI/UX for retrying failed messages - reduces the chances of error retries 6 | 7 | - fix: secure request not goes to numbers which don't fit the E.16 format 8 | 9 | - update: notify when message fails to send 10 | 11 | - fix: remove refresh button from homescreen (should fix that refresh does not affect saved keys) 12 | 13 | - fix: maintains the last used service provider during conversations for dual sims 14 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/0.46.0.txt: -------------------------------------------------------------------------------- 1 | - fix: issue with sent message not showing up 2 | - fix: tags now show up in forwarded messages 3 | - fix: issue with keys reseting after default app changes or reset button is clicked 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/1.0.0.txt: -------------------------------------------------------------------------------- 1 | - Encryption algorithm changed from EC (secp256k1) to X25519 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Deku SMS is a feature-rich, open-source default SMS app designed to enhance your messaging experience while prioritizing your privacy and security. With Deku SMS, you can seamlessly send and receive end-to-end encrypted SMS messages, ensuring that your conversations remain confidential. 2 | 3 | One of the standout features of Deku SMS is its ability to send images over SMS. This is our experimental feature which allows you to share memorable moments or funny pictures. Our goal for this feature is to help communication in areas with limited connectivity but require the need for communicating important documents or images. 4 | 5 | In addition to its privacy and multimedia capabilities, Deku SMS enables users with the option to configure the app to forward their SMS messages to their online servers. This convenient feature ensures that you can access your messages even when you're away from your device, providing you with greater flexibility and convenience in managing your conversations. 6 | 7 | Key Features: 8 | - End-to-end encryption: Protect your SMS conversations with robust encryption, giving you peace of mind that your messages are secure. 9 | - Image sharing: Easily send and receive images over SMS, making your conversations more engaging and expressive. 10 | - Message forwarding: Configure the app to forward your SMS messages to your online servers, allowing you to access your messages from any device. 11 | 12 | Deku SMS is an open-source project, and we believe in the power of community collaboration. We encourage developers and enthusiasts to explore our codebase on GitHub, where they can contribute to the app's development and customization. We greatly appreciate stars, pull requests, forks and issues. This will greatly help improve the experience for everyone. 13 | 14 | Visit our GitHub repository at: https://github.com/deku-messaging/Deku-SMS-Android -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/featureGraphic.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/1_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/2_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/3_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/4_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/5_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/images/phoneScreenshots/6_en-US.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Deku is an SMS app that supports end to end encryption and photo sharing -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Deku SMS -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/video.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/fastlane/metadata/android/en-US/video.txt -------------------------------------------------------------------------------- /fastlane/metadata/android/ru/full_description.txt: -------------------------------------------------------------------------------- 1 | Deku SMS это SMS чат с открытым исходным кодом, предназначенный для улучшения обмена сообщениями при соблюдении вашей приватности и безопасности. С Deku SMS вы можете беспрепятственно отправлять и получать шифрованные SMS-сообщения, гарантируя что ваши разговоры остаются конфиденциальными. 2 | 3 | Одной из выдающихся возможностей Deku SMS является его способность отправлять изображения по SMS. Это наша экспериментальная функция которая позволяет вам делиться запоминающимися моментами или забавными картинками. Наша цель для этой функции это помочь общению в областях с плохой связью, но где есть необходимость передачи важных документов или изображений. 4 | 5 | В дополнение к своей конфиденциальности и мультимедийным возможностям Deku SMS позволяет пользователям настроить приложение для пересылки своих SMS сообщений на свои онлайн серверы. Эта удобная функция гарантирует что вы можете получить доступ к своим сообщениям даже если вы находитесь вдали от своего устройства, предоставляя вам большую гибкость и удобство в управлении вашими разговорами. 6 | 7 | Ключевые возможности: 8 | - Сквозное шифрование: защитите свои SMS-разговоры с помощью надежного шифрования, давая вам душевное спокойствие что ваши сообщения безопасны. 9 | - Обмен изображениями: легко отправлять и получать изображения по SMS, что делает ваши разговоры более привлекательными и выразительными. 10 | - Переадресация сообщений: Настройте приложение для пересылки ваших SMS на ваши серверы, что позволяет вам получить доступ к вашим сообщениям с любого устройства. 11 | 12 | Deku SMS-это проект с открытым исходным кодом, и мы верим в силу сотрудничества сообщества. Мы призываем разработчиков и энтузиастов изучить наш код на GitHub, где они могут внести свой вклад в разработку и настройку приложения. Мы очень ценим звезды, запросы, вилки и проблемы. Это очень поможет всем. 13 | 14 | Посетите наш репозиторий GitHub: https://github.com/deku-messaging/deku-sms-android -------------------------------------------------------------------------------- /fastlane/metadata/android/ru/short_description.txt: -------------------------------------------------------------------------------- 1 | Deku это SMS приложение со сквозным шифрованием и обменом фотографиями -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | # org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | org.gradle.parallel=true 15 | org.gradle.daemon=true 16 | org.gradle.caching=true 17 | # AndroidX package structure to make it clearer which packages are bundled with the 18 | # Android operating system, and which are packaged with your app"s APK 19 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 20 | android.useAndroidX=true 21 | # Automatically convert third-party libraries to use AndroidX 22 | android.enableJetifier=true 23 | android.defaults.buildfeatures.buildconfig=true 24 | android.nonTransitiveRClass=false 25 | android.nonFinalResIds=false 26 | kotlin.incremental=true 27 | org.gradle.configureondemand=true 28 | 29 | GROUP=com.afkanerd.oss1 30 | POM_ARTIFACT_ID=smswithoutborders_libsignal 31 | VERSION_NAME=0.0.11 32 | 33 | POM_NAME=SMSWithoutBorders libsignal including Double-Ratchet 34 | POM_DESCRIPTION=An implementation of the Double Ratchet Algorithm as provided by Signal protocol 35 | POM_INCEPTION_YEAR=2024 36 | POM_URL=https://github.com/smswithoutborders/lib_signal_double_ratchet_java 37 | 38 | POM_LICENSE_NAME=MIT License 39 | POM_LICENSE_URL=https://mit-license.org/ 40 | POM_LICENSE_DIST=repo 41 | 42 | POM_SCM_URL=https://github.com/smswithoutborders/lib_signal_double_ratchet_java 43 | POM_SCM_CONNECTION=scm:git:https://github.com/smswithoutborders/lib_signal_double_ratchet_java.git 44 | POM_SCM_DEV_CONNECTION=scm:git:https://github.com/smswithoutborders/lib_signal_double_ratchet_java.git 45 | 46 | POM_DEVELOPER_ID=sherlock 47 | POM_DEVELOPER_NAME=Sherlock Wisdom 48 | POM_DEVELOPER_URL=https://github.com/sherlockwisdom/ 49 | 50 | SONATYPE_AUTOMATIC_RELEASE=true 51 | # or when publishing to https://s01.oss.sonatype.org 52 | SONATYPE_HOST=CENTRAL_PORTAL 53 | 54 | RELEASE_SIGNING_ENABLED=true 55 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dekusms/DekuSMS-Android/af1cf0175feb2a9dbfb0513069cb8d0d716e6148/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 29 13:47:19 WAT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /keystore.properties.example: -------------------------------------------------------------------------------- 1 | storePassword= 2 | keyPassword= 3 | keyAlias= 4 | storeFile=keys/app-release-key.jks 5 | -------------------------------------------------------------------------------- /pre-push.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | root_dir=$(git rev-parse --show-toplevel) 4 | branch_name=$(git symbolic-ref HEAD) 5 | 6 | if [ "$branch_name" == "refs/heads/master" ] || [ "$branch_name" == "refs/heads/staging" ]; then 7 | $root_dir/gradlew test 8 | else 9 | exit 0 10 | fi 11 | -------------------------------------------------------------------------------- /publish-maven.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://vanniktech.github.io/gradle-maven-publish-plugin/central/#configuring-the-pom 4 | 5 | # KeyID = last 8 digits of public key 'gpg --list-keys' 6 | # Generate the secring file 7 | # gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg 8 | 9 | ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache 10 | -------------------------------------------------------------------------------- /release.properties.example: -------------------------------------------------------------------------------- 1 | github_token= 2 | google_playstore_creds_filepath= 3 | app_package_name= 4 | github_username= 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cachetools==5.3.1 2 | certifi==2023.7.22 3 | charset-normalizer==3.2.0 4 | google-api-core==2.11.1 5 | google-api-python-client==2.97.0 6 | google-auth==2.22.0 7 | google-auth-httplib2==0.1.0 8 | google-auth-oauthlib==1.0.0 9 | googleapis-common-protos==1.60.0 10 | httplib2==0.22.0 11 | idna==3.4 12 | oauthlib==3.2.2 13 | protobuf==4.24.2 14 | pyasn1==0.5.0 15 | pyasn1-modules==0.3.0 16 | pyparsing==3.1.1 17 | requests==2.31.0 18 | requests-oauthlib==1.3.1 19 | rsa==4.9 20 | six==1.16.0 21 | uritemplate==4.1.1 22 | urllib3==1.26.16 23 | -------------------------------------------------------------------------------- /scripts/do_build.yaml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | package_update: true 4 | packages: 5 | - ca-certificates 6 | - curl 7 | - python-is-python3 8 | - python3-virtualenv 9 | - diffoscope 10 | 11 | runcmd: 12 | - install -m 0755 -d /etc/apt/keyrings 13 | - curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 14 | - chmod a+r /etc/apt/keyrings/docker.asc 15 | - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null 16 | - apt-get update 17 | - apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 18 | - curl -H "Content-type: application/json" -d "{\"message\":\"$(date)\nDroplet done creating... commence to merge to master\"}" staging.smswithoutborders.com:6969/message/deku 19 | -------------------------------------------------------------------------------- /scripts/import.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./populate_db_debug.py && adb push data.json /sdcard/ 4 | -------------------------------------------------------------------------------- /scripts/populate_db_debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | from wonderwords import RandomSentence 5 | 6 | data_array = [] 7 | 8 | threads = set() 9 | ids = 1 10 | _type = 1 11 | s = RandomSentence() 12 | 13 | for i in range((1000//50)): 14 | _len = i+1 * 50 15 | threads.add(str(i+1)) 16 | for j in range(_len): 17 | data = { "_mk": None, 18 | "address": f"+{_len}6505556789", 19 | "data": None, 20 | "date": "1733237444789", 21 | "date_sent": None, 22 | "error_code": 0, 23 | "formatted_date": None, 24 | "id": ids, 25 | "isIs_encrypted": False, 26 | "isIs_image": False, 27 | "isIs_key": False, 28 | "isRead": True, 29 | "message_id": str(f"{i}{j}"), 30 | "num_segments": 0, 31 | "status": 32, 32 | "subscription_id": 0, 33 | "tag": None, 34 | "text": (s.sentence() + " " 35 | + s.bare_bone_sentence() 36 | + " " + s.simple_sentence() 37 | + " " + s.bare_bone_with_adjective()), 38 | "thread_id": str(i+1), 39 | "type": 1 if _type == 2 else 2 40 | } 41 | ids += 1 42 | _type = 1 if _type == 2 else 2 43 | data_array.append(data) 44 | 45 | for thread_id in threads: 46 | count = 0 47 | for data in data_array: 48 | if data['thread_id'] == thread_id: 49 | count += 1 50 | print(thread_id, ":", count) 51 | 52 | print("+ writing:", len(data_array)) 53 | with open('data.json', 'w') as fd: 54 | json.dump(data_array, fd) 55 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 3 | repositories { 4 | google() 5 | mavenCentral() 6 | jcenter() // Warning: this repository is going to shut down soon 7 | } 8 | } 9 | rootProject.name = "swob_server" 10 | include ':app' 11 | include ':smswithoutborders_libsignal-doubleratchet' 12 | -------------------------------------------------------------------------------- /track.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | 5 | if sys.argv[1] == 'staging': 6 | print('beta') 7 | elif sys.argv[1] == 'master' or sys.argv[1] == 'main': 8 | print('production') 9 | else: 10 | print('internal') 11 | -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | releaseVersion=0 2 | stagingVersion=61 3 | nightlyVersion=0 4 | versionName=0.61.0 5 | tagVersion=67 6 | --------------------------------------------------------------------------------