├── backend ├── log.txt ├── .gitignore ├── info.txt └── index.php ├── .idea ├── .name ├── compiler.xml ├── vcs.xml ├── misc.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── jarRepositories.xml ├── codeStyles │ └── Project.xml └── uiDesigner.xml ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── layout │ │ │ │ ├── switch_item.xml │ │ │ │ ├── log_item.xml │ │ │ │ └── activity_main.xml │ │ │ ├── xml │ │ │ │ └── ussd_service.xml │ │ │ └── menu │ │ │ │ └── main_menu.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── ibnux │ │ │ │ └── smsgateway │ │ │ │ ├── data │ │ │ │ ├── UssdData.java │ │ │ │ ├── LogLine.java │ │ │ │ ├── PaginationListener.java │ │ │ │ └── LogAdapter.java │ │ │ │ ├── ObjectBox.java │ │ │ │ ├── layanan │ │ │ │ ├── BootReceiver.java │ │ │ │ ├── BackgroundService.java │ │ │ │ ├── SmsListener.java │ │ │ │ ├── UssdService.java │ │ │ │ └── PushService.java │ │ │ │ ├── Aplikasi.java │ │ │ │ ├── Utils │ │ │ │ ├── SimInfo.java │ │ │ │ ├── TelephonyInfo.java │ │ │ │ ├── Fungsi.java │ │ │ │ └── SimUtil.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── ibnux │ │ │ └── smsgateway │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── ibnux │ │ └── smsgateway │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro ├── objectbox-models │ ├── default.json.bak │ └── default.json └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .github └── FUNDING.yml ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /backend/log.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | SMS Gateway -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.token -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name='SMS Gateway' 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibnux/Android-SMS-Gateway/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ibnux 4 | custom: ['https://paypal.me/ibnux', 'https://trakteer.id/ibnux'] 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/data/UssdData.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.data; 2 | 3 | public class UssdData { 4 | public String to =""; 5 | public int sim = 0; 6 | } 7 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | local.properties 4 | .idea/caches 5 | .idea/libraries 6 | .idea/sonarlint 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 | app/google-services.json 17 | app/release/ 18 | *.apk -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/data/LogLine.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.data; 2 | 3 | import io.objectbox.annotation.Entity; 4 | import io.objectbox.annotation.Id; 5 | 6 | @Entity 7 | public class LogLine { 8 | @Id 9 | public long id; 10 | public long time; 11 | public String date; 12 | public String message; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SMS Gateway 3 | Listen to USSD Request and send it to server 4 | ussd 5 | cd 6 | / 7 | uval 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/ibnux/smsgateway/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/switch_item.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/xml/ussd_service.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/ObjectBox.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway; 2 | 3 | import android.content.Context; 4 | 5 | import com.ibnux.smsgateway.data.MyObjectBox; 6 | 7 | import io.objectbox.BoxStore; 8 | 9 | public class ObjectBox { 10 | private static BoxStore boxStore; 11 | 12 | public static void init(Context context) { 13 | boxStore = MyObjectBox.builder() 14 | .androidContext(context.getApplicationContext()) 15 | .build(); 16 | } 17 | 18 | public static BoxStore get() { return boxStore; } 19 | } 20 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/layanan/BootReceiver.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.layanan; 2 | 3 | /** 4 | * Created by Ibnu Maksum 2020 5 | */ 6 | 7 | import android.content.BroadcastReceiver; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | 11 | public class BootReceiver extends BroadcastReceiver { 12 | 13 | @Override 14 | public void onReceive(Context context, Intent intent) { 15 | if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { 16 | Intent serviceIntent = new Intent(context, BackgroundService.class); 17 | context.startService(serviceIntent); 18 | context.startService(new Intent(context, UssdService.class)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /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/main/java/com/ibnux/smsgateway/Aplikasi.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway; 2 | 3 | /** 4 | * Created by Ibnu Maksum 2020 5 | */ 6 | 7 | import android.app.Application; 8 | import android.content.SharedPreferences; 9 | 10 | import java.util.UUID; 11 | 12 | public class Aplikasi extends Application { 13 | 14 | public static Application app; 15 | public static String secret; 16 | private SharedPreferences sp; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | this.app = this; 22 | ObjectBox.init(this); 23 | sp = getSharedPreferences("pref",0); 24 | secret = sp.getString("secret",null); 25 | if(secret==null){ 26 | secret = UUID.randomUUID().toString(); 27 | sp.edit().putString("secret", secret).apply(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ibnux/smsgateway/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway; 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 org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.ibnux.smsgateway", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/info.txt: -------------------------------------------------------------------------------- 1 | 2 |
 3 | [PARAMATER]
 4 | GET or POST
 5 | 
 6 | to : phone number
 7 | text : text message
 8 | secret : md5 secret key with time
 9 | deviceID : Device ID from app
10 | time : time second used for secret
11 | 
12 | [PHP EXAMPLE]
13 | <?php
14 | 
15 | //Set to your time zone in phone
16 | date_default_timezone_set('Asia/Jakarta');
17 | 
18 | $time = time();
19 | $deviceID = "sadsdgsadasfsf";
20 | $secret = "b0ecc194-ibnu-ibnu-b12b-bc7e8c60919b";
21 | $secret = md5($secret.$time);
22 | 
23 | // USING GET
24 | echo file_get_contents("https://sms.ibnux.net/?to=".urlencode("+62818123456")."&text=".urlencode("hello world")."&secret=$secret&time=$time&deviceID=".urlencode($deviceID));
25 | 
26 | // with POST, you don't need urlencode
27 | 
28 | ?>
29 | 
30 | 
31 | Download android apps at github
32 | 
-------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/Utils/SimInfo.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.Utils; 2 | 3 | public class SimInfo { 4 | private int id_; 5 | private String display_name; 6 | private String icc_id; 7 | private int slot; 8 | 9 | public SimInfo(int id_, String display_name, String icc_id, int slot) { 10 | this.id_ = id_; 11 | this.display_name = display_name; 12 | this.icc_id = icc_id; 13 | this.slot = slot; 14 | } 15 | 16 | public int getId_() { 17 | return id_; 18 | } 19 | 20 | public String getDisplay_name() { 21 | return display_name; 22 | } 23 | 24 | public String getIcc_id() { 25 | return icc_id; 26 | } 27 | 28 | public int getSlot() { 29 | return slot; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "SimInfo{" + 35 | "id_=" + id_ + 36 | ", display_name='" + display_name + '\'' + 37 | ", icc_id='" + icc_id + '\'' + 38 | ", slot=" + slot + 39 | '}'; 40 | } 41 | } -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 16 | 17 | 20 | 21 | 24 | 25 | 28 | 29 | 32 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/log_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 26 | 30 | 31 | -------------------------------------------------------------------------------- /app/objectbox-models/default.json.bak: -------------------------------------------------------------------------------- 1 | { 2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", 3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", 4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", 5 | "entities": [ 6 | { 7 | "id": "1:853953005959405758", 8 | "lastPropertyId": "3:8298078138931550946", 9 | "name": "LogLine", 10 | "properties": [ 11 | { 12 | "id": "1:7468674126049701597", 13 | "name": "id", 14 | "type": 6, 15 | "flags": 1 16 | }, 17 | { 18 | "id": "2:3743244855524059654", 19 | "name": "time", 20 | "type": 6, 21 | "flags": 4 22 | }, 23 | { 24 | "id": "3:8298078138931550946", 25 | "name": "message", 26 | "type": 9 27 | } 28 | ], 29 | "relations": [] 30 | } 31 | ], 32 | "lastEntityId": "1:853953005959405758", 33 | "lastIndexId": "0:0", 34 | "lastRelationId": "0:0", 35 | "lastSequenceId": "0:0", 36 | "modelVersion": 5, 37 | "modelVersionParserMinimum": 5, 38 | "retiredEntityUids": [], 39 | "retiredIndexUids": [], 40 | "retiredPropertyUids": [], 41 | "retiredRelationUids": [], 42 | "version": 1 43 | } -------------------------------------------------------------------------------- /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=-Xmx1536M \ 10 | --add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ 11 | --add-opens=java.base/java.lang=ALL-UNNAMED \ 12 | --add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ 13 | --add-opens=java.base/java.io=ALL-UNNAMED \ 14 | --add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | # AndroidX package structure to make it clearer which packages are bundled with the 20 | # Android operating system, and which are packaged with your app's APK 21 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 22 | android.useAndroidX=true 23 | # Automatically convert third-party libraries to use AndroidX 24 | android.enableJetifier=true 25 | 26 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.google.gms.google-services' 3 | apply plugin: 'io.objectbox' 4 | 5 | android { 6 | compileSdkVersion 29 7 | buildToolsVersion "29.0.2" 8 | defaultConfig { 9 | //change applicationId if you want to use your own Firebase 10 | applicationId "com.takobana.smsgateway" 11 | minSdkVersion 19 12 | //noinspection ExpiredTargetSdkVersion 13 | targetSdkVersion 29 14 | versionCode 5 15 | versionName "3.0" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | android { 26 | compileOptions { 27 | sourceCompatibility 1.8 28 | targetCompatibility 1.8 29 | } 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation fileTree(dir: 'libs', include: ['*.jar']) 35 | implementation 'androidx.appcompat:appcompat:1.1.0' 36 | implementation 'com.google.firebase:firebase-messaging:19.0.1' 37 | implementation 'com.karumi:dexter:6.1.0' 38 | testImplementation 'junit:junit:4.12' 39 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 41 | } 42 | -------------------------------------------------------------------------------- /app/objectbox-models/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.", 3 | "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", 4 | "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", 5 | "entities": [ 6 | { 7 | "id": "1:853953005959405758", 8 | "lastPropertyId": "4:4105318653413103689", 9 | "name": "LogLine", 10 | "properties": [ 11 | { 12 | "id": "1:7468674126049701597", 13 | "name": "id", 14 | "type": 6, 15 | "flags": 1 16 | }, 17 | { 18 | "id": "2:3743244855524059654", 19 | "name": "time", 20 | "type": 6, 21 | "flags": 4 22 | }, 23 | { 24 | "id": "3:8298078138931550946", 25 | "name": "message", 26 | "type": 9 27 | }, 28 | { 29 | "id": "4:4105318653413103689", 30 | "name": "date", 31 | "type": 9 32 | } 33 | ], 34 | "relations": [] 35 | } 36 | ], 37 | "lastEntityId": "1:853953005959405758", 38 | "lastIndexId": "0:0", 39 | "lastRelationId": "0:0", 40 | "lastSequenceId": "0:0", 41 | "modelVersion": 5, 42 | "modelVersionParserMinimum": 5, 43 | "retiredEntityUids": [], 44 | "retiredIndexUids": [], 45 | "retiredPropertyUids": [], 46 | "retiredRelationUids": [], 47 | "version": 1 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/data/PaginationListener.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.data; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.recyclerview.widget.LinearLayoutManager; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | 7 | public abstract class PaginationListener extends RecyclerView.OnScrollListener { 8 | public static final int PAGE_START = 1; 9 | @NonNull 10 | private LinearLayoutManager layoutManager; 11 | /** 12 | * Set scrolling threshold here (for now i'm assuming 10 item in one page) 13 | */ 14 | private static final int PAGE_SIZE = 50; 15 | /** 16 | * Supporting only LinearLayoutManager for now. 17 | */ 18 | public PaginationListener(@NonNull LinearLayoutManager layoutManager) { 19 | this.layoutManager = layoutManager; 20 | } 21 | @Override 22 | public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { 23 | super.onScrolled(recyclerView, dx, dy); 24 | int visibleItemCount = layoutManager.getChildCount(); 25 | int totalItemCount = layoutManager.getItemCount(); 26 | int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition(); 27 | if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount 28 | && firstVisibleItemPosition >= 0 29 | && totalItemCount >= PAGE_SIZE) { 30 | loadMoreItems(); 31 | } 32 | } 33 | protected abstract void loadMoreItems(); 34 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 18 | 19 | 28 | 29 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Android SMS Gateway 3 | 4 | This is recreate from [Old SMS Gateway](https://github.com/anjlab/android-sms-gateway) 5 | now using Firebase 6 | to turn my android as sms sender 7 | 8 | # HOW IT WORKS 9 | 10 | Sending SMS 11 | 12 | 1. You send data to sms.ibnux.net (or your server) 13 | 2. Server will send push notification 14 | 3. App receive push notification, and route it to sms 15 | 4. App receive sent notification, and post it to your server 16 | 5. App receive delivered notification, and post it to your server 17 | 18 | RECEIVE SMS 19 | 1. App receive SMS 20 | 2. App send it to your server 21 | 22 | # HOW TO USE? 23 | 24 | Download APK from [release](https://github.com/ibnux/Android-SMS-Gateway/releases) page 25 | then open https://sms.ibnux.net/ to learn how to send sms 26 | 27 | you can find backend folder for server side in this source 28 | 29 | to compile yourself, you need your own Firebase 30 | 31 | # FEATURES 32 | 33 | - SENDING SMS 34 | - RECEIVE SMS to SERVER 35 | - SENT NOTIFICATION to SERVER 36 | - DELIVERED NOTIFICATION to SERVER 37 | - USSD 38 | - MULTIPLE SIMCARD 39 | - RETRY SMS FAILED TO SENT 3 TIMES 40 | 41 | ## USSD Request 42 | 43 | Not all phone and carrier work with this, this feature need accessibility to read message and auto close USSD dialog, but some device failed to close Dialog, i use samsung S10 Lite and it cannot close dialog 44 | 45 | ## MULTIPLE SIMCARD 46 | 47 | i think not all phone will work too, because of different of API for some OS which vendor has modification 48 | 49 | # Install on your own Server? 50 | 51 | You need to understand how to build android Apps, and compile your own version. 52 | 53 | Create Firebase project, add apps to project to get google-services.json 54 | 55 | Add server key to **backend** script 56 | 57 | You will see MyObjectBox error, just build it once, it will create automatically, read in [here](https://docs.objectbox.io/getting-started#generate-objectbox-code) 58 | 59 | ## MQTT VERSION 60 | 61 | https://github.com/ibnux/Android-SMS-Gateway-MQTT/ 62 | 63 | *** 64 | 65 | ## Traktir @ibnux 66 | 67 | [](https://karyakarsa.com/ibnux) 68 | 69 | [](https://trakteer.id/ibnux) 70 | 71 | ## DONATE @ibnux 72 | 73 | [paypal.me/ibnux](https://paypal.me/ibnux) 74 | 75 | # LICENSE 76 | ## Apache License 2.0 77 | 78 | Permissions 79 | 80 | ✓ Commercial use 81 | ✓ Distribution 82 | ✓ Modification 83 | ✓ Patent use 84 | ✓ Private use 85 | 86 | Conditions 87 | 88 | License and copyright notice 89 | State changes 90 | 91 | Limitations 92 | 93 | No Liability 94 | No Trademark use 95 | No Warranty 96 | 97 | you can find license file inside folder 98 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/layanan/BackgroundService.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.layanan; 2 | 3 | /** 4 | * Created by Ibnu Maksum 2020 5 | */ 6 | 7 | import android.app.Notification; 8 | import android.app.NotificationChannel; 9 | import android.app.NotificationManager; 10 | import android.app.PendingIntent; 11 | import android.app.Service; 12 | import android.content.BroadcastReceiver; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.content.IntentFilter; 16 | import android.os.Build; 17 | import android.os.IBinder; 18 | 19 | import androidx.core.app.NotificationCompat; 20 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 21 | 22 | import com.ibnux.smsgateway.Aplikasi; 23 | import com.ibnux.smsgateway.MainActivity; 24 | import com.ibnux.smsgateway.R; 25 | import com.ibnux.smsgateway.Utils.Fungsi; 26 | 27 | public class BackgroundService extends Service { 28 | 29 | public BackgroundService() { 30 | 31 | } 32 | 33 | @Override 34 | public void onCreate() { 35 | Fungsi.log("BackgroundService onCreate"); 36 | LocalBroadcastManager.getInstance(this).registerReceiver(receiver,new IntentFilter("BackgroundService")); 37 | } 38 | 39 | @Override 40 | public void onDestroy() { 41 | Fungsi.log("BackgroundService onDestroy"); 42 | LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); 43 | } 44 | 45 | @Override 46 | public int onStartCommand(Intent intent, int flags, int startId) { 47 | Fungsi.log("BackgroundService onStartCommand"); 48 | NotificationManager mNotificationManager = (NotificationManager) Aplikasi.app.getSystemService(Context.NOTIFICATION_SERVICE); 49 | 50 | if(Build.VERSION.SDK_INT>25) { 51 | NotificationChannel androidChannel = new NotificationChannel("Push Listener", 52 | "Background", NotificationManager.IMPORTANCE_LOW); 53 | androidChannel.enableLights(false); 54 | androidChannel.enableVibration(false); 55 | androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); 56 | 57 | mNotificationManager.createNotificationChannel(androidChannel); 58 | } 59 | 60 | PendingIntent contentIntent = PendingIntent.getActivity(Aplikasi.app, 0, new Intent(Aplikasi.app, MainActivity.class), 0); 61 | 62 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(Aplikasi.app,"Push Listener") 63 | .setSmallIcon(R.mipmap.ic_launcher) 64 | .setContentTitle(Aplikasi.app.getText(R.string.app_name)) 65 | .setOngoing(true) 66 | .setContentText("Listening for push") 67 | .setAutoCancel(false); 68 | mBuilder.setContentIntent(contentIntent); 69 | mNotificationManager.notify(2, mBuilder.build()); 70 | return Service.START_STICKY; 71 | } 72 | 73 | @Override 74 | public IBinder onBind(Intent intent) { 75 | Fungsi.log("BackgroundService onBind"); 76 | return null; 77 | } 78 | 79 | BroadcastReceiver receiver = new BroadcastReceiver() { 80 | @Override 81 | public void onReceive(Context context, Intent intent) { 82 | Fungsi.log("BackgroundService BroadcastReceiver received"); 83 | if(intent.hasExtra("kill") && intent.getBooleanExtra("kill",false)){ 84 | Fungsi.log("BackgroundService KILL"); 85 | ((NotificationManager) Aplikasi.app.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll(); 86 | intent = new Intent("MainActivity"); 87 | intent.putExtra("kill",true); 88 | LocalBroadcastManager.getInstance(BackgroundService.this).sendBroadcast(intent); 89 | }else { 90 | LocalBroadcastManager.getInstance(BackgroundService.this).sendBroadcast(new Intent("MainActivity")); 91 | } 92 | } 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/data/LogAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.data; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.TextView; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.ibnux.smsgateway.ObjectBox; 12 | import com.ibnux.smsgateway.R; 13 | import com.ibnux.smsgateway.Utils.Fungsi; 14 | 15 | import java.util.List; 16 | 17 | public class LogAdapter extends RecyclerView.Adapter { 18 | private List datas; 19 | long offset = 0, limit = 50; 20 | String search = ""; 21 | long smallTime = System.currentTimeMillis(), bigTime = 0; 22 | 23 | public static class MyViewHolder extends RecyclerView.ViewHolder { 24 | TextView txtDate, txtMsg; 25 | 26 | public MyViewHolder(View v) { 27 | super(v); 28 | txtMsg = v.findViewById(R.id.txtMsg); 29 | txtDate = v.findViewById(R.id.txtDate); 30 | } 31 | } 32 | 33 | public LogAdapter() { 34 | Fungsi.log("Data: " + ObjectBox.get().boxFor(LogLine.class).count()); 35 | } 36 | 37 | public void reload() { 38 | smallTime = System.currentTimeMillis(); 39 | bigTime = 0; 40 | if (search.length() > 0) { 41 | datas = ObjectBox.get().boxFor(LogLine.class).query().contains(LogLine_.message, search).orderDesc(LogLine_.time).build().find(offset, limit); 42 | } else { 43 | datas = ObjectBox.get().boxFor(LogLine.class).query().orderDesc(LogLine_.time).build().find(offset, limit); 44 | } 45 | for (int n = 0; n < getItemCount(); n++) { 46 | if (datas.get(n).time > bigTime) { 47 | bigTime = datas.get(n).time; 48 | } 49 | if (smallTime > datas.get(n).time) { 50 | smallTime = datas.get(n).time; 51 | } 52 | } 53 | Fungsi.log("reload " + datas.size() + " " + bigTime + " " + smallTime); 54 | notifyDataSetChanged(); 55 | } 56 | 57 | public void search(String search) { 58 | this.search = search; 59 | reload(); 60 | } 61 | 62 | public void getNewData() { 63 | List dts; 64 | if (search.length() > 0) { 65 | dts = ObjectBox.get().boxFor(LogLine.class).query().contains(LogLine_.message, search).greater(LogLine_.time, bigTime).order(LogLine_.time).build().find(offset, limit); 66 | } else { 67 | dts = ObjectBox.get().boxFor(LogLine.class).query().greater(LogLine_.time, bigTime).order(LogLine_.time).build().find(offset, limit); 68 | } 69 | for (int n = 0; n < dts.size(); n++) { 70 | datas.add(0, dts.get(n)); 71 | if (dts.get(n).time > bigTime) { 72 | bigTime = dts.get(n).time; 73 | } 74 | } 75 | Fungsi.log("getNewData " + dts.size() + " " + bigTime); 76 | notifyDataSetChanged(); 77 | if (datas.size() > 500) { 78 | reload(); 79 | } 80 | } 81 | 82 | public void nextData() { 83 | List dts; 84 | if (search.length() > 0) { 85 | dts = ObjectBox.get().boxFor(LogLine.class).query().contains(LogLine_.message, search).less(LogLine_.time, smallTime).orderDesc(LogLine_.time).build().find(offset, limit); 86 | } else { 87 | dts = ObjectBox.get().boxFor(LogLine.class).query().less(LogLine_.time, smallTime).orderDesc(LogLine_.time).build().find(offset, limit); 88 | } 89 | for (int n = 0; n < dts.size(); n++) { 90 | datas.add(dts.get(n)); 91 | if (smallTime > dts.get(n).time) { 92 | smallTime = dts.get(n).time; 93 | } 94 | } 95 | Fungsi.log("nextData " + dts.size() + " " + smallTime); 96 | notifyDataSetChanged(); 97 | } 98 | 99 | @NonNull 100 | @Override 101 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 102 | return new LogAdapter.MyViewHolder(LayoutInflater.from(parent.getContext()) 103 | .inflate(R.layout.log_item, parent, false)); 104 | } 105 | 106 | @Override 107 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 108 | LogLine ll = datas.get(position); 109 | holder.txtDate.setText(ll.date); 110 | holder.txtMsg.setText(ll.message); 111 | } 112 | 113 | @Override 114 | public int getItemCount() { 115 | return (datas == null) ? 0 : datas.size(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/layanan/SmsListener.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.layanan; 2 | 3 | import static com.ibnux.smsgateway.layanan.PushService.writeLog; 4 | 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.SharedPreferences; 9 | import android.os.AsyncTask; 10 | import android.provider.Telephony; 11 | import android.telephony.SmsMessage; 12 | import android.util.Log; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.BufferedWriter; 16 | import java.io.InputStreamReader; 17 | import java.io.OutputStream; 18 | import java.io.OutputStreamWriter; 19 | import java.net.HttpURLConnection; 20 | import java.net.URL; 21 | import java.net.URLEncoder; 22 | 23 | import javax.net.ssl.HttpsURLConnection; 24 | 25 | public class SmsListener extends BroadcastReceiver { 26 | 27 | SharedPreferences sp; 28 | @Override 29 | public void onReceive(Context context, Intent intent) { 30 | if(sp==null)sp = context.getSharedPreferences("pref",0); 31 | String url = sp.getString("urlPost",null); 32 | if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) { 33 | for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) { 34 | String messageFrom = smsMessage.getOriginatingAddress(); 35 | String messageBody = smsMessage.getMessageBody(); 36 | String messageTimestamp = smsMessage.getTimestampMillis()+""; 37 | Log.i("SMS From", messageFrom); 38 | Log.i("SMS Body", messageBody); 39 | writeLog("SMS: RECEIVED : " + messageFrom + " " + messageBody,context); 40 | if(url!=null){ 41 | if(sp.getBoolean("gateway_on",true)) { 42 | sendPOST(url, messageFrom, messageBody,"received",context,messageTimestamp); 43 | }else{ 44 | writeLog("GATEWAY OFF: SMS NOT POSTED TO SERVER", context); 45 | } 46 | 47 | }else{ 48 | Log.i("SMS URL", "URL not SET"); 49 | } 50 | } 51 | } 52 | } 53 | 54 | static class postDataTask extends AsyncTask { 55 | 56 | private Exception exception; 57 | 58 | protected String doInBackground(String... datas) { 59 | URL url; 60 | String response = ""; 61 | try { 62 | try { 63 | url = new URL(datas[0]); 64 | 65 | HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 66 | conn.setReadTimeout(15000); 67 | conn.setConnectTimeout(15000); 68 | conn.setRequestMethod("POST"); 69 | conn.setDoInput(true); 70 | conn.setDoOutput(true); 71 | 72 | OutputStream os = conn.getOutputStream(); 73 | BufferedWriter writer = new BufferedWriter( 74 | new OutputStreamWriter(os, "UTF-8")); 75 | writer.write(datas[1]); 76 | 77 | writer.flush(); 78 | writer.close(); 79 | os.close(); 80 | int responseCode=conn.getResponseCode(); 81 | 82 | if (responseCode == HttpsURLConnection.HTTP_OK) { 83 | String line; 84 | BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream())); 85 | while ((line=br.readLine()) != null) { 86 | response+=line; 87 | } 88 | } 89 | else { 90 | response=""; 91 | 92 | } 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | 97 | return "SMS: POST : "+datas[0]+" : "+response; 98 | }catch (Exception e){ 99 | e.printStackTrace(); 100 | return "SMS: POST FAILED : "+datas[0]+" : "+e.getMessage(); 101 | } 102 | } 103 | 104 | @Override 105 | protected void onPostExecute(String response) { 106 | writeLog(response,null); 107 | } 108 | } 109 | 110 | 111 | public static void sendPOST(String urlPost,String from, String msg,String tipe, Context context, String msgTimestamp){ 112 | if(urlPost==null) return; 113 | if(from.isEmpty()) return; 114 | if(!urlPost.startsWith("http")) return; 115 | try { 116 | new postDataTask().execute(urlPost, 117 | "number="+URLEncoder.encode(from, "UTF-8")+ 118 | "&message="+URLEncoder.encode(msg, "UTF-8")+ 119 | "&type=" + URLEncoder.encode(tipe, "UTF-8") + 120 | "×tamp=" + URLEncoder.encode(msgTimestamp, "UTF-8") 121 | ); 122 | }catch (Exception e){ 123 | e.printStackTrace(); 124 | writeLog("SMS: POST FAILED : "+urlPost+" : "+e.getMessage(),context); 125 | } 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /backend/index.php: -------------------------------------------------------------------------------- 1 | 1) 39 | file_put_contents("log.txt", json_encode($_REQUEST) . "\n\n", FILE_APPEND); 40 | 41 | if (empty($to) || empty($text) || empty($secret) || empty($token)) { 42 | readfile("info.txt"); 43 | die(); 44 | } 45 | 46 | $result = sendPush($token, $secret, $time, $to, $text, $sim); 47 | 48 | if (isset($_GET['debug']) && count($_REQUEST) > 1) 49 | file_put_contents("log.txt", $result . "\n\n", FILE_APPEND); 50 | echo $result; 51 | 52 | function sendPush($token, $secret, $time, $to, $message, $sim = 0) 53 | { 54 | global $firebaseProject; 55 | $url = "https://fcm.googleapis.com/v1/projects/$firebaseProject/messages:send"; 56 | 57 | $fields = array( 58 | "message" => array( 59 | "token" => $token, 60 | 'data' => array( 61 | "to" => $to, 62 | "time" => $time, 63 | "secret" => $secret, 64 | "message" => $message, 65 | "sim" => $sim, 66 | ) 67 | ) 68 | ); 69 | 70 | $headers = array( 71 | 'Authorization: Bearer ' . getToken(), 72 | 'Content-Type: application/json' 73 | ); 74 | 75 | $ch = curl_init(); 76 | curl_setopt($ch, CURLOPT_URL, $url); 77 | curl_setopt($ch, CURLOPT_POST, true); 78 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 79 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 80 | curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields)); 81 | 82 | $result = curl_exec($ch); 83 | 84 | curl_close($ch); 85 | 86 | return $result; 87 | } 88 | 89 | function base64UrlEncode($text) 90 | { 91 | return str_replace( 92 | ['+', '/', '='], 93 | ['-', '_', ''], 94 | base64_encode($text) 95 | ); 96 | } 97 | 98 | function getToken() 99 | { 100 | global $firebaseAuthFile; 101 | $md5 = md5($firebaseAuthFile); 102 | // if exists and not expired 103 | if (file_exists("$md5.token") && time() - filemtime("$md5.token") < 3500) { 104 | return file_get_contents("$md5.token"); 105 | } 106 | $authConfigString = file_get_contents($firebaseAuthFile); 107 | $authConfig = json_decode($authConfigString); 108 | // Read private key from service account details 109 | $secret = openssl_get_privatekey($authConfig->private_key); 110 | 111 | // Create the token header 112 | $header = json_encode([ 113 | 'typ' => 'JWT', 114 | 'alg' => 'RS256' 115 | ]); 116 | // Get seconds since 1 January 1970 117 | $time = time(); 118 | // Allow 1 minute time deviation between client en server (not sure if this is necessary) 119 | $start = $time - 60; 120 | $end = $start + 3600; 121 | // Create payload 122 | $payload = json_encode([ 123 | "iss" => $authConfig->client_email, 124 | "scope" => "https://www.googleapis.com/auth/firebase.messaging", 125 | "aud" => "https://oauth2.googleapis.com/token", 126 | "exp" => $end, 127 | "iat" => $start 128 | ]); 129 | // Encode Header 130 | $base64UrlHeader = base64UrlEncode($header); 131 | // Encode Payload 132 | $base64UrlPayload = base64UrlEncode($payload); 133 | // Create Signature Hash 134 | openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $secret, OPENSSL_ALGO_SHA256); 135 | // Encode Signature to Base64Url String 136 | $base64UrlSignature = base64UrlEncode($signature); 137 | // Create JWT 138 | $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; 139 | 140 | //-----Request token, with an http post request------ 141 | $options = array('http' => array( 142 | 'method' => 'POST', 143 | 'content' => 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=' . $jwt, 144 | 'header' => "Content-Type: application/x-www-form-urlencoded" 145 | )); 146 | $context = stream_context_create($options); 147 | $responseText = file_get_contents("https://oauth2.googleapis.com/token", false, $context); 148 | $response = json_decode($responseText, true); 149 | file_put_contents("$md5.token", $response['access_token']); 150 | return $response['access_token']; 151 | } 152 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/layanan/UssdService.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.layanan; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.accessibilityservice.AccessibilityServiceInfo; 5 | import android.content.Intent; 6 | import android.text.TextUtils; 7 | import android.view.accessibility.AccessibilityEvent; 8 | import android.view.accessibility.AccessibilityNodeInfo; 9 | import android.widget.Toast; 10 | 11 | import com.ibnux.smsgateway.Utils.Fungsi; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | public class UssdService extends AccessibilityService { 17 | 18 | public static String TAG = "USSD"; 19 | 20 | @Override 21 | public void onCreate() { 22 | Fungsi.log(TAG,"UssdService onCreate"); 23 | } 24 | 25 | @Override 26 | public void onDestroy() { 27 | Fungsi.log(TAG,"UssdService onDestroy"); 28 | super.onDestroy(); 29 | } 30 | 31 | @Override 32 | public int onStartCommand(Intent intent, int flags, int startId) { 33 | Fungsi.log(TAG,"UssdService onStartCommand"); 34 | return super.onStartCommand(intent, flags, startId); 35 | } 36 | 37 | @Override 38 | public void onAccessibilityEvent(AccessibilityEvent event) { 39 | Fungsi.log(TAG, "onAccessibilityEvent"); 40 | 41 | if(!getSharedPreferences("pref",0).getBoolean("gateway_on",true)){ 42 | Fungsi.log(TAG, "gateway_off"); 43 | return; 44 | } 45 | 46 | AccessibilityNodeInfo source = event.getSource(); 47 | /* if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !event.getClassName().equals("android.app.AlertDialog")) { // android.app.AlertDialog is the standard but not for all phones */ 48 | if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !String.valueOf(event.getClassName()).contains("AlertDialog")) { 49 | Fungsi.log(TAG, "TYPE_WINDOW_STATE_CHANGED"); 50 | return; 51 | } 52 | if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && (source == null || !source.getClassName().equals("android.widget.TextView"))) { 53 | Fungsi.log(TAG, "TYPE_WINDOW_CONTENT_CHANGED"); 54 | return; 55 | } 56 | if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && TextUtils.isEmpty(source.getText())) { 57 | Fungsi.log(TAG, "TYPE_WINDOW_CONTENT_CHANGED"); 58 | return; 59 | } 60 | 61 | List eventText; 62 | 63 | if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 64 | eventText = event.getText(); 65 | } else { 66 | eventText = Collections.singletonList(source.getText()); 67 | } 68 | 69 | String text = processUSSDText(eventText); 70 | 71 | if( TextUtils.isEmpty(text) ) return; 72 | 73 | // Close dialog 74 | performGlobalAction(GLOBAL_ACTION_BACK); // This works on 4.1+ only 75 | Fungsi.log(TAG, text); 76 | if(PushService.current!=null){ 77 | PushService.writeLog("USSD Received: " + text, this); 78 | SmsListener.sendPOST(getSharedPreferences("pref", 0).getString("urlPost", null), 79 | PushService.current.to+PushService.current.sim, text, "ussd", this, String.valueOf(System.currentTimeMillis())); 80 | }else { 81 | PushService.writeLog("USSD Received: " + text, this); 82 | SmsListener.sendPOST(getSharedPreferences("pref", 0).getString("urlPost", null), 83 | "ussd", text, "ussd", this, String.valueOf(System.currentTimeMillis())); 84 | } 85 | PushService.runUssd(); 86 | } 87 | 88 | private String processUSSDText(List eventText) { 89 | for (CharSequence s : eventText) { 90 | String text = String.valueOf(s); 91 | // Return text if text is the expected ussd response 92 | if( true ) { 93 | return text; 94 | } 95 | } 96 | return null; 97 | } 98 | 99 | @Override 100 | public void onInterrupt() { 101 | } 102 | 103 | @Override 104 | protected void onServiceConnected() { 105 | super.onServiceConnected(); 106 | Fungsi.log(TAG, "onServiceConnected"); 107 | AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 108 | info.flags = AccessibilityServiceInfo.DEFAULT; 109 | info.packageNames = new String[]{"com.android.phone"}; 110 | info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED; 111 | info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; 112 | setServiceInfo(info); 113 | Toast.makeText(this,"Ready to listen USSD",Toast.LENGTH_SHORT).show(); 114 | } 115 | 116 | // @Override 117 | // public void onAccessibilityEvent(AccessibilityEvent event) { 118 | // Fungsi.log(TAG, "onAccessibilityEvent"); 119 | // String text = event.getText().toString(); 120 | // 121 | // if (event.getClassName().equals("android.app.AlertDialog")||event.getClassName().equals("com.android.phone.UssdAlertActivity")) { 122 | // performGlobalAction(GLOBAL_ACTION_BACK); 123 | // Fungsi.log(TAG, text); 124 | // PushService.writeLog("USSD: "+text,this); 125 | // SmsListener.sendPOST(getSharedPreferences("pref",0).getString("urlPost",null), 126 | // "ussd", text,"ussd",this); 127 | // Intent i = new Intent("MainActivity"); 128 | // i.putExtra("newMessage","newMessage"); 129 | // LocalBroadcastManager.getInstance(Aplikasi.app).sendBroadcast(i); 130 | // } 131 | // 132 | // } 133 | // 134 | // @Override 135 | // public void onInterrupt() { 136 | // } 137 | // 138 | // @Override 139 | // protected void onServiceConnected() { 140 | // super.onServiceConnected(); 141 | // Fungsi.log(TAG, "onServiceConnected"); 142 | // AccessibilityServiceInfo info = new AccessibilityServiceInfo(); 143 | // info.flags = AccessibilityServiceInfo.DEFAULT; 144 | // info.packageNames = new String[]{"com.android.phone"}; 145 | // info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; 146 | // info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC; 147 | // setServiceInfo(info); 148 | // } 149 | 150 | } -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/Utils/TelephonyInfo.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.Utils; 2 | 3 | import android.content.Context; 4 | import android.telephony.TelephonyManager; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | public final class TelephonyInfo { 9 | 10 | private static TelephonyInfo telephonyInfo; 11 | private String imeiSIM1; 12 | private String imeiSIM2; 13 | private boolean isSIM1Ready; 14 | private boolean isSIM2Ready; 15 | 16 | public String getImeiSIM1() { 17 | return imeiSIM1; 18 | } 19 | 20 | /*public static void setImeiSIM1(String imeiSIM1) { 21 | TelephonyInfo.imeiSIM1 = imeiSIM1; 22 | }*/ 23 | 24 | public String getImeiSIM2() { 25 | return imeiSIM2; 26 | } 27 | 28 | /*public static void setImeiSIM2(String imeiSIM2) { 29 | TelephonyInfo.imeiSIM2 = imeiSIM2; 30 | }*/ 31 | 32 | public boolean isSIM1Ready() { 33 | return isSIM1Ready; 34 | } 35 | 36 | /*public static void setSIM1Ready(boolean isSIM1Ready) { 37 | TelephonyInfo.isSIM1Ready = isSIM1Ready; 38 | }*/ 39 | 40 | public boolean isSIM2Ready() { 41 | return isSIM2Ready; 42 | } 43 | 44 | /*public static void setSIM2Ready(boolean isSIM2Ready) { 45 | TelephonyInfo.isSIM2Ready = isSIM2Ready; 46 | }*/ 47 | 48 | public boolean isDualSIM() { 49 | return imeiSIM2 != null; 50 | } 51 | 52 | private TelephonyInfo() { 53 | } 54 | 55 | public static TelephonyInfo getInstance(Context context){ 56 | 57 | if(telephonyInfo == null) { 58 | 59 | telephonyInfo = new TelephonyInfo(); 60 | 61 | TelephonyManager telephonyManager = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)); 62 | 63 | // telephonyInfo.imeiSIM1 = telephonyManager.getDeviceId();; 64 | // telephonyInfo.imeiSIM2 = null; 65 | // 66 | // try { 67 | // telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdGemini", 0); 68 | // telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdGemini", 1); 69 | // } catch (GeminiMethodNotFoundException e) { 70 | // e.printStackTrace(); 71 | // 72 | // try { 73 | // telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceId", 0); 74 | // telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceId", 1); 75 | // } catch (GeminiMethodNotFoundException e1) { 76 | // //Call here for next manufacturer's predicted method name if you wish 77 | // e1.printStackTrace(); 78 | // } 79 | // } 80 | 81 | telephonyInfo.isSIM1Ready = telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; 82 | telephonyInfo.isSIM2Ready = false; 83 | 84 | try { 85 | telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimStateGemini", 0); 86 | telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimStateGemini", 1); 87 | } catch (GeminiMethodNotFoundException e) { 88 | 89 | e.printStackTrace(); 90 | 91 | try { 92 | telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimState", 0); 93 | telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimState", 1); 94 | } catch (GeminiMethodNotFoundException e1) { 95 | //Call here for next manufacturer's predicted method name if you wish 96 | e1.printStackTrace(); 97 | } 98 | } 99 | } 100 | 101 | return telephonyInfo; 102 | } 103 | 104 | private static String getDeviceIdBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException { 105 | 106 | String imei = null; 107 | 108 | TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 109 | 110 | try{ 111 | 112 | Class telephonyClass = Class.forName(telephony.getClass().getName()); 113 | 114 | Class[] parameter = new Class[1]; 115 | parameter[0] = int.class; 116 | Method getSimID = telephonyClass.getMethod(predictedMethodName, parameter); 117 | 118 | Object[] obParameter = new Object[1]; 119 | obParameter[0] = slotID; 120 | Object ob_phone = getSimID.invoke(telephony, obParameter); 121 | 122 | if(ob_phone != null){ 123 | imei = ob_phone.toString(); 124 | 125 | } 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | throw new GeminiMethodNotFoundException(predictedMethodName); 129 | } 130 | 131 | return imei; 132 | } 133 | 134 | private static boolean getSIMStateBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException { 135 | 136 | boolean isReady = false; 137 | 138 | TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 139 | 140 | try{ 141 | 142 | Class telephonyClass = Class.forName(telephony.getClass().getName()); 143 | 144 | Class[] parameter = new Class[1]; 145 | parameter[0] = int.class; 146 | Method getSimStateGemini = telephonyClass.getMethod(predictedMethodName, parameter); 147 | 148 | Object[] obParameter = new Object[1]; 149 | obParameter[0] = slotID; 150 | Object ob_phone = getSimStateGemini.invoke(telephony, obParameter); 151 | 152 | if(ob_phone != null){ 153 | int simState = Integer.parseInt(ob_phone.toString()); 154 | if(simState == TelephonyManager.SIM_STATE_READY){ 155 | isReady = true; 156 | } 157 | } 158 | } catch (Exception e) { 159 | e.printStackTrace(); 160 | throw new GeminiMethodNotFoundException(predictedMethodName); 161 | } 162 | 163 | return isReady; 164 | } 165 | 166 | 167 | private static class GeminiMethodNotFoundException extends Exception { 168 | 169 | private static final long serialVersionUID = -996812356902545308L; 170 | 171 | public GeminiMethodNotFoundException(String info) { 172 | super(info); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/Utils/Fungsi.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.Utils; 2 | 3 | /** 4 | * Created by Ibnu Maksum 2020 5 | */ 6 | 7 | import static android.content.ContentValues.TAG; 8 | 9 | import android.app.Notification; 10 | import android.app.NotificationChannel; 11 | import android.app.NotificationManager; 12 | import android.app.PendingIntent; 13 | import android.content.ContentValues; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.database.Cursor; 17 | import android.net.Uri; 18 | import android.os.Build; 19 | import android.provider.Settings; 20 | import android.telephony.SmsManager; 21 | import android.text.TextUtils; 22 | import android.util.Log; 23 | 24 | import androidx.core.app.NotificationCompat; 25 | 26 | import com.ibnux.smsgateway.Aplikasi; 27 | import com.ibnux.smsgateway.MainActivity; 28 | import com.ibnux.smsgateway.R; 29 | import com.ibnux.smsgateway.layanan.PushService; 30 | import com.ibnux.smsgateway.layanan.UssdService; 31 | 32 | import java.security.MessageDigest; 33 | import java.security.NoSuchAlgorithmException; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | public class Fungsi { 38 | private static NotificationManager mNotificationManager; 39 | public static String SENT = "SMS_SENT"; 40 | public static String DELIVERED = "SMS_DELIVERED"; 41 | 42 | 43 | public static void sendSMS(final String number, String message, final Context cx){ 44 | 45 | if (!TextUtils.isEmpty(number) && !TextUtils.isEmpty(message)) 46 | { 47 | int time = (int) System.currentTimeMillis()/1000; 48 | Intent is = new Intent(SENT); 49 | is.putExtra("number",number); 50 | PendingIntent sentPI = PendingIntent.getBroadcast(cx, time, 51 | is, 0); 52 | Intent id = new Intent(DELIVERED); 53 | id.putExtra("number",number); 54 | PendingIntent deliveredPI = PendingIntent.getBroadcast(cx, time, 55 | id, 0); 56 | 57 | try 58 | { 59 | SmsManager smsManager = SmsManager.getDefault(); 60 | ArrayList parts = smsManager.divideMessage(message); 61 | if (parts.size() > 1) 62 | { 63 | try { 64 | ArrayList spi = new ArrayList<>(); 65 | ArrayList dpi = new ArrayList<>(); 66 | for (int n = 0; n < parts.size(); n++) { 67 | spi.add(sentPI); 68 | dpi.add(deliveredPI); 69 | } 70 | smsManager.sendMultipartTextMessage(number, null, parts, spi, dpi); 71 | }catch (Exception e){ 72 | smsManager.sendTextMessage(number, null, message, sentPI, deliveredPI); 73 | } 74 | } 75 | else 76 | { 77 | smsManager.sendTextMessage(number, null, message, sentPI, deliveredPI); 78 | } 79 | 80 | String result = number + ": " + message; 81 | Log.i(TAG, result); 82 | 83 | sendNotification(number, message); 84 | 85 | ContentValues values = new ContentValues(); 86 | values.put("address", number); 87 | values.put("body", message); 88 | Aplikasi.app.getContentResolver() 89 | .insert(Uri.parse("content://sms/sent"), values); 90 | PushService.writeLog("SUBMIT SMS SUCCESS: " + number, cx); 91 | } 92 | catch (Exception ex) 93 | { 94 | ex.printStackTrace(); 95 | PushService.writeLog("SEND FAILED: " + number + " " + message+"\n\n"+ex.getMessage(), cx); 96 | } 97 | } 98 | } 99 | 100 | public static void sendNotification(String to, String msg) { 101 | if(mNotificationManager==null) 102 | mNotificationManager = (NotificationManager) Aplikasi.app.getSystemService(Context.NOTIFICATION_SERVICE); 103 | if(Build.VERSION.SDK_INT>25) { 104 | 105 | NotificationChannel androidChannel = new NotificationChannel("com.ibnux.smsgateway", 106 | "SMS Notifikasi", NotificationManager.IMPORTANCE_LOW); 107 | androidChannel.enableLights(false); 108 | androidChannel.enableVibration(false); 109 | androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); 110 | 111 | mNotificationManager.createNotificationChannel(androidChannel); 112 | } 113 | 114 | PendingIntent contentIntent = PendingIntent.getActivity(Aplikasi.app, 0, new Intent(Aplikasi.app, MainActivity.class), 0); 115 | 116 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(Aplikasi.app,"com.ibnux.smsgateway") 117 | .setSmallIcon(R.mipmap.ic_launcher) 118 | .setContentTitle(Aplikasi.app.getText(R.string.app_name)) 119 | .setStyle(new NotificationCompat.BigTextStyle() 120 | .bigText(msg)) 121 | .setContentText("sent to "+to).setAutoCancel(true); 122 | mBuilder.setContentIntent(contentIntent); 123 | mNotificationManager.notify(1, mBuilder.build()); 124 | } 125 | 126 | public static void log(String txt){ 127 | Log.d("SMSin","-------------------------------"); 128 | Log.d("SMSin",txt+""); 129 | Log.d("SMSin","-------------------------------"); 130 | } 131 | 132 | public static void log(String tag, String txt){ 133 | Log.d(tag,"-------------------------------"); 134 | Log.d(tag,txt+""); 135 | Log.d(tag,"-------------------------------"); 136 | } 137 | 138 | public static String md5(final String s) { 139 | final String MD5 = "MD5"; 140 | try { 141 | // Create MD5 Hash 142 | MessageDigest digest = java.security.MessageDigest 143 | .getInstance(MD5); 144 | digest.update(s.getBytes()); 145 | byte messageDigest[] = digest.digest(); 146 | 147 | // Create Hex String 148 | StringBuilder hexString = new StringBuilder(); 149 | for (byte aMessageDigest : messageDigest) { 150 | String h = Integer.toHexString(0xFF & aMessageDigest); 151 | while (h.length() < 2) 152 | h = "0" + h; 153 | hexString.append(h); 154 | } 155 | return hexString.toString(); 156 | 157 | } catch (NoSuchAlgorithmException e) { 158 | e.printStackTrace(); 159 | } 160 | return ""; 161 | } 162 | 163 | public static boolean isAccessibilitySettingsOn(Context mContext) { 164 | int accessibilityEnabled = 0; 165 | final String service = mContext.getPackageName() + "/" + UssdService.class.getCanonicalName(); 166 | log("USSD",service); 167 | try { 168 | accessibilityEnabled = Settings.Secure.getInt( 169 | mContext.getApplicationContext().getContentResolver(), 170 | android.provider.Settings.Secure.ACCESSIBILITY_ENABLED); 171 | Log.v("USSD", "accessibilityEnabled = " + accessibilityEnabled); 172 | } catch (Settings.SettingNotFoundException e) { 173 | Log.e("USSD", "Error finding setting, default accessibility to not found: " 174 | + e.getMessage()); 175 | } 176 | TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':'); 177 | 178 | if (accessibilityEnabled == 1) { 179 | Log.v("USSD", "***ACCESSIBILITY IS ENABLED*** -----------------"); 180 | String settingValue = Settings.Secure.getString( 181 | mContext.getApplicationContext().getContentResolver(), 182 | Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 183 | if (settingValue != null) { 184 | mStringColonSplitter.setString(settingValue); 185 | while (mStringColonSplitter.hasNext()) { 186 | String accessibilityService = mStringColonSplitter.next(); 187 | 188 | Log.v("USSD", "-------------- > accessibilityService :: " + accessibilityService + " " + service); 189 | if (accessibilityService.equalsIgnoreCase(service)) { 190 | Log.v("USSD", "We've found the correct setting - accessibility is switched on!"); 191 | return true; 192 | } 193 | } 194 | } 195 | } else { 196 | Log.v("USSD", "***ACCESSIBILITY IS DISABLED***"); 197 | } 198 | 199 | return false; 200 | } 201 | 202 | public static Uri ussdToCallableUri(String ussd) { 203 | 204 | String uriString = ""; 205 | 206 | if(!ussd.startsWith("tel:")) 207 | uriString += "tel:"; 208 | 209 | for(char c : ussd.toCharArray()) { 210 | 211 | if(c == '#') 212 | uriString += Uri.encode("#"); 213 | else 214 | uriString += c; 215 | } 216 | 217 | return Uri.parse(uriString); 218 | } 219 | 220 | public static List getSIMInfo(Context context) { 221 | List simInfoList = new ArrayList<>(); 222 | Uri URI_TELEPHONY = Uri.parse("content://telephony/siminfo/"); 223 | Cursor c = context.getContentResolver().query(URI_TELEPHONY, null, null, null, null); 224 | if (c.moveToFirst()) { 225 | do { 226 | int id = c.getInt(c.getColumnIndex("_id")); 227 | int slot = c.getInt(c.getColumnIndex("slot")); 228 | String display_name = c.getString(c.getColumnIndex("display_name")); 229 | String icc_id = c.getString(c.getColumnIndex("icc_id")); 230 | SimInfo simInfo = new SimInfo(id, display_name, icc_id, slot); 231 | Log.d("apipas_sim_info", simInfo.toString()); 232 | simInfoList.add(simInfo); 233 | } while (c.moveToNext()); 234 | } 235 | c.close(); 236 | 237 | return simInfoList; 238 | } 239 | 240 | 241 | } 242 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/Utils/SimUtil.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway.Utils; 2 | 3 | import android.app.PendingIntent; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | import android.os.IBinder; 8 | import android.telephony.SmsManager; 9 | import android.telephony.SubscriptionInfo; 10 | import android.telephony.SubscriptionManager; 11 | 12 | import com.ibnux.smsgateway.layanan.PushService; 13 | 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.lang.reflect.Method; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | public class SimUtil { 20 | 21 | public static boolean sendSMS(Context ctx, int simID, String toNum, String centerNum, String smsText, int retry) { 22 | String name; 23 | 24 | try { 25 | if (simID == 0) { 26 | name = "isms"; 27 | // for model : "Philips T939" name = "isms0" 28 | } else if (simID == 1) { 29 | name = "isms2"; 30 | } else { 31 | PushService.writeLog("can not get service which for sim '" + simID + "', only 0,1 accepted as values", ctx); 32 | return false; 33 | } 34 | Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class); 35 | method.setAccessible(true); 36 | Object param = method.invoke(null, name); 37 | 38 | int time = (int) System.currentTimeMillis()/1000; 39 | 40 | Intent is = new Intent(Fungsi.SENT); 41 | is.putExtra("number",toNum); 42 | is.putExtra("centerNum",centerNum); 43 | is.putExtra("simID",simID); 44 | is.putExtra("smsText",smsText); 45 | is.putExtra("retry",retry); 46 | PendingIntent sentPI = PendingIntent.getBroadcast(ctx, time, 47 | is, 0); 48 | Intent id = new Intent(Fungsi.DELIVERED); 49 | is.putExtra("number",toNum); 50 | is.putExtra("centerNum",centerNum); 51 | is.putExtra("simID",simID); 52 | is.putExtra("smsText",smsText); 53 | is.putExtra("retry",retry); 54 | PendingIntent deliveredPI = PendingIntent.getBroadcast(ctx, time, 55 | id, 0); 56 | 57 | method = Class.forName("com.android.internal.telephony.ISms$Stub").getDeclaredMethod("asInterface", IBinder.class); 58 | method.setAccessible(true); 59 | Object stubObj = method.invoke(null, param); 60 | try { 61 | if (stubObj != null) { 62 | if (Build.VERSION.SDK_INT < 18) { 63 | method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, PendingIntent.class, PendingIntent.class); 64 | method.invoke(stubObj, toNum, centerNum, smsText, sentPI, deliveredPI); 65 | } else { 66 | method = stubObj.getClass().getMethod("sendText", String.class, String.class, String.class, String.class, PendingIntent.class, PendingIntent.class); 67 | method.invoke(stubObj, ctx.getPackageName(), toNum, centerNum, smsText, sentPI, deliveredPI); 68 | } 69 | } else { 70 | SubscriptionManager localSubscriptionManager = SubscriptionManager.from(ctx); 71 | if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) { 72 | List localList = localSubscriptionManager.getActiveSubscriptionInfoList(); 73 | SubscriptionInfo simInfo = (SubscriptionInfo) localList.get(simID); 74 | SmsManager 75 | .getSmsManagerForSubscriptionId(simInfo.getSubscriptionId()) 76 | .sendTextMessage(toNum, null, smsText, sentPI, deliveredPI); 77 | } 78 | } 79 | }catch (Exception e){ 80 | e.printStackTrace(); 81 | SubscriptionManager localSubscriptionManager = SubscriptionManager.from(ctx); 82 | if (localSubscriptionManager.getActiveSubscriptionInfoCount() > 1) { 83 | List localList = localSubscriptionManager.getActiveSubscriptionInfoList(); 84 | SubscriptionInfo simInfo = (SubscriptionInfo) localList.get(simID); 85 | SmsManager 86 | .getSmsManagerForSubscriptionId(simInfo.getSubscriptionId()) 87 | .sendTextMessage(toNum, null, smsText, sentPI, deliveredPI); 88 | } 89 | } 90 | 91 | PushService.writeLog("SUBMIT SMS SUCCESS: " + toNum + " SIM" + (simID+1), ctx); 92 | 93 | return true; 94 | } catch (ClassNotFoundException e) { 95 | PushService.writeLog("ClassNotFoundException:" + e.getMessage(), ctx); 96 | e.printStackTrace(); 97 | } catch (NoSuchMethodException e) { 98 | PushService.writeLog("NoSuchMethodException:" + e.getMessage(), ctx); 99 | e.printStackTrace(); 100 | } catch (InvocationTargetException e) { 101 | PushService.writeLog("InvocationTargetException:" + e.getMessage(), ctx); 102 | e.printStackTrace(); 103 | } catch (IllegalAccessException e) { 104 | PushService.writeLog("IllegalAccessException:" + e.getMessage(), ctx); 105 | e.printStackTrace(); 106 | } catch (Exception e) { 107 | PushService.writeLog("Exception:" + e.getMessage(), ctx); 108 | e.printStackTrace(); 109 | } 110 | return false; 111 | } 112 | 113 | 114 | public static boolean sendMultipartTextSMS(Context ctx, int simID, String toNum, String centerNum, ArrayList smsTextlist) { 115 | String name; 116 | try { 117 | if (simID == 0) { 118 | name = "isms"; 119 | // for model : "Philips T939" name = "isms0" 120 | } else if (simID == 1) { 121 | name = "isms2"; 122 | } else { 123 | PushService.writeLog("can not get service which for sim '" + simID + "', only 0,1 accepted as values", ctx); 124 | return false; 125 | } 126 | Method method = Class.forName("android.os.ServiceManager").getDeclaredMethod("getService", String.class); 127 | method.setAccessible(true); 128 | Object param = method.invoke(null, name); 129 | 130 | ArrayList sentIntentList = new ArrayList<>(); 131 | ArrayList deliveryIntentList = new ArrayList<>(); 132 | String sms = ""; 133 | for(int n=0;n 1) { 171 | List localList = localSubscriptionManager.getActiveSubscriptionInfoList(); 172 | SubscriptionInfo simInfo = (SubscriptionInfo) localList.get(simID); 173 | for(int n=0;n 1) { 187 | List localList = localSubscriptionManager.getActiveSubscriptionInfoList(); 188 | SubscriptionInfo simInfo = (SubscriptionInfo) localList.get(simID); 189 | for(int n=0;n logBox; 44 | private final static String simSlotName[] = { 45 | "extra_asus_dial_use_dualsim", 46 | "com.android.phone.extra.slot", 47 | "slot", 48 | "simslot", 49 | "sim_slot", 50 | "subscription", 51 | "Subscription", 52 | "phone", 53 | "com.android.phone.DialingMode", 54 | "simSlot", 55 | "slot_id", 56 | "simId", 57 | "simnum", 58 | "phone_type", 59 | "slotId", 60 | "slotIdx" 61 | }; 62 | 63 | public static Context context; 64 | 65 | public static List ussdDataList = new ArrayList<>(); 66 | public static boolean isRun = false; 67 | public static UssdData current; 68 | public static long lastUssd = 0L; 69 | 70 | BroadcastReceiver deliveredReceiver = new BroadcastReceiver() { 71 | @Override 72 | public void onReceive(Context arg0, Intent arg1) { 73 | String msg = null; 74 | switch (getResultCode()) { 75 | case Activity.RESULT_OK: 76 | msg = "success"; 77 | break; 78 | case Activity.RESULT_CANCELED: 79 | msg = "failed"; 80 | break; 81 | } 82 | if (msg != null) { 83 | writeLog("DELIVERED: " + msg + " : " + arg1.getStringExtra("number"), arg0); 84 | SmsListener.sendPOST( 85 | getSharedPreferences("pref", 0).getString("urlPost", null), 86 | arg1.getStringExtra("number"), 87 | msg, 88 | "delivered", 89 | arg0, 90 | String.valueOf(System.currentTimeMillis())); 91 | } 92 | } 93 | }; 94 | 95 | BroadcastReceiver sentReceiver = new BroadcastReceiver() { 96 | @Override 97 | public void onReceive(Context arg0, Intent arg1) { 98 | String msg = null; 99 | switch (getResultCode()) { 100 | case Activity.RESULT_OK: 101 | msg = "success"; 102 | break; 103 | case SmsManager.RESULT_ERROR_GENERIC_FAILURE: 104 | msg = "Generic failure"; 105 | break; 106 | case SmsManager.RESULT_ERROR_NO_SERVICE: 107 | msg = "No service"; 108 | break; 109 | case SmsManager.RESULT_ERROR_NULL_PDU: 110 | msg = "Null PDU"; 111 | break; 112 | case SmsManager.RESULT_ERROR_RADIO_OFF: 113 | msg = "Radio off"; 114 | break; 115 | } 116 | 117 | // RETRY AFTER 10 SECOND IF FAILED UNTIL 3 TIMES 118 | if(msg!=null && !msg.equals("success")){ 119 | int retry = arg1.getIntExtra("retry",0); 120 | if(retry<3){ 121 | PushService.writeLog("SENT FAILED: " + msg, context); 122 | PushService.writeLog("RETRY SEND SMS in 10s #" + (retry+1), context); 123 | new Handler().postDelayed(new Runnable() { 124 | @Override 125 | public void run() { 126 | String number = arg1.getStringExtra("number"); 127 | int simID = arg1.getIntExtra("simID",0); 128 | String centerNum = arg1.getStringExtra("centerNum"); 129 | String smsText = arg1.getStringExtra("smsText"); 130 | int retry = arg1.getIntExtra("retry",0); 131 | retry++; 132 | SimUtil.sendSMS(context,simID,number,centerNum,smsText,retry); 133 | } 134 | }, 10000); 135 | 136 | return; 137 | } 138 | } 139 | 140 | if (msg != null) { 141 | writeLog("SENT: " + msg + " : " + arg1.getStringExtra("number"), arg0); 142 | SmsListener.sendPOST(getSharedPreferences("pref", 0).getString("urlPost", null), 143 | arg1.getStringExtra("number"), msg, "sent", arg0, String.valueOf(System.currentTimeMillis())); 144 | } 145 | } 146 | }; 147 | 148 | @Override 149 | public void onCreate() { 150 | context = this; 151 | registerReceiver(sentReceiver, new IntentFilter(Fungsi.SENT)); 152 | registerReceiver(deliveredReceiver, new IntentFilter(Fungsi.DELIVERED)); 153 | } 154 | 155 | @Override 156 | public void onDestroy() { 157 | unregisterReceiver(sentReceiver); 158 | unregisterReceiver(deliveredReceiver); 159 | super.onDestroy(); 160 | } 161 | 162 | @Override 163 | public void onMessageReceived(RemoteMessage remoteMessage) { 164 | Fungsi.log(TAG, "From: " + remoteMessage.getFrom()); 165 | 166 | // Check if message contains a data payload. 167 | if (remoteMessage.getData() != null && remoteMessage.getData().size() > 0) { 168 | String messageId = remoteMessage.getMessageId(); 169 | String to = remoteMessage.getData().get("to"); 170 | String sim = "0"; 171 | if (remoteMessage.getData().containsKey("sim")) { 172 | sim = remoteMessage.getData().get("sim"); 173 | if(sim.isEmpty()){ 174 | sim = "0"; 175 | } 176 | } 177 | String message = remoteMessage.getData().get("message"); 178 | String secret = remoteMessage.getData().get("secret"); 179 | String time = "0"; 180 | if (remoteMessage.getData().containsKey("time")) { 181 | time = remoteMessage.getData().get("time"); 182 | } 183 | SharedPreferences sp = getSharedPreferences("pref", 0); 184 | String scrt = sp.getString("secret", ""); 185 | 186 | 187 | Fungsi.log("Local Secret " + scrt + "\n" + 188 | "received Secret " + secret + "\n" + 189 | "Time " + time + "\n" + 190 | "To " + to + "\n" + 191 | "SIM " + sim + "\n" + 192 | "messageId " + messageId + "\n" + 193 | "Message " + message); 194 | 195 | if (!TextUtils.isEmpty(to) && !TextUtils.isEmpty(message) && !TextUtils.isEmpty(secret)) { 196 | 197 | //cek dulu secret vs secret, jika oke, berarti tidak diHash, no expired 198 | if (scrt.equals(secret)) { 199 | if (sp.getBoolean("gateway_on", true)) { 200 | sendSMSorUSSD(to, message, Integer.parseInt(sim),messageId); 201 | } else { 202 | writeLog("GATEWAY OFF: " + to + " " + message, this); 203 | } 204 | } else { 205 | int expired = sp.getInt("expired", 3600); 206 | if (TextUtils.isEmpty(time)) time = "0"; 207 | long current = System.currentTimeMillis() / 1000L; 208 | long senttime = Long.parseLong(time); 209 | long timeout = current - senttime; 210 | Fungsi.log(current + " - " + senttime + " : " + expired + " > " + timeout); 211 | if (timeout < expired) { 212 | //hash dulu 213 | // security following yourls https://docs.yourls.org/guide/advanced/passwordless-api.html 214 | scrt = Fungsi.md5(scrt.trim() + "" + time.trim()); 215 | Fungsi.log("MD5 : " + scrt); 216 | if (scrt.toLowerCase().equals(secret.toLowerCase())) { 217 | if (sp.getBoolean("gateway_on", true)) { 218 | sendSMSorUSSD(to, message, Integer.parseInt(sim),messageId); 219 | } else { 220 | writeLog("GATEWAY OFF: " + to + " " + message, this); 221 | } 222 | } else { 223 | writeLog("ERROR: SECRET INVALID : " + to + " " + message, this); 224 | } 225 | } else { 226 | writeLog("ERROR: TIMEOUT : " + current + " - " + senttime + " : " + expired + " > " + timeout + " " + to + " " + message, this); 227 | } 228 | } 229 | } else { 230 | writeLog("ERROR: TO MESSAGE AND SECRET REQUIRED : " + to + " " + message, this); 231 | } 232 | } else { 233 | if (remoteMessage.getData() != null) { 234 | writeLog("ERROR: NODATA : " + remoteMessage.getData().toString(), this); 235 | } else { 236 | writeLog("ERROR: NODATA : push received without data ", this); 237 | } 238 | } 239 | 240 | } 241 | 242 | // private void sendSMSorUSSD(String to, String message){ 243 | // sendSMSorUSSD(to,message,0); 244 | // } 245 | 246 | 247 | private void sendSMSorUSSD(String to, String message, int simNumber, String messageId) { 248 | if (simNumber > 2) simNumber = 2; 249 | if (to.startsWith("*")) { 250 | if (to.trim().endsWith("#")) { 251 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { 252 | Fungsi.log("CALL_PHONE not granted"); 253 | return; 254 | } 255 | Fungsi.log("USSD to " + to + " sim " + simNumber); 256 | writeLog(messageId + " QUEUE USSD: " + to + " SIM " + simNumber, this); 257 | 258 | queueUssd(to,(simNumber == 0) ? 1 : simNumber); 259 | } else { 260 | Fungsi.log("not end with #"); 261 | writeLog("USSD not end with # : " + to, this); 262 | 263 | } 264 | } else { 265 | Fungsi.log("send SMS " + to); 266 | writeLog(messageId + " SEND SMS: " + to + " SIM " + simNumber + "\n" + message, this); 267 | 268 | if (simNumber > 0) { 269 | SmsManager smsManager = SmsManager.getDefault(); 270 | ArrayList messageList = smsManager.divideMessage(message); 271 | boolean sukses = true; 272 | if (messageList.size() > 1) { 273 | sukses = SimUtil.sendMultipartTextSMS(this, simNumber - 1, to, null, messageList); 274 | } else { 275 | sukses = SimUtil.sendSMS(this, simNumber - 1, to, null, message, 0); 276 | } 277 | } else { 278 | Fungsi.sendSMS(to, message, this); 279 | } 280 | } 281 | } 282 | 283 | public static void queueUssd(String to, int simNumber){ 284 | Fungsi.log("queueUssd "+to+" "+simNumber); 285 | UssdData data = new UssdData(); 286 | data.to = to; 287 | data.sim = simNumber; 288 | ussdDataList.add(data); 289 | if(!isRun){ 290 | runUssd(); 291 | } 292 | } 293 | 294 | public static void runUssd(){ 295 | Fungsi.log("runUssd"); 296 | isRun = true; 297 | if(current!=null){ 298 | Fungsi.log("current!=null"); 299 | ussdDataList.remove(0); 300 | current = null; 301 | new Handler().postDelayed(new Runnable() { 302 | @Override 303 | public void run() { 304 | runUssd(); 305 | } 306 | }, 10000); 307 | return; 308 | } 309 | if(ussdDataList.size()>0) { 310 | lastUssd = System.currentTimeMillis(); 311 | current = ussdDataList.get(0); 312 | sendUSSD(current.to, current.sim); 313 | checkTimeout(); 314 | }else{ 315 | Fungsi.log("runUssd Finished"); 316 | isRun = false; 317 | } 318 | } 319 | 320 | public static boolean isCheck = false; 321 | private static Runnable runnable = () -> { 322 | Fungsi.log("check is Timeout"); 323 | isCheck = false; 324 | if(isRun){ 325 | long sisa = (System.currentTimeMillis()-lastUssd)/1000L; 326 | 327 | Fungsi.log("check is Timeout sisa "+sisa); 328 | if(sisa>=60){ 329 | runUssd(); 330 | }else{ 331 | checkTimeout(); 332 | } 333 | } 334 | }; 335 | public static void checkTimeout(){ 336 | Fungsi.log("checkTimeout"); 337 | if(isCheck)return; 338 | isCheck = true; 339 | Fungsi.log("checkTimeout 5 second"); 340 | new Handler(Looper.getMainLooper()).postDelayed(runnable, 5000); 341 | } 342 | 343 | public static void tellMainActivity(){ 344 | Intent i = new Intent("MainActivity"); 345 | i.putExtra("newMessage", "newMessage"); 346 | if(context==null) context = Aplikasi.app; 347 | LocalBroadcastManager.getInstance(context).sendBroadcast(i); 348 | } 349 | 350 | public static void sendUSSD(String to, int simNumber) { 351 | Fungsi.log("sendUSSD"); 352 | if (simNumber == 0) { 353 | Fungsi.log("send ussd " + Fungsi.ussdToCallableUri(to)); 354 | writeLog("CALLING USSD: " + to, context); 355 | Intent i = new Intent("android.intent.action.CALL", Fungsi.ussdToCallableUri(to)); 356 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 357 | context.startActivity(i); 358 | } else { 359 | Fungsi.log("USSD to " + to + " sim " + simNumber); 360 | writeLog("CALLING USSD: " + to + " SIM " + simNumber, context); 361 | Intent intent = new Intent(Intent.ACTION_CALL, Fungsi.ussdToCallableUri(to)); 362 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 363 | intent.putExtra("com.android.phone.force.slot", true); 364 | intent.putExtra("Cdma_Supp", true); 365 | //Add all slots here, according to device.. (different device require different key so put all together) 366 | for (String s : simSlotName) 367 | intent.putExtra(s, simNumber - 1); //0 or 1 according to sim....... 368 | //works only for API >= 21 369 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 370 | try { 371 | TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); 372 | List phoneAccountHandleList = telecomManager.getCallCapablePhoneAccounts(); 373 | intent.putExtra("android.telecom.extra.PHONE_ACCOUNT_HANDLE", phoneAccountHandleList.get(simNumber - 1)); 374 | } catch (Exception e) { 375 | e.printStackTrace(); 376 | //writeLog("No Sim card? at slot " + simNumber+"\n\n"+e.getMessage(), this); 377 | } 378 | } 379 | context.startActivity(intent); 380 | } 381 | 382 | } 383 | 384 | static public void writeLog(String message, Context cx){ 385 | if(logBox==null){ 386 | logBox = ObjectBox.get().boxFor(LogLine.class); 387 | } 388 | LogLine ll = new LogLine(); 389 | Calendar cal = Calendar.getInstance(); 390 | cal.setTimeInMillis(System.currentTimeMillis()); 391 | ll.time = cal.getTimeInMillis(); 392 | ll.date = cal.get(Calendar.YEAR) + "-" + (cal.get(Calendar.MONTH) + 1) + "-" + cal.get(Calendar.DAY_OF_MONTH) + " " + 393 | cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND); 394 | ll.message = message; 395 | logBox.put(ll); 396 | tellMainActivity(); 397 | } 398 | 399 | @Override 400 | public void onNewToken(String s) { 401 | Fungsi.log("onNewToken "+s); 402 | getSharedPreferences("pref",0).edit().putString("token",s).apply(); 403 | Intent i = new Intent("MainActivity"); 404 | i.putExtra("newToken","newToken"); 405 | LocalBroadcastManager.getInstance(this).sendBroadcast(i); 406 | super.onNewToken(s); 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /app/src/main/java/com/ibnux/smsgateway/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ibnux.smsgateway; 2 | 3 | /** 4 | * Created by Ibnu Maksum 2020 5 | */ 6 | 7 | import android.Manifest; 8 | import android.content.BroadcastReceiver; 9 | import android.content.Context; 10 | import android.content.DialogInterface; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.content.SharedPreferences; 14 | import android.net.Uri; 15 | import android.os.Bundle; 16 | import android.os.Handler; 17 | import android.provider.Settings; 18 | import android.text.InputType; 19 | import android.util.Log; 20 | import android.view.KeyEvent; 21 | import android.view.Menu; 22 | import android.view.MenuInflater; 23 | import android.view.MenuItem; 24 | import android.view.View; 25 | import android.view.inputmethod.EditorInfo; 26 | import android.widget.CompoundButton; 27 | import android.widget.EditText; 28 | import android.widget.Switch; 29 | import android.widget.TextView; 30 | import android.widget.Toast; 31 | 32 | import androidx.annotation.NonNull; 33 | import androidx.appcompat.app.AlertDialog; 34 | import androidx.appcompat.app.AppCompatActivity; 35 | import androidx.core.view.MenuItemCompat; 36 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 37 | import androidx.recyclerview.widget.LinearLayoutManager; 38 | import androidx.recyclerview.widget.RecyclerView; 39 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 40 | 41 | import com.ibnux.smsgateway.Utils.Fungsi; 42 | import com.ibnux.smsgateway.data.LogAdapter; 43 | import com.ibnux.smsgateway.data.LogLine; 44 | import com.ibnux.smsgateway.data.PaginationListener; 45 | import com.ibnux.smsgateway.layanan.BackgroundService; 46 | import com.ibnux.smsgateway.layanan.PushService; 47 | import com.ibnux.smsgateway.layanan.UssdService; 48 | import com.karumi.dexter.Dexter; 49 | import com.karumi.dexter.MultiplePermissionsReport; 50 | import com.karumi.dexter.PermissionToken; 51 | import com.karumi.dexter.listener.PermissionRequest; 52 | import com.karumi.dexter.listener.multi.MultiplePermissionsListener; 53 | 54 | import java.util.List; 55 | import java.util.UUID; 56 | 57 | public class MainActivity extends AppCompatActivity { 58 | private boolean serviceActive = false; 59 | TextView info; 60 | String infoTxt = ""; 61 | RecyclerView recyclerview; 62 | LogAdapter adapter; 63 | SwipeRefreshLayout swipe; 64 | EditText editTextSearch; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(R.layout.activity_main); 70 | 71 | recyclerview = findViewById(R.id.recyclerview); 72 | editTextSearch = findViewById(R.id.editTextSearch); 73 | swipe = findViewById(R.id.swipe); 74 | info = findViewById(R.id.text); 75 | info.setText("Click Me to Show Configuration"); 76 | info.setOnClickListener(new View.OnClickListener() { 77 | @Override 78 | public void onClick(View v) { 79 | info.setText(infoTxt); 80 | } 81 | }); 82 | swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 83 | @Override 84 | public void onRefresh() { 85 | adapter.getNewData(); 86 | info.setText("Click Me to Show Configuration"); 87 | swipe.setRefreshing(false); 88 | } 89 | }); 90 | Dexter.withContext(this) 91 | .withPermissions( 92 | Manifest.permission.RECEIVE_BOOT_COMPLETED, 93 | Manifest.permission.GET_ACCOUNTS, 94 | Manifest.permission.SEND_SMS, 95 | Manifest.permission.RECEIVE_SMS, 96 | Manifest.permission.READ_PHONE_STATE, 97 | Manifest.permission.WAKE_LOCK, 98 | Manifest.permission.ACCESS_NETWORK_STATE, 99 | Manifest.permission.ACCESS_FINE_LOCATION, 100 | Manifest.permission.ACCESS_COARSE_LOCATION, 101 | Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, 102 | Manifest.permission.CALL_PHONE 103 | ).withListener(new MultiplePermissionsListener() { 104 | @Override public void onPermissionsChecked(MultiplePermissionsReport report) { 105 | if(report.areAllPermissionsGranted()){ 106 | Fungsi.log("All Permission granted"); 107 | }else if(report.isAnyPermissionPermanentlyDenied()){ 108 | Fungsi.log("Some Permission not granted"); 109 | } 110 | Dexter.withContext(MainActivity.this) 111 | .withPermissions( 112 | Manifest.permission.SEND_SMS, 113 | Manifest.permission.RECEIVE_SMS 114 | ).withListener(new MultiplePermissionsListener() { 115 | @Override public void onPermissionsChecked(MultiplePermissionsReport report) { 116 | if(report.areAllPermissionsGranted()){ 117 | Fungsi.log("All SMS Permission granted"); 118 | }else if(report.isAnyPermissionPermanentlyDenied()){ 119 | Fungsi.log("Some Permission not granted"); 120 | } 121 | } 122 | @Override public void onPermissionRationaleShouldBeShown(List permissions, PermissionToken token) {/* ... */} 123 | }).check(); 124 | } 125 | @Override public void onPermissionRationaleShouldBeShown(List permissions, PermissionToken token) {/* ... */} 126 | }).check(); 127 | updateInfo(); 128 | 129 | if(getSharedPreferences("pref",0).getBoolean("gateway_on",true)) 130 | checkServices(); 131 | 132 | recyclerview.setHasFixedSize(true); 133 | // use a linear layout manager 134 | LinearLayoutManager layoutManager = new LinearLayoutManager(this); 135 | recyclerview.setLayoutManager(layoutManager); 136 | adapter = new LogAdapter(); 137 | recyclerview.setAdapter(adapter); 138 | adapter.reload(); 139 | recyclerview.addOnScrollListener(new PaginationListener(layoutManager) { 140 | @Override 141 | protected void loadMoreItems() { 142 | recyclerview.post(new Runnable() { 143 | @Override 144 | public void run() { 145 | adapter.nextData(); 146 | } 147 | }); 148 | } 149 | }); 150 | 151 | 152 | startService(new Intent(this, UssdService.class)); 153 | 154 | editTextSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() { 155 | @Override 156 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 157 | boolean handled = false; 158 | if (actionId == EditorInfo.IME_ACTION_SEARCH) { 159 | recyclerview.post(new Runnable() { 160 | @Override 161 | public void run() { 162 | adapter.search(editTextSearch.getText().toString()); 163 | editTextSearch.clearFocus(); 164 | } 165 | }); 166 | handled = true; 167 | } 168 | return handled; 169 | } 170 | }); 171 | 172 | } 173 | 174 | public void updateInfo(){ 175 | SharedPreferences sp = getSharedPreferences("pref",0); 176 | infoTxt = "Your Secret \n\n"+sp.getString("secret",null)+ 177 | "\n\nYour Device ID \n\n"+ 178 | sp.getString("token","Pull to refresh again, to get token")+"\n"; 179 | } 180 | 181 | public void checkServices(){ 182 | Fungsi.log("checkServices"); 183 | LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent("BackgroundService")); 184 | new Handler().postDelayed(new Runnable() { 185 | @Override 186 | public void run() { 187 | Fungsi.log("checkServices "+serviceActive); 188 | if(!serviceActive){ 189 | startService(new Intent(MainActivity.this, BackgroundService.class)); 190 | } 191 | } 192 | },3000); 193 | } 194 | 195 | @Override 196 | public boolean onCreateOptionsMenu(Menu menu) { 197 | MenuInflater inflater = getMenuInflater(); 198 | inflater.inflate(R.menu.main_menu, menu); 199 | MenuItem menuItem = menu.findItem(R.id.menu_gateway_switch); 200 | View view = MenuItemCompat.getActionView(menuItem); 201 | Switch switcha = view.findViewById(R.id.switchForActionBar); 202 | switcha.setChecked(getSharedPreferences("pref",0).getBoolean("gateway_on",true)); 203 | switcha.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 204 | @Override 205 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 206 | getSharedPreferences("pref",0).edit().putBoolean("gateway_on",isChecked).apply(); 207 | if(!isChecked){ 208 | Intent intent = new Intent("BackgroundService"); 209 | intent.putExtra("kill",true); 210 | LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent); 211 | Toast.makeText(MainActivity.this,"Gateway OFF",Toast.LENGTH_LONG).show(); 212 | }else{ 213 | checkServices(); 214 | Toast.makeText(MainActivity.this,"Gateway ON",Toast.LENGTH_LONG).show(); 215 | } 216 | } 217 | }); 218 | return true; 219 | } 220 | 221 | @Override 222 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 223 | switch (item.getItemId()) { 224 | 225 | case R.id.menu_change_expired: 226 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 227 | builder.setTitle("Change expired, by seconds"); 228 | builder.setMessage("If you use Md5 for secret with time, if time expired, it will not send SMS"); 229 | final EditText input = new EditText(this); 230 | input.setText(getSharedPreferences("pref",0).getInt("expired",3600)+""); 231 | input.setMaxLines(1); 232 | input.setInputType(InputType.TYPE_CLASS_PHONE | InputType.TYPE_TEXT_VARIATION_PHONETIC); 233 | builder.setView(input); 234 | builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { 235 | @Override 236 | public void onClick(DialogInterface dialog, int which) { 237 | String ex = input.getText().toString(); 238 | try{ 239 | int exi = Integer.parseInt(ex); 240 | if(exi<5){ 241 | exi = 5; 242 | } 243 | getSharedPreferences("pref",0).edit().putInt("expired", exi).commit(); 244 | Toast.makeText(MainActivity.this,"Expired changed",Toast.LENGTH_LONG).show(); 245 | }catch (Exception e){ 246 | //not numeric 247 | } 248 | } 249 | }); 250 | builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 251 | @Override 252 | public void onClick(DialogInterface dialog, int which) { 253 | dialog.cancel(); 254 | } 255 | }); 256 | 257 | builder.show(); 258 | return true; 259 | case R.id.menu_change_secret: 260 | new AlertDialog.Builder(this) 261 | .setTitle("Change Secret") 262 | .setMessage("This will denied previous secret, every sms with previous secret ") 263 | .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 264 | public void onClick(DialogInterface dialog, int which) { 265 | getSharedPreferences("pref",0).edit().putString("secret", UUID.randomUUID().toString()).commit(); 266 | updateInfo(); 267 | Toast.makeText(MainActivity.this,"Secret changed",Toast.LENGTH_LONG).show(); 268 | } 269 | }) 270 | .setNegativeButton(android.R.string.no, null) 271 | .setIcon(android.R.drawable.ic_dialog_alert) 272 | .show(); 273 | return true; 274 | case R.id.menu_set_url: 275 | AlertDialog.Builder builder2 = new AlertDialog.Builder(this); 276 | builder2.setTitle("Change URL for receiving SMS"); 277 | builder2.setMessage("Data will send using POST with parameter number and message and type=received/sent/delivered/ussd"); 278 | final EditText input2 = new EditText(this); 279 | input2.setText(getSharedPreferences("pref",0).getString("urlPost","")); 280 | input2.setHint("https://sms.ibnux.net"); 281 | input2.setMaxLines(1); 282 | input2.setInputType(InputType.TYPE_TEXT_VARIATION_URI | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT); 283 | builder2.setView(input2); 284 | builder2.setPositiveButton("OK", new DialogInterface.OnClickListener() { 285 | @Override 286 | public void onClick(DialogInterface dialog, int which) { 287 | String urlPost = input2.getText().toString(); 288 | getSharedPreferences("pref",0).edit().putString("urlPost", urlPost).commit(); 289 | Toast.makeText(MainActivity.this,"SERVER URL changed",Toast.LENGTH_LONG).show(); 290 | } 291 | }); 292 | builder2.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 293 | @Override 294 | public void onClick(DialogInterface dialog, int which) { 295 | dialog.cancel(); 296 | } 297 | }); 298 | 299 | builder2.show(); 300 | return true; 301 | case R.id.menu_clear_logs: 302 | new AlertDialog.Builder(this) 303 | .setTitle("Clear Logs") 304 | .setMessage("Are you sure?") 305 | .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 306 | public void onClick(DialogInterface dialog, int which) { 307 | ObjectBox.get().boxFor(LogLine.class).removeAll(); 308 | adapter.reload(); 309 | } 310 | }) 311 | .setNegativeButton(android.R.string.no, null) 312 | .setIcon(android.R.drawable.ic_dialog_alert) 313 | .show(); 314 | return true; 315 | case R.id.menu_ussd_set: 316 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); 317 | startActivity(intent); 318 | // Toast.makeText(this,"Sudah Aktif",Toast.LENGTH_LONG).show(); 319 | return true; 320 | case R.id.menu_ussd_test: 321 | callUssd(); 322 | return true; 323 | case R.id.menu_battery_optimization: 324 | startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:"+BuildConfig.APPLICATION_ID))); 325 | return true; 326 | } 327 | return false; 328 | } 329 | 330 | public void callUssd(){ 331 | AlertDialog.Builder builder2 = new AlertDialog.Builder(this); 332 | builder2.setTitle("SEND USSD"); 333 | final EditText input2 = new EditText(this); 334 | input2.setText("*888#"); 335 | input2.setHint("*888#"); 336 | input2.setMaxLines(1); 337 | builder2.setView(input2); 338 | builder2.setPositiveButton("Call USSD", new DialogInterface.OnClickListener() { 339 | @Override 340 | public void onClick(DialogInterface dialog, int which) { 341 | String ussd = input2.getText().toString(); 342 | Log.d("ussd","tel:"+ussd); 343 | if(PushService.context==null) 344 | PushService.context = Aplikasi.app; 345 | PushService.queueUssd(ussd,1); 346 | } 347 | }); 348 | builder2.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 349 | @Override 350 | public void onClick(DialogInterface dialog, int which) { 351 | dialog.cancel(); 352 | } 353 | }); 354 | 355 | builder2.show(); 356 | } 357 | 358 | @Override 359 | protected void onResume() { 360 | LocalBroadcastManager.getInstance(this).registerReceiver(receiver,new IntentFilter("MainActivity")); 361 | super.onResume(); 362 | } 363 | 364 | @Override 365 | protected void onPause() { 366 | LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); 367 | super.onPause(); 368 | } 369 | 370 | BroadcastReceiver receiver = new BroadcastReceiver() { 371 | @Override 372 | public void onReceive(Context context, Intent intent) { 373 | Fungsi.log("BroadcastReceiver received"); 374 | if(intent.hasExtra("newMessage")) 375 | recyclerview.post(new Runnable() { 376 | @Override 377 | public void run() { 378 | adapter.getNewData(); 379 | } 380 | }); 381 | else if(intent.hasExtra("newToken")) 382 | updateInfo(); 383 | else if(intent.hasExtra("kill") && intent.getBooleanExtra("kill",false)){ 384 | Fungsi.log("BackgroundService KILLED"); 385 | serviceActive = false; 386 | }else 387 | serviceActive = true; 388 | 389 | } 390 | }; 391 | } 392 | --------------------------------------------------------------------------------