diff --git a/SmsInterceptor/.gitignore b/SmsInterceptor/.gitignore new file mode 100644 index 0000000..2b82276 --- /dev/null +++ b/SmsInterceptor/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/ +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/SmsInterceptor/README.md b/SmsInterceptor/README.md new file mode 100644 index 0000000..57eb7f7 --- /dev/null +++ b/SmsInterceptor/README.md @@ -0,0 +1,12 @@ +Приложение для перехвата и анализа входящих СМС +================================================ +Приложение состоит из одного экрана, где можно указать номер телефона, смс от которого необходимо перехватывать. + +![Screen_example](arts/screen1.jpg) + +**Как импортировать в свой проект?** + +Вся логика происходит в классе SmsListener.
+Его необходимо скопировать и добавить в AndroidManifest.xml своего проекта +Также на устройствах начиная от 6.0 для чтения входящих смс необходимо получить разрешение от пользователя +на чтение смс `Manifest.permission.RECEIVE_SMS`. \ No newline at end of file diff --git a/SmsInterceptor/app/.gitignore b/SmsInterceptor/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/SmsInterceptor/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/SmsInterceptor/app/build.gradle b/SmsInterceptor/app/build.gradle new file mode 100644 index 0000000..4c554ba --- /dev/null +++ b/SmsInterceptor/app/build.gradle @@ -0,0 +1,40 @@ +apply plugin: 'com.android.application' +apply plugin: 'me.tatarka.retrolambda' +apply plugin: 'com.neenbedankt.android-apt' + +android { + compileSdkVersion 25 + buildToolsVersion "25.0.0" + defaultConfig { + applicationId "com.example.ereminilya.smsinterceptor" + minSdkVersion 15 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + +} + +dependencies { + compile 'com.android.support:appcompat-v7:25.0.0' + compile 'com.jakewharton:butterknife:7.0.0' + compile 'com.karumi:dexter:2.3.1' + + compile "com.google.dagger:dagger:2.5" + apt "com.google.dagger:dagger-compiler:2.5" + provided 'javax.annotation:jsr250-api:1.0' + + testCompile 'junit:junit:4.12' +} \ No newline at end of file diff --git a/SmsInterceptor/app/proguard-rules.pro b/SmsInterceptor/app/proguard-rules.pro new file mode 100644 index 0000000..2057f19 --- /dev/null +++ b/SmsInterceptor/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/ereminilya/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/SmsInterceptor/app/src/main/AndroidManifest.xml b/SmsInterceptor/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d2bbdf7 --- /dev/null +++ b/SmsInterceptor/app/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/App.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/App.java new file mode 100644 index 0000000..fd1da4d --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/App.java @@ -0,0 +1,27 @@ +package com.example.ereminilya.smsinterceptor; + +import android.app.Application; +import android.support.annotation.NonNull; + +import com.example.ereminilya.smsinterceptor.utils.di.AppComponent; +import com.example.ereminilya.smsinterceptor.utils.di.AppModule; +import com.example.ereminilya.smsinterceptor.utils.di.DaggerAppComponent; +import com.karumi.dexter.Dexter; + +/** + * Created by ereminilya on 24/11/16. + */ +public class App extends Application { + + private AppComponent component; + + @Override public void onCreate() { + super.onCreate(); + Dexter.initialize(this); + component = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); + } + + @NonNull public AppComponent getComponent() { + return component; + } +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/Settings.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/Settings.java new file mode 100644 index 0000000..a1dbbc8 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/Settings.java @@ -0,0 +1,37 @@ +package com.example.ereminilya.smsinterceptor; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.example.ereminilya.smsinterceptor.utils.storage.Storage; + +/** + * Created by ereminilya on 24/11/16. + */ +public class Settings { + + private static final String KEY_PHONE_TO_INTERCEPT = "phoneToIntercept"; + private static final String KEY_INTERCEPTION_ENABLED = "interceptionEnabled"; + + private final Storage storage; + + public Settings(Storage storage) { + this.storage = storage; + } + + public void saveNumber(@NonNull String phone) { + storage.putString(KEY_PHONE_TO_INTERCEPT, phone); + } + + public void enableInterception(boolean enabled) { + storage.putBoolean(KEY_INTERCEPTION_ENABLED, enabled); + } + + public boolean isInterceptionEnabled() { + return storage.getBoolean(KEY_INTERCEPTION_ENABLED, true); + } + + @Nullable public String getPhoneToSpy() { + return storage.getString(KEY_PHONE_TO_INTERCEPT); + } +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SettingsScreen.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SettingsScreen.java new file mode 100644 index 0000000..86b4e80 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SettingsScreen.java @@ -0,0 +1,91 @@ +package com.example.ereminilya.smsinterceptor; + +import android.Manifest; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.SwitchCompat; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.example.ereminilya.smsinterceptor.utils.di.Injector; +import com.karumi.dexter.Dexter; +import com.karumi.dexter.PermissionToken; +import com.karumi.dexter.listener.PermissionDeniedResponse; +import com.karumi.dexter.listener.PermissionGrantedResponse; +import com.karumi.dexter.listener.PermissionRequest; +import com.karumi.dexter.listener.single.PermissionListener; + +import javax.inject.Inject; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class SettingsScreen extends AppCompatActivity { + + @Inject Settings settings; + + @Bind(R.id.phone_to_spy) TextView uiPhoneToSpy; + @Bind(R.id.sms_interception_toggle) SwitchCompat uiInterceptionEnabledToggle; + @Bind(R.id.grant_permission) View uiGrantPermissionBtn; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings_screen); + ButterKnife.bind(this); + Injector.inject(this); + if (savedInstanceState == null) { + uiInterceptionEnabledToggle.setChecked(settings.isInterceptionEnabled()); + String phoneToSpy = settings.getPhoneToSpy(); + if (phoneToSpy != null) { + uiPhoneToSpy.setText(phoneToSpy); + } + } + } + + @Override protected void onResume() { + super.onResume(); + int status = ActivityCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS); + if (status != PackageManager.PERMISSION_GRANTED) { + uiGrantPermissionBtn.setVisibility(View.VISIBLE); + } + } + + @OnClick(R.id.grant_permission) void onGrantPermissioClick() { + Dexter.checkPermission(new PermissionListener() { + @Override public void onPermissionGranted(PermissionGrantedResponse response) { + Toast.makeText(SettingsScreen.this, R.string.receive_sms_granted, Toast.LENGTH_SHORT).show(); + uiGrantPermissionBtn.setVisibility(View.GONE); + } + + @Override public void onPermissionDenied(PermissionDeniedResponse response) { + + } + + @Override + public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { + + } + }, Manifest.permission.RECEIVE_SMS); + } + + @OnClick(R.id.sms_interception_toggle) void onToggleClick(SwitchCompat toggle) { + boolean finalState = toggle.isChecked(); + toggle.setChecked(finalState); + settings.enableInterception(finalState); + } + + @OnClick(R.id.save) void onSaveClick() { + String phone = uiPhoneToSpy.getText().toString(); + settings.saveNumber(phone); + Toast.makeText(this, getString(R.string.phone_updated, phone), Toast.LENGTH_SHORT).show(); + } + +// Пользователь может внести телефон отправителя, смс от которого нужно перехватывать. +// Сообщение перехватывается и открывается экран с текстом этого сообщения. +// Приложении деплоить и описание в один из репозиториев примеров. +} \ No newline at end of file diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SmsListener.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SmsListener.java new file mode 100644 index 0000000..fff4742 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/SmsListener.java @@ -0,0 +1,56 @@ +package com.example.ereminilya.smsinterceptor; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.telephony.SmsMessage; +import android.widget.Toast; + +import com.example.ereminilya.smsinterceptor.utils.Strings; +import com.example.ereminilya.smsinterceptor.utils.di.Injector; + +import javax.inject.Inject; + +/** + * Created by ereminilya on 24/11/16. + */ + +public class SmsListener extends BroadcastReceiver { + + @Inject Settings settings; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { + Injector.inject(this, context); + Bundle bundle = intent.getExtras(); + SmsMessage[] messages; + if (bundle != null) { + try { + Object[] pdus = (Object[]) bundle.get("pdus"); + messages = new SmsMessage[pdus.length]; + messages[0] = SmsMessage.createFromPdu((byte[]) pdus[0]); + String wholeString = getMessageText(messages[0]); + String sender = messages[0].getOriginatingAddress(); + if (Strings.equals(settings.getPhoneToSpy(), sender)) { + Toast.makeText(context, "message from " + sender + " intercepted: " + wholeString, + Toast.LENGTH_SHORT).show(); + } + } catch (Exception ignored) { + Toast.makeText(context, "Something went wrong", Toast.LENGTH_SHORT).show(); + } + } + } + } + + /** + * if you need message body you can get it here + * @param message is intercepted SMS + * @return message text + */ + private String getMessageText(@NonNull SmsMessage message) { + return message.getMessageBody(); + } +} \ No newline at end of file diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/Strings.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/Strings.java new file mode 100644 index 0000000..57c9c38 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/Strings.java @@ -0,0 +1,13 @@ +package com.example.ereminilya.smsinterceptor.utils; + +import android.support.annotation.Nullable; + +/** + * Created by ereminilya on 24/11/16. + */ +public class Strings { + + public static boolean equals(@Nullable String str1, @Nullable String str2) { + return !(str1 == null || str2 == null) && str1.equals(str2); + } +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppComponent.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppComponent.java new file mode 100644 index 0000000..a76ae40 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppComponent.java @@ -0,0 +1,20 @@ +package com.example.ereminilya.smsinterceptor.utils.di; + +import com.example.ereminilya.smsinterceptor.SettingsScreen; +import com.example.ereminilya.smsinterceptor.SmsListener; + +import javax.inject.Singleton; + +import dagger.Component; + +/** + * Created by ereminilya on 24/11/16. + */ +@Singleton +@Component(modules = {AppModule.class}) +public interface AppComponent { + + void inject(SettingsScreen settingsScreen); + + void inject(SmsListener smsListener); +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppModule.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppModule.java new file mode 100644 index 0000000..802eb0c --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/AppModule.java @@ -0,0 +1,47 @@ +package com.example.ereminilya.smsinterceptor.utils.di; + +import android.app.Application; +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; + +import com.example.ereminilya.smsinterceptor.Settings; +import com.example.ereminilya.smsinterceptor.utils.storage.SharedPrefsStorage; +import com.example.ereminilya.smsinterceptor.utils.storage.Storage; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Created by ereminilya on 24/11/16. + */ +@Module +public class AppModule { + + private Application mApplication; + + public AppModule(Application application) { + mApplication = application; + } + + @Provides + @Singleton Context providesApplication() { + return mApplication; + } + + + @Provides @Singleton Settings providesSettings(Storage storage) { + return new Settings(storage); + } + + @Provides @Singleton Storage providesStorage(SharedPreferences sharedPreferences) { + return new SharedPrefsStorage(sharedPreferences); + } + + @Provides @Singleton public SharedPreferences providesSharedPrefs(@NonNull Context context) { + return PreferenceManager.getDefaultSharedPreferences(context); + } +} \ No newline at end of file diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/Injector.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/Injector.java new file mode 100644 index 0000000..cbada41 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/di/Injector.java @@ -0,0 +1,22 @@ +package com.example.ereminilya.smsinterceptor.utils.di; + +import android.content.Context; + +import com.example.ereminilya.smsinterceptor.App; +import com.example.ereminilya.smsinterceptor.SettingsScreen; +import com.example.ereminilya.smsinterceptor.SmsListener; + +/** + * Created by ereminilya on 24/11/16. + */ + +public class Injector { + + public static void inject(SettingsScreen settingsScreen) { + ((App) settingsScreen.getApplicationContext()).getComponent().inject(settingsScreen); + } + + public static void inject(SmsListener smsListener, Context context) { + ((App) context.getApplicationContext()).getComponent().inject(smsListener); + } +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/SharedPrefsStorage.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/SharedPrefsStorage.java new file mode 100644 index 0000000..d668811 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/SharedPrefsStorage.java @@ -0,0 +1,61 @@ +package com.example.ereminilya.smsinterceptor.utils.storage; + +import android.content.SharedPreferences; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * Created by ereminilya on 24/11/16. + */ +public class SharedPrefsStorage implements Storage { + + private final SharedPreferences sp; + + public SharedPrefsStorage(SharedPreferences sp) { + this.sp = sp; + } + + @Override public boolean contains(@NonNull String key) { + return sp.contains(key); + } + + @Override public void putLong(@NonNull String key, long number) { + sp.edit().putLong(key, number).apply(); + } + + @Override public long getLong(@NonNull String key, long defaultValue) { + return sp.getLong(key, defaultValue); + } + + @Override public void putInt(@NonNull String key, int number) { + sp.edit().putInt(key, number).apply(); + } + + @Override public int getInt(@NonNull String key, int defaultValue) { + return sp.getInt(key, defaultValue); + } + + @Override public void putBoolean(@NonNull String key, boolean value) { + sp.edit().putBoolean(key, value).apply(); + } + + @Override public boolean getBoolean(@NonNull String key, boolean defaultValue) { + return sp.getBoolean(key, defaultValue); + } + + @Override public void putString(@NonNull String key, @NonNull String str) { + sp.edit().putString(key, str).apply(); + } + + @Nullable @Override public String getString(@NonNull String key) { + return sp.getString(key, null); + } + + @Override public void remove(@NonNull String key) { + sp.edit().remove(key).apply(); + } + + @Override public void clear() { + sp.edit().clear().apply(); + } +} diff --git a/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/Storage.java b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/Storage.java new file mode 100644 index 0000000..39e4872 --- /dev/null +++ b/SmsInterceptor/app/src/main/java/com/example/ereminilya/smsinterceptor/utils/storage/Storage.java @@ -0,0 +1,32 @@ +package com.example.ereminilya.smsinterceptor.utils.storage; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * Created by ereminilya on 24/11/16. + */ +public interface Storage { + + boolean contains(@NonNull String key); + + void putLong(@NonNull String key, long number); + + long getLong(@NonNull String key, long defaultValue); + + void putInt(@NonNull String key, int number); + + int getInt(@NonNull String key, int defaultValue); + + void putBoolean(@NonNull String key, boolean value); + + boolean getBoolean(@NonNull String key, boolean defaultValue); + + void putString(@NonNull final String key, @NonNull String str); + + @Nullable String getString(@NonNull String key); + + void remove(@NonNull String key); + + void clear(); +} diff --git a/SmsInterceptor/app/src/main/res/layout/activity_settings_screen.xml b/SmsInterceptor/app/src/main/res/layout/activity_settings_screen.xml new file mode 100644 index 0000000..3135f10 --- /dev/null +++ b/SmsInterceptor/app/src/main/res/layout/activity_settings_screen.xml @@ -0,0 +1,41 @@ + + + + + + + +