Folks!
I'm trying to implement BroadcastReceiver for listening to incoming Message using SMSRetrieverAPI.
I'm getting messages in Broadcast's onReceive method but after that, I have to pass that Message String in activity without restarting it, So I have implemented interface.
I have tried below code but, it throws NullPointerException for mOTPReceiveListener. Something didn't work perfectly so please correct me.
SMSBroadcastReceiver is like below
public class SMSBroadcastReceiver extends BroadcastReceiver {
OTPReceiveListener mOTPReceiveListener = null;
public void InitOTPReceiveListener(OTPReceiveListener otpreceivelistener){
this.mOTPReceiveListener = otpreceivelistener;
}
#Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status status = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch(status.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get SMS message contents
String message = (String) extras.get(SmsRetriever.EXTRA_SMS_MESSAGE);
Log.e("VERIFY","Full Message : "+message);
if (mOTPReceiveListener != null) {
String[] temp = message.split("-");
String[] msg = temp[1].split(" ");
message = msg[0];
Log.e("VERIFY","message : "+message);
mOTPReceiveListener.onOTPReceived(message);
}
break;
case CommonStatusCodes.TIMEOUT:
// Waiting for SMS timed out (5 minutes)
mOTPReceiveListener.onOTPTimeOut();
break;
}
}
}
public interface OTPReceiveListener {
void onOTPReceived(String otp);
void onOTPTimeOut();
}
}
and the activity code
public class VerificationActivity extends BaseActivity implements SMSBroadcastReceiver.OTPReceiveListener
{
Context mContext;
Button btn_verify_submit;
TextView text_resend_otp;
private String TAG = "VERIFY";
private String PHONE = null;
private SmsRetrieverClient mSmsRetrieverClient;
private SMSBroadcastReceiver mSMSBroadcastReceiver;
SMSBroadcastReceiver.OTPReceiveListener mOTPReceiveListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verification);
initView();
setClickListener();
PHONE = getIntent().getStringExtra("phone");
Log.e(TAG,"phone : "+PHONE);
mSmsRetrieverClient = SmsRetriever.getClient(this);
startVerify();
mOTPReceiveListener = this;
mSMSBroadcastReceiver = new SMSBroadcastReceiver();
mSMSBroadcastReceiver.InitOTPReceiveListener(this);
}
private void initView()
{
layout_verification_main = findViewById(R.id.layout_verification_main);
overrideFonts(layout_verification_main,VerificationActivity.this);
edt_verify_code = findViewById(R.id.edt_verify_code);
btn_verify_submit = findViewById(R.id.btn_verify_submit);
text_resend_otp = findViewById(R.id.text_resend_otp);
}
private void setClickListener()
{
btn_verify_submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Call OTP Match API
}
});
text_resend_otp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Call Resend OTP API
}
});
}
private void startVerify() {
Task<Void> task = mSmsRetrieverClient.startSmsRetriever();
task.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.e(TAG, "SmsRetrievalResult start onSuccess.");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "SmsRetrievalResult start failed.", e);
}
});
}
#Override
public void onOTPReceived(String otp) {
edt_verify_code.setText("" + otp);
Toast.makeText(mContext, "Message OTP : " + otp, Toast.LENGTH_SHORT).show();
Log.e("VERIFY","otp in activity : "+otp);
}
#Override
public void onOTPTimeOut() {
}
}
and I have registered in Manifest like this
<receiver android:name=".broadcast.SMSBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="com.google.android.gms.auth.api.phone.SMS_RETRIEVED"/>
</intent-filter>
</receiver>
Am I missing something?
The SMSBroadcastReceiver instance created from the definition in your manifest is NOT the same one you created in VerificationActivity.onCreate().
You can register the broadcast receiver from your activity using Context.registerReceiver().
To contact between activity and receiver what you are try is wrong as your listener is dependent on activities onCreate() i.e on lifecycle of Activity.
So to solve this you need to follow below step's:
Solution
Be sure that activity is alive by starting the activity from your receiver (startActitvity() using intent ) again. check this thread if you don't want to restart the activity again and want to be sure that activity is on screen.
Send a broadcast from your receiver to activity read this thread. No need of listener as activity can directly listen to message
you can combine both solution's for you own use case it will work.
Related
I want to pass data from an activity to a fragment via BroadcastReceiver. But I cant get the data in the fragment,cause the onReceive() there not fire up.
In my AppConfig:
public static final String CREATE_POST = "created_post";
In activity A implement all this stuff :
StringRequest request = new StringRequest(Request.Method.POST, AppConfig.MYURL, new Response.Listener<String>(){
#Override
public void onResponse(String response) {
Intent intent = new Intent(AppConfig.CREATE_POST);
intent.putExtra("myId",myId);
intent.putExtra("userId",userId);
intent.putExtra("username",username);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
In the fragment with should be receive the data I already implement this :
private BroadcastReceiver broadcasterReceiver;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//..other code here
broadcasterReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(AppConfig.CREATE_POST)){
Log.d("Broadcast","get called")
//HERE I get the intent here
}
}
}
//I already register the boardcast in onResume() and onPause()
#Override
public void onResume() {
super.onResume();
//register the broadcaster
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcasterReceiver,
new IntentFilter(AppConfig.CREATE_POST));
}
#Override
public void onPause() {
super.onPause();
//unregister the broadcaster
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcasterReceiver);
}
What I still missing out? In order to make this work.
First of all, create an interface anywhere in your package. For eg -
public interface SyncDataListener {
void refreshDashboard(String myId, String username, String userId);
}
Then in your Activity, create a global declaration and setter/resetter methods like -
private SyncDataListener syncDataListener;
public void setSyncDataListener(SyncDataListener syncDataListener) {
this.syncDataListener = syncDataListener;
}
public void resetSyncDataListener(){
syncDataListener = null;
}
Next in your Fragment implement the above interface and override the method like -
public class DashboardFragment extends Fragment implements SyncDataListener {
#Override
public void refreshDashboard(String myId, String username, String userId) {
//Your code that deals with the data received from activity
}
}
Also in the Fragment's onAttach(Context context) method call the setter method created in the activity like -
#Override
public void onAttach(Context context) {
super.onAttach(context);
((MainActivity) getActivity()).setSyncDataListener(this);
}
Also make sure you reset the listener instance when your Fragment gets destroyed like -
#Override
public void onDestroyView() {
super.onDestroyView();
((MainActivity) getActivity()).resetSyncDataListener();
}
Now whenever you need to send data from Activity to Fragment you can call -
if (syncDataListener != null) {
syncDataListener.refreshDashboard(myId, username, userId);
}
Use this code , this is work in my side :
private BroadcastReceiver broadcasterReceiver;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//..other code here
broadcasterReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(AppConfig.CREATE_POST)){
Log.d("Broadcast","get called")
//HERE I get the intent here
}
}
}
//I already register the boardcast in onResume() and onPause()
#Override
public void onResume() {
super.onResume();
//register the broadcaster
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcasterReceiver,
new IntentFilter(AppConfig.CREATE_POST));
}
#Override
public void onPause() {
super.onPause();
//unregister the broadcaster
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcasterReceiver);
}
use this code to register the broadcast
I have implemented an otto bus example. It works fine, but ONLY on the second time I visit the activity.
For example, when I load the app and hit the secret message button I am taken to the activity but the toast does not show. Then I hit the back button to return to the MainActivity and hit the show secret message button again and when I am taken to the secret message activity the toast is displayed. I realize it works the second time because I have created a leak by not unregistering the event.
Is there something I am missing about the logic?
MainActivity:
public class MainActivity extends AppCompatActivity {
Button buttonSecretMessage;
Intent intentToMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentToMessage = new Intent(MainActivity.this, SecretMessageActivity.class);
buttonSecretMessage = (Button) findViewById(R.id.buttonSecretMessage);
buttonSecretMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EventBus.getInstance().post(new MakeMySecretMessageEvent());
startActivity(intentToMessage);
}
});
}
}
Secret Message Activity:
public class SecretMessageActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_secret_message);
}
#Subscribe
public void getMySecretMessage(MakeMySecretMessageEvent event){
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
#Override
protected void onStart(){
super.onStart();
EventBus.getInstance().register(this);
}
#Override
protected void onStop() {
super.onStop();
//EventBus.getInstance().unregister(this);
}
}
MakeMySecretMessageEvent:
public class MakeMySecretMessageEvent {
public MakeMySecretMessageEvent() {
}
public String getMessage() {
String message = "YOU ARE AWESOME!";
return message;
}
}
EventBus:
public final class EventBus extends Bus{
private static final EventBus Bus = new EventBus();
public static Bus getInstance() {
return Bus;
}
private EventBus() {
}
}
You can send sticky event using EventBus library. It allows you to send events to component which is not created yet.
You`ll find more info here.
Here EventBus has applied in wrong scenario, when you can simply send data via intent or bundle. Which is more reliable in communication with one activity with another. You will never ever receive event on first click, as event fire is instant and your activity creation will take some time accordingly.
So try to use bundle or intent to setup communication b/w to activity one after another.
Thanks to contributors I now have a better understanding of the activity life cycle and how it fits in with event bus. That is you cannot send an event from the MainActivity to its children, but the other way around instead. Below reflects how to implement an otto event bus to pass a simple object from an activity back to the main activity. Hopefully someone else can find this useful :) And if this can be improved upon please comment. Thanks.
Main Activity:
public class MainActivity extends AppCompatActivity {
Button buttonSecretMessage;
Intent intentToMessage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getInstance().register(this);
intentToMessage = new Intent(MainActivity.this, SecretMessageActivity.class);
buttonSecretMessage = (Button) findViewById(R.id.buttonSecretMessage);
buttonSecretMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(intentToMessage);
}
});
}
public MakeMySecretMessageEvent event;
#Subscribe
public void getMySecretMessage(MakeMySecretMessageEvent event) {
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
protected void onStop() {
super.onStop();
if(event != null ){
EventBus.getInstance().unregister(this);
}
}
}
SecretMessageActivity (this is where the secret message is created)
public class SecretMessageActivity extends AppCompatActivity {
Button buttonClickToMeToSeeMessage;
Intent intentToMain;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_secret_message);
intentToMain = new Intent(SecretMessageActivity.this, MainActivity.class);
buttonClickToMeToSeeMessage = (Button) findViewById(R.id.buttonClickToMeToSeeMessage);
buttonClickToMeToSeeMessage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MakeMySecretMessageEvent makeMySecretMessageEvent = new MakeMySecretMessageEvent();
EventBus.getInstance().post(makeMySecretMessageEvent);
startActivity(intentToMain);
}
});
}
}
MakeMySecretMessageEvent
public class MakeMySecretMessageEvent {
public MakeMySecretMessageEvent() {
}
public String getMessage() {
String message = "YOU ARE AWESOME!";
return message;
}
}
EventBus:
public final class EventBus extends Bus{
private static final EventBus Bus = new EventBus();
public static Bus getInstance() {
return Bus;
}
private EventBus() {
}
}
I know this question has been asked many times but still i am unable to solve my problem.I want to get the OTP from the SMS in the editText of the Activity.For this i am using broadcast receiver.
Code for broadcast receiver:
private static final String TAG = ReceiveSms.class.getSimpleName();
private SmsReceivedListner smsReceived = null;
#Override
public void onReceive(Context context, Intent intent) {
//code to get sms....
Log.e(TAG, "OTP received: " + verificationCode);
if (smsReceived != null) {
smsReceived.onSmsReceived(verificationCode);
} else {
if (Constants.isLoggingEnable) {
Logger.logError(TAG, "Sms listner is null");
}
}
}
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
public void setOnSmsReceivedListener(Context context) {
this.smsReceived = (SmsReceivedListner) context;
}
Activity Code
public class EnterOtp extends MasterActivity implements View.OnClickListener, OnTaskComplete, SmsReceivedListner {
private static final String TAG = EnterOtp.class.getSimpleName();
private Context context;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.otp);
context = this;
init();
}
private void init() {
setUpToolbar();
receiveSms = new ReceiveSms();
receiveSms.setOnSmsReceivedListener(this);
}
I have used interface but always i am getting it as null.So what can i do to get the otp.
P.S-
I dont want to start new Activity via intent because the activity is running only, so if via Intent can i pass the otp without starting new Activity and also maintaing the back stack as well?
If you want receive sms only when activity is running use this code:
private void init()
{
receiveSms = new ReceiveSms();
receiveSms.setOnSmsReceivedListener(this);
registerReceiver(receiveSms, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
}
And remove this receiver from AndroidManifest.xml
I hope it helped you.
EDIT:
In onDestroy you must use this code:
protected void onDestroy()
{
super.onDestroy();
// ...
unregisterReceiver(receiveSms);
}
I am having a "Receiver no registered exception" in OnDestroy method of my app using fragments.
I have a MainActiviy class where I check if the user registered an account.
If not account created, I load the register account class fragment to allow the user create an account.
After the user create the account clicking a button I restart the MainActivity class.
I need to register a broadcastreceiver only after the user create an account.
But, after the restart the MainActivity class from Frgamnent Class, I am getting an exception of receiver not registered in event OnDestroy of Main Activity.
Any help to solve it will be appreciated.
Thanks in Advance, Luiz
My MainActivity Class
public class MainActivity extends Activity {
// if run on phone, isSinglePane = true
// if run on tablet, isSinglePane = false
static boolean isSinglePane;
private GcmUtil gcmUtil;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View v = findViewById(R.id.phone_container);
if (!AccountRegisterCreated()){
//this fragmment register an account to user, and save in Preferences
RegisterFragment myListFragment= new RegisterFragment();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.phone_container, myListFragment);
fragmentTransaction.commit();
return;
}
registerReceiver(registrationStatusReceiver, new IntentFilter(Common.ACTION_REGISTER));
}
private BroadcastReceiver registrationStatusReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null && Common.ACTION_REGISTER.equals(intent.getAction())) {
switch (intent.getIntExtra(Common.EXTRA_STATUS, 100)) {
case Common.STATUS_SUCCESS:
getActionBar().setSubtitle("online");
break;
case Common.STATUS_FAILED:
getActionBar().setSubtitle("offline");
break;
}
}
}
};
#Override
protected void onPause() {
ContentValues values = new ContentValues(1);
super.onPause();
}
#Override
protected void onDestroy() {
unregisterReceiver(registrationStatusReceiver);
super.onDestroy();
}
private boolean AccountRegisterCreated(){
SharedPreferences prefs;
prefs= PreferenceManager.getDefaultSharedPreferences(this);
String fullname = prefs.getString(DataProvider.COL_EMAIL,"");
if (!fullname.isEmpty()) {
return true;
}
return false;
}
}
My Fragment Class:
public class RegisterFragment extends Fragment {
private static SharedPreferences prefs;
static final String TAG = "pushabout";
TextView name;
TextView email;
TextView password;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.register, null);
name = (TextView) view.findViewById(R.id.reg_fullname);
email = (TextView) view.findViewById(R.id.reg_email);
password = (TextView) view.findViewById(R.id.reg_password);
if (email.getText().toString().isEmpty()){
email.setText(Common.getPreferredEmail());
}
Button mButton = (Button) view.findViewById(R.id.btnRegister);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//checa email e reg e salva pref e registra gcm
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = prefs.edit();
editor.putString(DataProvider.COL_NAME, sname);
editor.putString(DataProvider.COL_EMAIL, semail);
editor.putString(DataProvider.COL_PWD, spassword);
editor.commit();
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
});
return view;
}
}
The problem is not with the Fragment. It's the BroadcastReceiver in your Activity.
Try declaring your BroadcastReceiver as a class field:
public class MainActivity extends Activity {
...
BroadcastReceiver mBroadcastReceiver;
...
Then, you should change how you're creating and registering the receiver. The way I've done it is by registering during the activity's onResume instead of onCreate(). If you do this, you'll also need to unregister during onPause() instead of onDestroy(). It will look something like this:
#Override
public void onResume() {
super.onResume();
// Create and register your receiver here.
if (AccountRegisterCreated) {
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
....
}
}
registerReceiver(mBroadcastReceiver, new IntentFilter(Common.ACTION_REGISTER));
}
...
}
#Override
public void onPause() {
super.onPause();
// Unregister your receiver here
if (mBroadcastReceiver != null) {
unregisterReceiver(mBroadcastReceiver);
}
}
Just declare a member variable of type boolean. When you register the BroadcastReceiver, set that variable to true. In onDestroy() only call unregisterReceiver() if the variable is true.
I'm trying to start a robospice request in Activity A and then receive the results of the request in Activity B.
In Activity A
FetchSpiceRequest fetchSpiceRequest = new FetchSpiceRequest();
spiceManager.execute(fetchSpiceRequest, new ActivityB().postListener);
The PostListener implements PendingRequestListener and is sitting within Activity B.
In Activity B
We addListener for the pending request that was started in Activity A below.
#Override
protected void onStart() {
spiceManager.start( this );
spiceManager.addListenerIfPending(Post.class, 0, new PostListener());
We then implement the PostListener in Activity B below.
public final class PostListener implements PendingRequestListener<Post> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Toast.makeText(MainActivity.this, "Exception: " + spiceException.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(Post post) {
Toast.makeText(MainActivity.this, "Added", Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestNotFound() {
Toast.makeText(MainActivity.this, "Request not found", Toast.LENGTH_SHORT).show();
}
}
Finally, we create a variable within Activity B called so that A can pick it up (eg. when I call new ActivityB().postListener):
public PostListener PostListener;
This doesn't work and it always calls onRequestNotFound in the PostListener.
What am I doing wrong?
I have also looked at this post: Robospice - keep spice service continue running when changing activity but #Snicolas doesn't seem to mention anything about how the spiceManager.execute statement should look like in the first activity.
Eventually, I had to use LocalBroadcastManager to achieve what I want to do:
Activity A
FetchSpiceRequest fetchSpiceRequest = new FetchSpiceRequest();
spiceManager.execute(fetchSpiceRequest, postListener);
Please note that postListener is situated within Activity A. It actually doesn't do much as all the work will now be performed inside the localbroadcastreceiver:
public final class AddPostListener implements RequestListener<Post> {
#Override
public void onRequestFailure(SpiceException spiceException) {
Toast.makeText(AddPostActivity.this, "Exception: " + spiceException.toString(), Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(Post post) {
Toast.makeText(AddPostActivity.this, "Added", Toast.LENGTH_SHORT).show();
Log.e("AddPostListener", "Background service finished"); }
Inside the fetchSpiceRequest, we perform the work asynchronously and once it is finished, we call the localbroadcast manager to send our results back to Activity B:
public class FetchSpiceRequest extends SpringAndroidSpiceRequest<Post> {
private Post mPost;
private Context mContext;
public AddPostSpiceRequest(Context context, Post post) {
super(Post.class);
mPost = post;
mContext = context;
}
#Override
public Post loadDataFromNetwork() throws Exception {
//Do some network activity to get a post and then after the network activity ends, send the post through the broadcast activity to Activity B
Intent intent = new Intent("my-event");
intent.putExtra("post", post);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
Activity B
Register your localBroadcast receiver:
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver,
new IntentFilter("my-event")); }
Do something with the received message:
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
Post post = intent.getParcelableExtra("post");
Log.e("receiver", "Got message: " + post.getPostheader());
}
};