├── 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 |
10 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
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 |
6 |
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 |
--------------------------------------------------------------------------------