├── www
└── smsRetriever.js
├── package.json
├── plugin.xml
├── readme.md
└── src
└── android
└── com
└── codingsans
└── ionic
└── smsRetriever
└── AndroidSmsRetriever.java
/www/smsRetriever.js:
--------------------------------------------------------------------------------
1 | var SmsRetrieverLoader = function(require, exports, module) {
2 | var exec = require('cordova/exec');
3 |
4 | function SmsRetriever() {}
5 |
6 | SmsRetriever.prototype.startWatching = function(success, failure) {
7 | exec(success, failure, 'AndroidSmsRetriever', 'start', []);
8 | };
9 |
10 | SmsRetriever.prototype.getAppHash = function (success, failure) {
11 | exec(success, failure, 'AndroidSmsRetriever', 'hash', []);
12 | };
13 | var smsRetriever = new SmsRetriever();
14 | module.exports = smsRetriever;
15 | };
16 |
17 | SmsRetrieverLoader(require, exports, module);
18 |
19 | cordova.define("cordova/plugin/SmsRetriever", SmsRetrieverLoader);
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-sms-retriever-manager",
3 | "version": "1.0.5",
4 | "description": "Easily retrieve SMS messages using the Google SMS Retriever API with our cross-platform plugin designed for Ionic/Cordova. This powerful tool streamlines the process and is specifically available for Android devices.",
5 | "cordova": {
6 | "id": "cordova-plugin-sms-retriever-manager",
7 | "platforms": [
8 | "android"
9 | ]
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/hanatharesh2712/ionic-native-sms-retriever-plugin-master.git"
14 | },
15 | "keywords": [
16 | "cordova",
17 | "phonegap",
18 | "sms",
19 | "ecosystem:cordova",
20 | "cordova-android"
21 | ],
22 | "engines": [
23 | {
24 | "name": "cordova",
25 | "version": ">=3.0.0"
26 | }
27 | ],
28 | "author": "Haresh Hanat",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/hanatharesh2712/ionic-native-sms-retriever-plugin-master/issues"
32 | },
33 | "homepage": "https://github.com/hanatharesh2712/ionic-native-sms-retriever-plugin-master"
34 | }
35 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | Cordova SMS Retriver Plugin
5 | Easily retrieve SMS messages using the Google SMS Retriever API with our cross-platform plugin designed for Ionic/Cordova. This powerful tool streamlines the process and is specifically available for Android devices.
6 | MIT
7 | ionic, ionic3, cordova, cordova-plugin, phonegap-plugin, ionic-framework, ionic-cordova, sms, sms-verificationm automatic SMS receive
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # ionic-native-sms-retriever-plugin-master
2 | # Cordova SMS retriver Plugin
3 |
4 | Cross-platform plugin for Cordova / PhoneGap to to easily retrive SMS for your APP without need permission of SMS_READ. Available for **Android**.
5 |
6 | ## Installing the plugin
7 |
8 | Using the Cordova CLI run:
9 |
10 | ```
11 | ionic cordova plugin add cordova-plugin-sms-retriever-manager
12 | npm install @ionic-native/sms-retriever
13 | ```
14 |
15 | It is also possible to install via repo url directly (unstable), run :
16 |
17 | ```sh
18 | ionic cordova plugin add https://github.com/hanatharesh2712/ionic-native-sms-retriever-plugin-master.git
19 | ```
20 | You can find working Demo for Cordova here: https://github.com/hanatharesh2712/automatic-sms-cordova/tree/main/hello
21 | ## Using the plugin
22 | HTML
23 |
24 | ```html
25 |
26 |
27 | ```
28 |
29 | Javascript
30 |
31 | ```js
32 | document.querySelector('.hashCode').addEventListener('click', this.getAppHash);
33 | document.querySelector('.startWatching').addEventListener('click', this.retriveSMS);
34 |
35 | var app = {
36 | getAppHash: function() {
37 | window['cordova']['plugins']['smsRetriever']['getAppHash'](
38 | (result) => {
39 | // Once you get this hash code of your app. Please remove this code.
40 | alert(result);
41 | console.log('Hash', result);
42 | },
43 | (err) => {
44 | console.log(err);
45 | });
46 | },
47 |
48 | retriveSMS: function() {
49 | window['cordova']['plugins']['smsRetriever']['startWatching'](
50 | // the first callback is the success callback. We got back the native code’s result here.
51 | (result) => {
52 | alert(result.Message);
53 | console.log('Message', result);
54 | },
55 | // the second is the error callback where we get back the errors
56 | (err) => {
57 | console.log(err);
58 | });
59 | }
60 | };
61 | ```
62 | You can find working Demo for Ionic 4 here: https://github.com/hanatharesh2712/sms-plugin-test
63 |
64 | Typescript (Ionic 4)
65 | ```typescript
66 | import { SmsRetriever } from '@ionic-native/sms-retriever/ngx';
67 |
68 | constructor(private smsRetriever: SmsRetriever) { }
69 |
70 | ...
71 |
72 | // This function is to get hash string of APP.
73 | // * @return {Promise} Returns a promise that resolves when successfully generate hash of APP.
74 | this.smsRetriever.getAppHash()
75 | .then((res: any) => console.log(res))
76 | .catch((error: any) => console.error(error));
77 |
78 | // * This function start wathching message arrive event and retrive message text.
79 | // * @return {Promise} Returns a promise that resolves when retrives SMS text or TIMEOUT after 5 min.
80 | this.smsRetriever.startWatching()
81 | .then((res: any) => console.log(res))
82 | .catch((error: any) => console.error(error));
83 | ```
84 |
85 | You can find working Demo for Ionic 3 here: https://github.com/hanatharesh2712/sms-plugin-test-ionic-3
86 |
87 | Typescript (Ionic 3)
88 | ```typescript
89 |
90 | import { SmsRetriever } from '@ionic-native/sms-retriever/ngx';
91 | var smsRetriever = window['cordova']['plugins']['smsRetriever'];
92 |
93 | public smsTextmessage: string = '';
94 | public appHashString: string = '';
95 | constructor(private smsRetriever: SmsRetriever) { }
96 |
97 | ...
98 |
99 | getHashCode() {
100 | smsRetriever['getAppHash'](
101 | (res) => {
102 | this.appHashString = res;
103 | console.log(res);
104 | }, (err) => {
105 | console.warn(err);
106 | }
107 | );
108 | }
109 |
110 | getSMS() {
111 | smsRetriever['startWatching'](
112 | (res) => {
113 | this.smsTextmessage = res.Message;
114 | console.log(res);
115 | }, (err) => {
116 | console.warn(err);
117 | }
118 | );
119 | }
120 | ```
121 |
122 | Flow to test:
123 | [](https://raw.githubusercontent.com/hanatharesh2712/automatic-sms-cordova/main/hello/res/ref-images/sms%20plugin%20demo.png)
124 |
125 | You need to send your application hash in SMS when you are sending from your backend. to generate the hash of your application read this: https://developers.google.com/identity/sms-retriever/verify
126 |
127 | To get your application hash code:
128 |
129 | * Without the correct hash, your app won't recieve the message callback. This only needs to be
130 | * generated once per app and stored. Then you can remove this function from your code.
131 |
132 | BUILD FAILED
133 |
134 | The problem is that you need to make sure that you set the target to android-19 or later in your ./platforms/android/project.properties file like this:
135 |
136 | # Project target.
137 | target=android-19
138 |
139 |
140 | ## Donations
141 |
142 | If your app is successful or if you are working for a company, please consider donating some beer money :beer::
143 |
144 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YP2LMRCJMGTNJ&source=url)
145 |
146 | Keep in mind that I am maintaining this repository on my free time so thank you for considering a donation. :+1:
147 |
148 |
149 | ## Contributing
150 |
151 | I believe that everything is working, feel free to put in an issue or to fork and make pull requests if you want to add a new feature.
152 |
153 | Things you can fix:
154 | * Allow for null number to be passed in
155 | Right now, it breaks when a null value is passed in for a number, but it works if it's a blank string, and allows the user to pick the number
156 | It should automatically convert a null value to an empty string
157 |
158 | Thanks for considering contributing to this project.
159 |
160 | ### Finding something to do
161 |
162 | Ask, or pick an issue and comment on it announcing your desire to work on it. Ideally wait until we assign it to you to minimize work duplication.
163 |
164 | ### Reporting an issue
165 |
166 | - Search existing issues before raising a new one.
167 |
168 | - Include as much detail as possible.
169 |
170 | ### Pull requests
171 |
172 | - Make it clear in the issue tracker what you are working on, so that someone else doesn't duplicate the work.
173 |
174 | - Use a feature branch, not master.
175 |
176 | - Rebase your feature branch onto origin/master before raising the PR.
177 |
178 | - Keep up to date with changes in master so your PR is easy to merge.
179 |
180 | - Be descriptive in your PR message: what is it for, why is it needed, etc.
181 |
182 | - Make sure the tests pass
183 |
184 | - Squash related commits as much as possible.
185 |
186 | ### Coding style
187 |
188 | - Try to match the existing indent style.
189 |
190 | - Don't mix platform-specific stuff into the main code.
191 |
192 |
193 |
194 |
195 | ## History
196 |
197 |
198 | ## License
199 |
200 | The MIT License
201 |
202 | Permission is hereby granted, free of charge, to any person obtaining a copy of
203 | this software and associated documentation files (the "Software"), to deal in
204 | the Software without restriction, including without limitation the rights to
205 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
206 | the Software, and to permit persons to whom the Software is furnished to do so,
207 | subject to the following conditions:
208 |
209 | The above copyright notice and this permission notice shall be included in all
210 | copies or substantial portions of the Software.
211 |
212 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
213 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
214 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
215 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
216 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
217 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
218 |
--------------------------------------------------------------------------------
/src/android/com/codingsans/ionic/smsRetriever/AndroidSmsRetriever.java:
--------------------------------------------------------------------------------
1 | package com.codingsans.ionic.smsRetriever;
2 |
3 | import org.apache.cordova.CallbackContext;
4 | import org.apache.cordova.CordovaInterface;
5 | import org.apache.cordova.CordovaWebView;
6 | import org.apache.cordova.CordovaPlugin;
7 | import org.apache.cordova.PluginResult;
8 | import org.apache.cordova.LOG;
9 |
10 | import android.content.BroadcastReceiver;
11 | import android.content.Intent;
12 | import android.content.IntentFilter;
13 | import android.os.Bundle;
14 | import android.os.Build;
15 | import android.util.Log;
16 | import android.widget.Toast;
17 |
18 | import org.json.JSONArray;
19 | import org.json.JSONObject;
20 | import org.json.JSONException;
21 |
22 | import android.content.Context;
23 | import com.google.android.gms.auth.api.phone.SmsRetriever;
24 | import com.google.android.gms.auth.api.phone.SmsRetrieverClient;
25 | import com.google.android.gms.common.api.Status;
26 | import com.google.android.gms.tasks.OnFailureListener;
27 | import com.google.android.gms.tasks.OnSuccessListener;
28 | import com.google.android.gms.common.api.CommonStatusCodes;
29 | import com.google.android.gms.tasks.Task;
30 |
31 | import java.util.ArrayList;
32 |
33 | import static com.google.android.gms.common.api.CommonStatusCodes.*;
34 | import static android.content.Context.RECEIVER_EXPORTED;
35 |
36 | import android.content.ContextWrapper;
37 | import android.content.pm.PackageManager;
38 | import android.content.pm.Signature;
39 | import android.util.Base64;
40 |
41 | import java.nio.charset.StandardCharsets;
42 | import java.security.MessageDigest;
43 | import java.security.NoSuchAlgorithmException;
44 | import java.util.Arrays;
45 |
46 | public class AndroidSmsRetriever extends CordovaPlugin {
47 |
48 | private SmsRetrieverClient smsRetrieverClient;
49 | //public static final int MAX_TIMEOUT = 300000; // 5 mins in millis
50 | private static final String TAG = AndroidSmsRetriever.class.getSimpleName();
51 |
52 | private static final String HASH_TYPE = "SHA-256";
53 | public static final int NUM_HASHED_BYTES = 9;
54 | public static final int NUM_BASE64_CHAR = 11;
55 |
56 | private CallbackContext callbackContext;
57 | private JSONObject data = new JSONObject();
58 |
59 | @Override
60 | public void initialize(CordovaInterface cordova, CordovaWebView webView) {
61 | LOG.v(TAG, "SmsRetriever: initialization");
62 | //Toast.makeText(this.cordova.getActivity().getApplicationContext(),"SmsRetriever: initialization", Toast.LENGTH_SHORT).show();
63 |
64 | super.initialize(cordova, webView);
65 |
66 | // Get an instance of SmsRetrieverClient, used to start listening for a matching
67 | // SMS message.
68 | smsRetrieverClient = SmsRetriever.getClient(this.cordova.getActivity().getApplicationContext());
69 | }
70 |
71 | /**
72 | * Get all the app signatures for the current package
73 | * @return
74 | */
75 | public ArrayList getAppSignatures() {
76 | ArrayList appCodes = new ArrayList<>();
77 |
78 | try {
79 | // Get all package signatures for the current package
80 | String packageName = cordova.getActivity().getApplicationContext().getPackageName();
81 | PackageManager packageManager = cordova.getActivity().getApplicationContext().getPackageManager();
82 | Signature[] signatures = packageManager.getPackageInfo(packageName,
83 | PackageManager.GET_SIGNATURES).signatures;
84 |
85 | // For each signature create a compatible hash
86 | for (Signature signature : signatures) {
87 | String hash = hash(packageName, signature.toCharsString());
88 | if (hash != null) {
89 | appCodes.add(String.format("%s", hash));
90 | }
91 | }
92 | } catch (PackageManager.NameNotFoundException e) {
93 | Log.e(TAG, "Unable to find package to obtain hash.", e);
94 | }
95 | return appCodes;
96 | }
97 |
98 | private static String hash(String packageName, String signature) {
99 | String appInfo = packageName + " " + signature;
100 | try {
101 | MessageDigest messageDigest = MessageDigest.getInstance(HASH_TYPE);
102 | messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
103 | byte[] hashSignature = messageDigest.digest();
104 |
105 | // truncated into NUM_HASHED_BYTES
106 | hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES);
107 | // encode into Base64
108 | String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
109 | base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR);
110 |
111 | Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
112 |
113 | return base64Hash;
114 | } catch (NoSuchAlgorithmException e) {
115 | Log.e(TAG, "hash:NoSuchAlgorithm", e);
116 | }
117 | return null;
118 | }
119 |
120 | @Override
121 | public void onDestroy() {
122 | try {
123 | if (mMessageReceiver != null) {
124 | this.cordova.getActivity().getApplicationContext().unregisterReceiver(mMessageReceiver);
125 | mMessageReceiver = null;
126 | }
127 | } catch(IllegalArgumentException e) {
128 | e.printStackTrace();
129 | }
130 | }
131 |
132 | @Override
133 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
134 | this.callbackContext = callbackContext;
135 |
136 | LOG.v(TAG, "Executing action: " + action);
137 | //Toast.makeText(this.cordova.getActivity().getApplicationContext(),"Executing action: " + action, Toast.LENGTH_SHORT).show();
138 |
139 | if ("start".equals(action)) {
140 | // Starts SmsRetriever, which waits for ONE matching SMS message until timeout
141 | // (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
142 | // action SmsRetriever#SMS_RETRIEVED_ACTION.
143 | Task task = smsRetrieverClient.startSmsRetriever();
144 |
145 | // Listen for success/failure of the start Task. If in a background thread, this
146 | // can be made blocking using Tasks.await(task, [timeout]);
147 | task.addOnSuccessListener(new OnSuccessListener() {
148 | @Override
149 | public void onSuccess(Void aVoid) {
150 | // Successfully started retriever, expect broadcast intent
151 | LOG.v(TAG, "Executing action: addOnSuccessListener");
152 | //Toast.makeText(cordova.getActivity().getApplicationContext(),"Executing action: addOnSuccessListener", Toast.LENGTH_SHORT).show();
153 |
154 | IntentFilter intentFilter = new IntentFilter();
155 | intentFilter.addAction(SmsRetriever.SMS_RETRIEVED_ACTION);
156 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
157 | cordova.getActivity().getApplicationContext().registerReceiver(mMessageReceiver, intentFilter, RECEIVER_EXPORTED);
158 | } else {
159 | cordova.getActivity().getApplicationContext().registerReceiver(mMessageReceiver, intentFilter);
160 | }
161 |
162 | }
163 | });
164 |
165 | task.addOnFailureListener(new OnFailureListener() {
166 | @Override
167 | public void onFailure(Exception e) {
168 | // Failed to start retriever, inspect Exception for more details
169 | LOG.v(TAG, "Executing action: addOnFailureListener");
170 | //Toast.makeText(cordova.getActivity().getApplicationContext(),"Executing action: addOnFailureListener", Toast.LENGTH_SHORT).show();
171 | }
172 | });
173 |
174 | PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
175 | r.setKeepCallback(true);
176 | callbackContext.sendPluginResult(r);
177 |
178 | return true;
179 |
180 | }
181 | else if ("hash".equals(action)) {
182 |
183 | ArrayList strHashCodes = getAppSignatures();
184 |
185 | if(strHashCodes.size() == 0 || strHashCodes.get(0) == null){
186 |
187 | String err = "Unable to find package to obtain hash code of application";
188 | PluginResult result = new PluginResult(PluginResult.Status.ERROR, err);
189 | callbackContext.sendPluginResult(result);
190 |
191 | } else {
192 |
193 | String strApplicationHash = strHashCodes.get(0);
194 | PluginResult result = new PluginResult(PluginResult.Status.OK, strApplicationHash);
195 | callbackContext.sendPluginResult(result);
196 |
197 | }
198 | }
199 |
200 | // Returning false results in a "MethodNotFound" error.
201 | return false;
202 | }
203 |
204 | // Our handler for received Intents. This will be called whenever an Intent
205 | // with an action named "custom-event-name" is broadcasted.
206 | private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
207 | @Override
208 | public void onReceive(Context context, Intent intent) {
209 |
210 | if (intent.getAction().equals(SmsRetriever.SMS_RETRIEVED_ACTION)) {
211 | final Bundle extra = intent.getExtras();
212 | if (extra != null && extra.containsKey(SmsRetriever.EXTRA_STATUS)) {
213 | Status status = (Status) extra.get(SmsRetriever.EXTRA_STATUS);
214 | switch (status.getStatusCode()) {
215 | case CommonStatusCodes.SUCCESS:
216 | final String message = extra.getString(SmsRetriever.EXTRA_SMS_MESSAGE);
217 | //if (!StringUtils.hasContent(message)) return;
218 | if(message == null) return;
219 |
220 | Log.d(TAG, message);
221 |
222 | data = new JSONObject();
223 | try {
224 | data.put("Message",message);
225 | } catch(JSONException e) {}
226 |
227 | //Toast.makeText(cordova.getActivity().getApplicationContext(),"Message: "+ message, Toast.LENGTH_LONG).show();
228 | PluginResult result = new PluginResult(PluginResult.Status.OK, data);
229 | callbackContext.sendPluginResult(result);
230 |
231 | break;
232 | case CommonStatusCodes.TIMEOUT:
233 |
234 | PluginResult resultTimeout = new PluginResult(PluginResult.Status.ERROR, "TIMEOUT");
235 | callbackContext.sendPluginResult(resultTimeout);
236 | break;
237 | }
238 | }
239 | }
240 | };
241 | };
242 | }
243 |
--------------------------------------------------------------------------------