├── .github
├── FUNDING.yml
└── workflows
│ └── android.yml
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── lint-baseline.xml
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── net
│ │ └── xcreen
│ │ └── restsms
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── net
│ │ │ └── xcreen
│ │ │ └── restsms
│ │ │ ├── AppContext.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── fragments
│ │ │ ├── AboutAppFragment.kt
│ │ │ ├── AboutFragment.kt
│ │ │ ├── AboutMeFragment.kt
│ │ │ ├── AboutThirdPartyLibrarysFragment.kt
│ │ │ ├── HomeFragment.kt
│ │ │ ├── LoggingDetailFragment.kt
│ │ │ ├── LoggingFragment.kt
│ │ │ └── SettingsFragment.kt
│ │ │ └── server
│ │ │ ├── SMSResponse.kt
│ │ │ ├── SMSServer.kt
│ │ │ ├── SMSServlet.kt
│ │ │ ├── SMSWelcomeServlet.kt
│ │ │ ├── ServerLogging.kt
│ │ │ └── ServerService.kt
│ └── res
│ │ ├── drawable-hdpi
│ │ ├── notification_icon.png
│ │ └── notification_stop.png
│ │ ├── drawable-mdpi
│ │ ├── notification_icon.png
│ │ └── notification_stop.png
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable-xhdpi
│ │ ├── notification_icon.png
│ │ └── notification_stop.png
│ │ ├── drawable-xxhdpi
│ │ ├── notification_icon.png
│ │ └── notification_stop.png
│ │ ├── drawable-xxxhdpi
│ │ ├── notification_icon.png
│ │ └── notification_stop.png
│ │ ├── drawable
│ │ ├── check_no.png
│ │ ├── check_yes.png
│ │ ├── ic_launcher_background.xml
│ │ ├── launcher.png
│ │ ├── nav_home.png
│ │ ├── nav_info.png
│ │ ├── nav_logging.png
│ │ ├── nav_logo.png
│ │ ├── nav_settings.png
│ │ └── navigation_view_drawer_item.xml
│ │ ├── layout
│ │ ├── about_third_party_list_item.xml
│ │ ├── activity_main.xml
│ │ ├── fragment_about.xml
│ │ ├── fragment_about_app.xml
│ │ ├── fragment_about_me.xml
│ │ ├── fragment_about_third_party_librarys.xml
│ │ ├── fragment_home.xml
│ │ ├── fragment_logging.xml
│ │ ├── fragment_logging_detail.xml
│ │ ├── fragment_settings.xml
│ │ └── nav_header_main.xml
│ │ ├── menu
│ │ ├── activity_main_drawer.xml
│ │ └── options_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── net
│ └── xcreen
│ └── restsms
│ └── ExampleUnitTest.java
├── build.gradle
├── diagram_draw.io.png
├── diagram_draw.io.xml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | custom: https://paypal.me/xcreen
3 |
--------------------------------------------------------------------------------
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v4
16 | - name: set up JDK 17
17 | uses: actions/setup-java@v4
18 | with:
19 | java-version: '17'
20 | distribution: 'temurin'
21 | - name: Make gradlew executable
22 | run: chmod +x ./gradlew
23 | - name: Build with Gradle
24 | run: ./gradlew build
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | restsms_keystore.txt
10 | restsms_keystore.jks
11 | app/release/*
12 | app/google-services.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Xcreen(David R)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RestSMS
2 |
3 | The RestSMS-App allows you to send SMS via Webservice from your Android-Device.
4 |
5 | 
6 |
7 | ### Requirements:
8 | - Android Version 6.0 or higher
9 | - Android-Device which is able to send SMS
10 |
11 | ### Android-Limit:
12 | Android´s default SMS-Limit are 30 SMS to a single phone number within 30 minutes.
13 | You can change your SMS-Limit for your device (root-permission is **not** required).
14 | #### How to change Android-Limit:
15 | 1. Make sure you have enabled USB-Debugging on your device and you are ready to use ADB.
16 | 2. Connect your device to the pc and open the terminal.
17 | 3. Open the adb-shell via the command: `adb shell`
18 | 4. Change the value of the SMS-Limit to the number of SMS you want to send within the 30 minutes timeframe. Via the command:
19 | `settings put global sms_outgoing_check_max_count 100`
20 | This command allows you to send 100 SMS to a phone number within the 30 minutes timeframe.
21 | 5. If you want to also change the timeframe, you can use the command:
22 | `settings put global sms_outgoing_check_interval_ms 900000`
23 | This command reduces the timeframe to 15 minutes.
24 | If you entered both commands, you would be able to send 100 SMS to a phone number within 15 minutes.
25 | ### API-Usage
26 | The default server-url is `http://127.0.0.1:8080/`.
27 | You can change the port in the settings-menu. The ip can be changed via the public network address, so you can access the api from anywhere.
28 | To send a SMS you can point to your address and use the /send-Endpoint (eg. `http://127.0.0.1:8080/send`).
29 | You have to send `phoneno` and `message` and if you enable authentication also `token` as post-parameter (you can use `form-data` and also `x-www-form-urlencoded`).
30 | A response you get a JSON with a `success` and a `message` variable.
31 |
32 | #### Example Curl (x-www-form-urlencoded)
33 | message = "Hello World"
34 | phoneno = "+4915100000000" (it´s also possible to define a list of phone numbers)
35 | token = "123"
36 | ```shell
37 | curl -X POST http://127.0.0.1:8080/send -H 'Cache-Control: no-cache' -H 'Content-Type: application/x-www-form-urlencoded' -d 'message=Hello%20World&phoneno=%2B4915100000000&token=123'
38 | ```
39 |
40 | #### Successful Response
41 | ```json
42 | {
43 | "success": true
44 | }
45 | ```
46 | ⚠️ That means the SMS got forwarded to your default SMS-App (packagename can be found under App-Information). The SMS still can get stuck in your default SMS-App. For example on a Emulator, you will get success = true, but the default SMS-App cant send the SMS, because the SIM-Card is just emulated. So success = true, does not mean that the SMS was already successful sent.
47 |
48 | #### Failed Response
49 | ```json
50 | {
51 | "message": "message or phoneno parameter are missing!",
52 | "success": false
53 | }
54 | ```
55 | The message variable holds the error-message, so you can adjust your request.
56 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | defaultConfig {
6 | applicationId "net.xcreen.restsms"
7 | minSdkVersion 23
8 | compileSdk 34
9 | targetSdkVersion 34
10 | versionCode 11
11 | versionName "2.5.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | buildFeatures {
21 | buildConfig = true
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_17
25 | targetCompatibility JavaVersion.VERSION_17
26 | }
27 | lint {
28 | baseline file('lint-baseline.xml')
29 | disable 'GoogleAppIndexingWarning'
30 | }
31 | namespace 'net.xcreen.restsms'
32 | }
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation "org.eclipse.jetty:jetty-server:$jetty_version"
37 | implementation "org.eclipse.jetty:jetty-servlet:$jetty_version"
38 | implementation 'com.google.code.gson:gson:2.11.0'
39 | implementation 'com.googlecode.libphonenumber:libphonenumber:8.13.49'
40 | implementation 'org.slf4j:slf4j-simple:1.7.36'
41 | implementation 'androidx.browser:browser:1.8.0'
42 | implementation 'androidx.appcompat:appcompat:1.6.1'
43 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
44 | implementation 'com.google.android.material:material:1.12.0'
45 | implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
46 | implementation 'androidx.core:core-ktx:1.13.1'
47 | implementation 'androidx.preference:preference-ktx:1.2.1'
48 | testImplementation 'junit:junit:4.13.2'
49 | androidTestImplementation 'androidx.test:runner:1.6.2'
50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
51 | }
52 |
--------------------------------------------------------------------------------
/app/lint-baseline.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
9 |
10 |
11 |
14 |
16 |
17 |
18 |
23 |
27 |
28 |
29 |
34 |
38 |
39 |
40 |
45 |
49 |
50 |
51 |
56 |
60 |
61 |
62 |
67 |
71 |
72 |
73 |
76 |
78 |
79 |
80 |
85 |
89 |
90 |
91 |
96 |
100 |
101 |
102 |
107 |
111 |
112 |
113 |
118 |
122 |
123 |
124 |
129 |
133 |
134 |
135 |
140 |
144 |
145 |
146 |
151 |
155 |
156 |
157 |
162 |
166 |
167 |
168 |
173 |
177 |
178 |
179 |
184 |
188 |
189 |
190 |
195 |
199 |
200 |
201 |
206 |
210 |
211 |
212 |
217 |
221 |
222 |
223 |
226 |
228 |
229 |
230 |
233 |
235 |
236 |
237 |
240 |
242 |
243 |
244 |
247 |
249 |
250 |
251 |
254 |
256 |
257 |
258 |
261 |
263 |
264 |
265 |
268 |
270 |
271 |
272 |
275 |
277 |
278 |
279 |
282 |
284 |
285 |
286 |
289 |
291 |
293 |
294 |
295 |
298 |
300 |
302 |
303 |
304 |
307 |
309 |
311 |
312 |
313 |
316 |
318 |
320 |
321 |
322 |
325 |
327 |
329 |
330 |
331 |
336 |
340 |
341 |
342 |
347 |
351 |
352 |
353 |
358 |
362 |
363 |
364 |
369 |
373 |
374 |
375 |
380 |
384 |
385 |
386 |
387 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/net/xcreen/restsms/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("net.xcreen.restsms", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Xcreen/RestSMS/d4b51ce150db56a537d1d003214eeb0755e25cd5/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/net/xcreen/restsms/AppContext.kt:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import net.xcreen.restsms.server.SMSServer
6 |
7 | class AppContext : Application() {
8 |
9 | companion object {
10 | lateinit var appContext: Context
11 | }
12 |
13 | var smsServer = SMSServer()
14 |
15 | override fun onCreate() {
16 | super.onCreate()
17 | appContext = applicationContext
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/net/xcreen/restsms/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms
2 |
3 | import android.app.Activity
4 | import android.app.AlertDialog
5 | import android.content.DialogInterface
6 | import android.os.Bundle
7 | import android.view.Menu
8 | import android.view.MenuItem
9 | import android.widget.Toast
10 | import androidx.activity.OnBackPressedCallback
11 | import androidx.appcompat.app.ActionBarDrawerToggle
12 | import androidx.appcompat.app.AppCompatActivity
13 | import androidx.appcompat.widget.Toolbar
14 | import androidx.core.view.GravityCompat
15 | import androidx.drawerlayout.widget.DrawerLayout
16 | import androidx.fragment.app.Fragment
17 | import com.google.android.material.navigation.NavigationView
18 | import net.xcreen.restsms.fragments.AboutFragment
19 | import net.xcreen.restsms.fragments.HomeFragment
20 | import net.xcreen.restsms.fragments.LoggingFragment
21 | import net.xcreen.restsms.fragments.SettingsFragment
22 | import org.slf4j.impl.SimpleLogger
23 | import java.io.File
24 |
25 | class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
26 | var toolbar: Toolbar? = null
27 | var drawerToggle: ActionBarDrawerToggle? = null
28 | var optionsMenu: Menu? = null
29 | var instance: Activity? = null
30 | var OPTION_ITEM_LOGGING_DELETE_ALL = 1000
31 |
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | super.onCreate(savedInstanceState)
34 | setContentView(R.layout.activity_main)
35 | instance = this
36 |
37 | //Set SLF4J Log-Level
38 | System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO")
39 |
40 | //Init Toolbar
41 | toolbar = findViewById(R.id.toolbar)
42 | setSupportActionBar(toolbar)
43 |
44 | //Init Navigation
45 | val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
46 | drawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
47 | drawerLayout.addDrawerListener(drawerToggle!!)
48 | drawerToggle!!.syncState()
49 | val navigationView: NavigationView = findViewById(R.id.nav_view)
50 | navigationView.setNavigationItemSelectedListener(this)
51 |
52 | //Don't replace Fragment, on orientation-change
53 | if (savedInstanceState == null) {
54 | //Set Home Fragment
55 | val fragmentTransaction = supportFragmentManager.beginTransaction()
56 | try {
57 | val homeFragment: Fragment = HomeFragment::class.java.getDeclaredConstructor().newInstance()
58 | fragmentTransaction.replace(R.id.main_framelayout, homeFragment).commit()
59 | } catch (ex: Exception) {
60 | ex.printStackTrace()
61 | }
62 | }
63 |
64 | onBackPressedDispatcher.addCallback(this, object: OnBackPressedCallback(true) {
65 | override fun handleOnBackPressed() {
66 | if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
67 | drawerLayout.closeDrawer(GravityCompat.START)
68 | } else {
69 | isEnabled = false
70 | onBackPressedDispatcher.onBackPressed()
71 | }
72 | }
73 | })
74 | }
75 |
76 | override fun onCreateOptionsMenu(optionsMenu: Menu): Boolean {
77 | this.optionsMenu = optionsMenu
78 | menuInflater.inflate(R.menu.options_menu, optionsMenu)
79 | return true
80 | }
81 |
82 | override fun onNavigationItemSelected(item: MenuItem): Boolean {
83 | val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
84 | //Remove all Option-Menu-Items
85 | optionsMenu!!.clear()
86 |
87 | //Set new Fragment
88 | val fragment: Fragment
89 | when (item.itemId) {
90 | R.id.nav_settings -> {
91 | fragment = SettingsFragment()
92 | toolbar!!.setTitle(R.string.nav_item_settings)
93 | }
94 | R.id.nav_logging -> {
95 | fragment = LoggingFragment()
96 | toolbar!!.setTitle(R.string.nav_item_logging)
97 | //Add Option-Items (if not exist)
98 | if (optionsMenu!!.findItem(OPTION_ITEM_LOGGING_DELETE_ALL) == null) {
99 | val loggingDeleteAllItem = optionsMenu!!.add(Menu.NONE, OPTION_ITEM_LOGGING_DELETE_ALL, 1, R.string.logging_options_delete_all)
100 | loggingDeleteAllItem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER)
101 | loggingDeleteAllItem.setOnMenuItemClickListener {
102 | val dialogClickListener = DialogInterface.OnClickListener { _, which ->
103 | when (which) {
104 | DialogInterface.BUTTON_POSITIVE -> {
105 | //Get all Logs
106 | val logPath = instance!!.filesDir.absolutePath + File.separator + "logs"
107 | val logDir = File(logPath)
108 | val logFiles = logDir.listFiles()
109 | if (logFiles != null) {
110 | //Delete all Logs
111 | for (logFile in logFiles) {
112 | logFile.delete()
113 | }
114 | }
115 | //Refresh List
116 | val fragmentTransaction = supportFragmentManager.beginTransaction()
117 | fragmentTransaction.replace(R.id.main_framelayout, LoggingFragment()).commit()
118 | //Show Success Message
119 | Toast.makeText(instance, R.string.logging_successful_deleted_all, Toast.LENGTH_LONG).show()
120 | }
121 | DialogInterface.BUTTON_NEGATIVE -> {
122 | }
123 | }
124 | }
125 | val builder = AlertDialog.Builder(instance)
126 | builder.setMessage(getString(R.string.logging_options_delete_all_quest))
127 | .setTitle(getString(R.string.logging_options_delete_all))
128 | .setPositiveButton(getString(R.string.yes), dialogClickListener)
129 | .setNegativeButton(getString(R.string.no), dialogClickListener)
130 | .show()
131 | true
132 | }
133 | }
134 | }
135 | R.id.nav_about -> {
136 | fragment = AboutFragment()
137 | toolbar!!.setTitle(R.string.nav_item_about)
138 | }
139 | R.id.nav_home -> {
140 | fragment = HomeFragment()
141 | toolbar!!.setTitle(R.string.app_name)
142 | }
143 | else -> {
144 | fragment = HomeFragment()
145 | toolbar!!.setTitle(R.string.app_name)
146 | }
147 | }
148 | //Replace Fragment
149 | val fragmentTransaction = supportFragmentManager.beginTransaction()
150 | fragmentTransaction.replace(R.id.main_framelayout, fragment).addToBackStack("fragBack").commit()
151 | drawerLayout.closeDrawer(GravityCompat.START)
152 | return true
153 | }
154 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/xcreen/restsms/fragments/AboutAppFragment.kt:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms.fragments
2 |
3 | import android.Manifest
4 | import android.content.Context
5 | import android.content.pm.PackageManager
6 | import android.graphics.PorterDuff
7 | import android.os.Build
8 | import android.os.Bundle
9 | import android.provider.Telephony
10 | import android.telephony.TelephonyManager
11 | import android.view.LayoutInflater
12 | import android.view.View
13 | import android.view.ViewGroup
14 | import android.widget.ImageView
15 | import android.widget.TextView
16 | import androidx.core.app.ActivityCompat
17 | import androidx.fragment.app.Fragment
18 | import net.xcreen.restsms.BuildConfig
19 | import net.xcreen.restsms.R
20 |
21 | class AboutAppFragment : Fragment() {
22 |
23 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
24 | val rootView = inflater.inflate(R.layout.fragment_about_app, container, false)
25 | //Set Version-Name
26 | val versionResultTV = rootView.findViewById(R.id.about_app_version_result_textview)
27 | versionResultTV.text = BuildConfig.VERSION_NAME
28 | //Set Version
29 | val versionNameResultTV = rootView.findViewById(R.id.about_app_versionname_result_textview)
30 | versionNameResultTV.text = BuildConfig.VERSION_CODE.toString()
31 | //Check if has SMS-Permission
32 | val smsPermissionResultImageView = rootView.findViewById(R.id.about_app_smspermission_result_iv)
33 | try {
34 | smsPermissionResultImageView.setColorFilter(requireContext().getColor(R.color.colorError), PorterDuff.Mode.SRC_ATOP)
35 | if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED) {
36 | smsPermissionResultImageView.setImageDrawable(requireContext().getDrawable(R.drawable.check_yes))
37 | smsPermissionResultImageView.setColorFilter(requireContext().getColor(R.color.colorPrimary), PorterDuff.Mode.SRC_ATOP)
38 | }
39 | } catch (ex: Exception) {
40 | ex.printStackTrace()
41 | }
42 | //Check Sim-State
43 | var primarySim = TelephonyManager.SIM_STATE_ABSENT
44 | var secondarySim = TelephonyManager.SIM_STATE_ABSENT
45 | try {
46 | val telephonyManager = requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
47 | primarySim = telephonyManager.simState
48 | if (Build.VERSION.SDK_INT >= 26) {
49 | primarySim = telephonyManager.getSimState(0)
50 | secondarySim = telephonyManager.getSimState(1)
51 | }
52 | } catch (ex: Exception) {
53 | ex.printStackTrace()
54 | }
55 | //Show Sim-State
56 | val simState1ResultTV = rootView.findViewById(R.id.about_app_sim1state_result_textview)
57 | val primarySimTextID = getTextIdBySimState(primarySim)
58 | if (primarySimTextID != 0) {
59 | simState1ResultTV.setText(primarySimTextID)
60 | }
61 | val simState2ResultTV = rootView.findViewById(R.id.about_app_sim2state_result_textview)
62 | val secondarySimTextID = getTextIdBySimState(secondarySim)
63 | if (secondarySimTextID != 0) {
64 | simState2ResultTV.setText(secondarySimTextID)
65 | }
66 | //Show Default-SMS-App
67 | val defaultSMSApp = Telephony.Sms.getDefaultSmsPackage(context)
68 | val defaultSMSAppResultTV = rootView.findViewById(R.id.about_app_defaultsmsapp_result_textview)
69 | if (defaultSMSApp != null) {
70 | defaultSMSAppResultTV.text = defaultSMSApp
71 | }
72 | return rootView
73 | }
74 |
75 | /**
76 | * Returns the String-ID for the Sim-Status
77 | * @param simState - SimState
78 | * @return id - Text-ID
79 | */
80 | private fun getTextIdBySimState(simState: Int): Int {
81 | return when (simState) {
82 | TelephonyManager.SIM_STATE_ABSENT -> R.string.error_sim_absent
83 | TelephonyManager.SIM_STATE_PIN_REQUIRED -> R.string.error_sim_pin
84 | TelephonyManager.SIM_STATE_PUK_REQUIRED -> R.string.error_sim_puk
85 | TelephonyManager.SIM_STATE_NETWORK_LOCKED -> R.string.error_sim_network_pin
86 | TelephonyManager.SIM_STATE_READY -> R.string.error_sim_ready
87 | TelephonyManager.SIM_STATE_NOT_READY -> R.string.error_sim_not_ready
88 | TelephonyManager.SIM_STATE_PERM_DISABLED -> R.string.error_sim_disabled
89 | TelephonyManager.SIM_STATE_CARD_IO_ERROR -> R.string.error_sim_card_error
90 | TelephonyManager.SIM_STATE_CARD_RESTRICTED -> R.string.error_sim_restricted
91 | else -> 0
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/xcreen/restsms/fragments/AboutFragment.kt:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms.fragments
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.appcompat.widget.LinearLayoutCompat
8 | import androidx.fragment.app.Fragment
9 | import androidx.viewpager2.adapter.FragmentStateAdapter
10 | import androidx.viewpager2.widget.ViewPager2
11 | import com.google.android.material.appbar.AppBarLayout
12 | import com.google.android.material.tabs.TabLayout
13 | import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
14 | import com.google.android.material.tabs.TabLayoutMediator
15 | import net.xcreen.restsms.R
16 |
17 | class AboutFragment : Fragment() {
18 | private var tabLayout: TabLayout? = null
19 | private var viewPager: ViewPager2? = null
20 | private var mainAppBarLayout: AppBarLayout? = null
21 |
22 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
23 | val rootView = inflater.inflate(R.layout.fragment_about, container, false)
24 | if (activity != null) { //Set Tabs
25 | tabLayout = TabLayout(requireActivity())
26 | tabLayout!!.addTab(tabLayout!!.newTab()) // About-Me Tab
27 | tabLayout!!.addTab(tabLayout!!.newTab()) // App-Info Tab
28 | tabLayout!!.addTab(tabLayout!!.newTab()) // Third-Party-Info Tab
29 | tabLayout!!.tabGravity = TabLayout.GRAVITY_FILL
30 | tabLayout!!.setBackgroundColor(resources.getColor(R.color.colorDarkBlack, null))
31 | tabLayout!!.setTabTextColors(resources.getColor(R.color.colorWhite, null), resources.getColor(R.color.colorLightBlue, null))
32 | tabLayout!!.addOnTabSelectedListener(object : OnTabSelectedListener {
33 | override fun onTabSelected(tab: TabLayout.Tab) {
34 | viewPager!!.currentItem = tab.position
35 | }
36 | override fun onTabUnselected(tab: TabLayout.Tab) {}
37 | override fun onTabReselected(tab: TabLayout.Tab) {}
38 | })
39 | //Add TabLayout to the AppBarLayout
40 | mainAppBarLayout = requireActivity().findViewById(R.id.appbar_layout)
41 | mainAppBarLayout!!.addView(tabLayout, LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT))
42 | //Set Page-Adapter to ViewPager
43 | val pagerAdapter = FragmentPageAdapter(this, tabLayout!!.tabCount)
44 | viewPager = rootView.findViewById(R.id.about_view_pager)
45 | viewPager!!.adapter = pagerAdapter
46 | TabLayoutMediator(tabLayout!!, viewPager!!) { tab, position ->
47 | tab.text = when (position) {
48 | 0 -> resources.getText(R.string.about_about_me)
49 | 1 -> resources.getText(R.string.about_app_information)
50 | 2 -> resources.getText(R.string.about_third_party_librarys)
51 | else -> resources.getText(R.string.about_about_me)
52 | }
53 | }.attach()
54 | }
55 | return rootView
56 | }
57 |
58 | override fun onDestroyView() {
59 | super.onDestroyView()
60 | try {
61 | //Remove Tab-Layout
62 | mainAppBarLayout!!.removeView(tabLayout)
63 | }
64 | catch (ex: Exception){
65 | ex.printStackTrace()
66 | }
67 | }
68 | }
69 |
70 | internal class FragmentPageAdapter(fragment: Fragment, private val numberOfTabs: Int) : FragmentStateAdapter(fragment) {
71 | override fun createFragment(position: Int): Fragment {
72 | return when (position) {
73 | 0 -> AboutMeFragment()
74 | 1 -> AboutAppFragment()
75 | 2 -> AboutThirdPartyLibrarysFragment()
76 | else -> AboutMeFragment()
77 | }
78 | }
79 |
80 | override fun getItemCount(): Int {
81 | return numberOfTabs
82 | }
83 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/xcreen/restsms/fragments/AboutMeFragment.kt:
--------------------------------------------------------------------------------
1 | package net.xcreen.restsms.fragments
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import android.widget.Button
8 | import androidx.browser.customtabs.CustomTabColorSchemeParams
9 | import androidx.fragment.app.Fragment
10 | import androidx.browser.customtabs.CustomTabsIntent
11 |
12 | import net.xcreen.restsms.R
13 |
14 | class AboutMeFragment : Fragment() {
15 |
16 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
17 | val rootView = inflater.inflate(R.layout.fragment_about_me, container, false)
18 | val customTabColorSchemeParams = CustomTabColorSchemeParams.Builder()
19 | .setToolbarColor(resources.getColor(R.color.colorDarkBlack, null))
20 | .setNavigationBarColor(resources.getColor(R.color.colorDarkBlack, null))
21 | .build()
22 |
23 | val donateBtn = rootView.findViewById