├── .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 |
4 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
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 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/gateway_client_project_listing_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/gateway_server_add_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/gateway_server_listing_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/gateway_server_routed_list_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/gateway_server_routed_menu_items_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------