I searched a lot but didn't find the solution to the following issue.
Context :
I use an AsyncHttpResponseHandler object to handle all my webservice
responses
If I get a particular error code from the WS, I want to show an alert dialog (whatever the activity currently displayed)
I came to think that using the LocalBroadcastManager would be a good solution as the HTTP handler is not aware of the currently displayed activity
Problem :
After implementation of all what seems to be needed to make it work, my intent sent from the async handler is not received.
Additional note :
the ApplicationContext is stored in my StaticItems
class which contains all the static data I need in my app.It is setup via a custom class which inherits from Application
If I broadcast the intent from an activity, the OnReceive event is triggered
I thank you in advance for any help you could provide.
Cheers !
Here are some pieces of my code :
The code in the http handler
public class AsyncResponseHandler extends AsyncHttpResponseHandler {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
if(response != null && response.length > 0) {
CrvData data = JsonHelper.getCrvData(new String(response));
String code = data.getErrorCode();
if (!TextUtils.isEmpty(code) && code.equals(StaticItems.C_WS_OBSOLETE_VERSION_ERROR)) {
Intent intent = new Intent();
intent.setAction(StaticItems.BROADCAST_INTENT_MESSAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse(data.getValue()));
LocalBroadcastManager.getInstance(StaticItems.applicationContext).sendBroadcast(intent);
}
}
}
The code of the root Activity I use for all my activities :
public class MainActivity extends Activity {
/**
* The local broadcast receiver
*/
protected MyBroadcastReceiver mMessageReceiver = new MyBroadcastReceiver();
#Override
protected void onResume() {
super.onResume();
// register
IntentFilter filter = new IntentFilter();
filter.addCategory(Intent.CATEGORY_DEFAULT);
filter.addAction(StaticItems.BROADCAST_INTENT_MESSAGE);
LocalBroadcastManager.getInstance(StaticItems.applicationContext).registerReceiver(mMessageReceiver, filter);
}
#Override
protected void onPause() {
super.onPause();
// Unregister
LocalBroadcastManager.getInstance(StaticItems.applicationContext).unregisterReceiver(mMessageReceiver);
}
}
The receiver
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setInverseBackgroundForced(true)
.setNegativeButton(context.getResources().getString(R.string.dlg_no), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setIcon(R.drawable.ic_dialog_alert_holo_light)
.setMessage(R.string.dlg_app_is_obsolete)
.setPositiveButton(context.getResources().getString(R.string.dlg_yes), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
context.startActivity(intent);
}
});
builder.show();
}
}
I had a similar problem, for some reason you can not setData(..) on Intent returned via LocalBroadcastManager, when I placed my URI in the extra data, it started to work.... possibly a bug?
My guess is that the context you are using is not an activity context and thus can't be used for creating dialogs.
Namely, the context that is passed to onReceive in this case should be your application context, StaticItems.applicationContext, which is not good for showing dialogs.
If this is the case, you should somehow rearrange your code to make sure to pass an activity context rather than the application one.
Remove addCategory() from your IntentFilter
was solved here
OK so... I figured out that the intent was not received because of this line :
intent.setData(Uri.parse(data.getValue()));
And as #mvai pointed, the dialog cannot be displayed from the application context. I managed to get the current activity by having a reference to it in my application class. (I checked out to be sure for not having any memory leak)
Related
From my main activity, I need to call an inner class and in a method within the class, I need to show AlertDialog. After dismissing it, when the OK button is pressed, forward to Google Play for purchase.
Things work perfectly for most of the times, but for few users it is crashing on builder.show() and I can see "android.view.WindowManager$BadTokenException: Unable to add window" from crash log. Please suggest.
My code is pretty much like this:
public class classname1 extends Activity{
public void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.<view>);
//call the <className1> class to execute
}
private class classNamename2 extends AsyncTask<String, Void, String>{
protected String doInBackground(String... params) {}
protected void onPostExecute(String result){
if(page.contains("error"))
{
AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
builder.setCancelable(true);
builder.setMessage("");
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
if(!<condition>)
{
try
{
String pl = "";
mHelper.<flow>(<class>.this, SKU, RC_REQUEST,
<listener>, pl);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
});
builder.show();
}
}
}
}
I have also seen the error in another alert where I am not forwarding to any other activity. It's simple like this:
AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
builder.setCancelable(true);
//if successful
builder.setMessage(" ");
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
// dialog.dismiss();
}
});
builder.show();
}
android.view.WindowManager$BadTokenException: Unable to add window"
Problem :
This exception occurs when the app is trying to notify the user from
the background thread (AsyncTask) by opening a Dialog.
If you are trying to modify the UI from background thread (usually
from onPostExecute() of AsyncTask) and if the activity enters
finishing stage i.e.) explicitly calling finish(), user pressing home
or back button or activity clean up made by Android then you get this
error.
Reason :
The reason for this exception is that, as the exception message says,
the activity has finished but you are trying to display a dialog with
a context of the finished activity. Since there is no window for the
dialog to display the android runtime throws this exception.
Solution:
Use isFinishing() method which is called by Android to check whether
this activity is in the process of finishing: be it explicit finish()
call or activity clean up made by Android. By using this method it is
very easy to avoid opening dialog from background thread when activity
is finishing.
Also maintain a weak reference for the activity (and not a strong
reference so that activity can be destroyed once not needed) and check
if the activity is not finishing before performing any UI using this
activity reference (i.e. showing a dialog).
eg.
private class chkSubscription extends AsyncTask<String, Void, String>{
private final WeakReference<login> loginActivityWeakRef;
public chkSubscription (login loginActivity) {
super();
this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
}
protected String doInBackground(String... params) {
//web service call
}
protected void onPostExecute(String result) {
if(page.contains("error")) //when not subscribed
{
if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
builder.setCancelable(true);
builder.setMessage(sucObject);
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
}
});
builder.show();
}
}
}
}
Update :
Window Tokens:
As its name implies, a window token is a special type of Binder token
that the window manager uses to uniquely identify a window in the
system. Window tokens are important for security because they make it
impossible for malicious applications to draw on top of the windows of
other applications. The window manager protects against this by
requiring applications to pass their application's window token as
part of each request to add or remove a window. If the tokens don't
match, the window manager rejects the request and throws a
BadTokenException. Without window tokens, this necessary
identification step wouldn't be possible and the window manager
wouldn't be able to protect itself from malicious applications.
A real-world scenario:
When an application starts up for the first time,
the ActivityManagerService creates a special kind of window token
called an application window token, which uniquely identifies the
application's top-level container window. The activity manager gives
this token to both the application and the window manager, and the
application sends the token to the window manager each time it wants
to add a new window to the screen. This ensures secure interaction
between the application and the window manager (by making it
impossible to add windows on top of other applications), and also
makes it easy for the activity manager to make direct requests to the
window manager.
I had dialog showing function:
void showDialog(){
new AlertDialog.Builder(MyActivity.this)
...
.show();
}
I was getting this error and i just had to check isFinishing() before calling this dialog showing function.
if(!isFinishing())
showDialog();
The possible reason is the context of the alert dialog. You may be finished that activity so its trying to open in that context but which is already closed.
Try changing the context of that dialog to you first activity beacause it won't be finished till the end.
e.g
rather than this.
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
try to use
AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
first you cannot extend AsyncTask without override doInBackground
second try to create AlterDailog from the builder then call show().
private boolean visible = false;
class chkSubscription extends AsyncTask<String, Void, String>
{
protected void onPostExecute(String result)
{
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setCancelable(true);
builder.setMessage(sucObject);
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton)
{
dialog.dismiss();
}
});
AlertDialog myAlertDialog = builder.create();
if(visible) myAlertDialog.show();
}
#Override
protected String doInBackground(String... arg0)
{
// TODO Auto-generated method stub
return null;
}
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
visible = true;
}
#Override
protected void onStop()
{
visible = false;
super.onStop();
}
I am creating Dialog in onCreate and using it with show and hide. For me the root cause was not dismissing onBackPressed, which was finishing the Home activity.
#Override
public void onBackPressed() {
new AlertDialog.Builder(this)
.setTitle("Really Exit?")
.setMessage("Are you sure you want to exit?")
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
Home.this.finish();
return;
}
}).create().show();
I was finishing the Home Activity onBackPressed without closing / dismissing my dialogs.
When I dismissed my dialogs the crash disappeared.
new AlertDialog.Builder(this)
.setTitle("Really Exit?")
.setMessage("Are you sure you want to exit?")
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
networkErrorDialog.dismiss() ;
homeLocationErrorDialog.dismiss() ;
currentLocationErrorDialog.dismiss() ;
Home.this.finish();
return;
}
}).create().show();
I try this it solved.
AlertDialog.Builder builder = new AlertDialog.Builder(
this);
builder.setCancelable(true);
builder.setTitle("Opss!!");
builder.setMessage("You Don't have anough coins to withdraw. ");
builder.setMessage("Please read the Withdraw rules.");
builder.setInverseBackgroundForced(true);
builder.setPositiveButton("OK",
(dialog, which) -> dialog.dismiss());
builder.create().show();
In my case I refactored code and put the creation of the Dialog in a separate class. I only handed over the clicked View because a View contains a context object already. This led to the same error message although all ran on the MainThread.
I then switched to handing over the Activity as well and used its context in the dialog creation
-> Everything works now.
fun showDialogToDeletePhoto(baseActivity: BaseActivity, clickedParent: View, deletePhotoClickedListener: DeletePhotoClickedListener) {
val dialog = AlertDialog.Builder(baseActivity) // <-- here
.setTitle(baseActivity.getString(R.string.alert_delete_picture_dialog_title))
...
}
I , can't format the code snippet properly, sorry :(
I got this error, but mine was coming from the Toasts, not a Dialog.
I have Activity and Fragments in my layout. Code for the Toast was in the Activity class. Fragments gets loaded before the Activity.
I think the Toast code was hit before the Context/Activity finished initializing. I think it was the getApplicationContext() in the command Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();
Try this :
public class <class> extends Activity{
private AlertDialog.Builder builder;
public void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.<view>);
builder = new AlertDialog.Builder(<class>.this);
builder.setCancelable(true);
builder.setMessage(<message>);
builder.setInverseBackgroundForced(true);
//call the <className> class to execute
}
private class <className> extends AsyncTask<String, Void, String>{
protected String doInBackground(String... params) {
}
protected void onPostExecute(String result){
if(page.contains("error")) //when not subscribed
{
if(builder!=null){
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
if(!<condition>)
{
try
{
String pl = "";
mHelper.<flow>(<class>.this, SKU, RC_REQUEST,
<listener>, pl);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
});
builder.show();
}
}
}
}
with this globals variables idea,
I saved MainActivity instance in onCreate();
Android global variable
public class ApplicationController extends Application {
public static MainActivity this_MainActivity;
}
and Open dialog like this. it worked.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Global Var
globals = (ApplicationController) this.getApplication();
globals.this_MainActivity = this;
}
and in a thread, I open dialog like this.
AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
Open MainActivity
Start a thread.
Open dialog from thread -> work.
Click "Back button" ( onCreate will be called and remove first MainActivity)
New MainActivity will start. ( and save it's instance to globals )
Open dialog from first thread --> it will open and work.
: )
How to get result of background process in any Activity?
I'm working with OTA update. App handle that in background with handler. When update is done I have to show alert dialog to the user. I can show it in SettingsActivity where I start with OTA but user can be in any other Activity.
private void checkIsUpdated() {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mDevice.getDevice().read(GattUUID.LS_DEVICE_INFORMATION, GattUUID.LC_FIRMWARE, new
BleDevice.ReadWriteListener() {
#Override
public void onEvent(ReadWriteEvent e) {
if (e.wasSuccess()) {
if (firmwareVersion.equals(e.data_string())) {
showAlertDialog("OTA update failed", "Device is not updated");
} else {
showAlertDialog("OTA update is successful.\nDevice is updated to new " +
"firmware!", "Device is updated");
}
Log.i("OTA", "Read firmware is new success");
} else {
Log.e("OTA", "Read firmware is new success");
}
}
});
}
}, 30000);
}
AlertDialog code
private void showAlertDialog(String message, String title) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.SwipeDialogLight);
builder.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
if (!alert.isShowing()) {
alert.show();
}
}
As #W4R10CK stated, I thought that too. The BroadcastReceiver might not a very good idea. But later, I gave a thought about it and if you call checkIsUpdated function inside a Service and send the Broadcast from it, it might be a feasible solution.
So basically you need a BaseActivity class and in which you need to have a BroadcastReceiver. You need to register it onResume and again unregister it onPause.
And you need to have an abstract method to be overriden in each of your Activity too.
So your BaseActivity may look like this..
public abstract class BaseActivity extends AppCompatActivity {
private final Context context;
public BaseActivity() {
this.context = setContext();
}
protected abstract Context setContext();
#Override
public void onResume() {
super.onResume();
registerBroadcastReceiver();
}
#Override
public void onPause() {
super.onPause();
unRegisterBroadcastReceiver();
}
// Declare your BroadcastReceiver here
private class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
showAlertDialog(context);
}
}
}
As #rogerwar suggested in the comments, the correct approach is a broadcast receiver.
Vogella has a nice tutorial
Since you want to have it in all activities, you can make a base class for all your activities and in this base class you can register the receiver in the onStart and unregister it in the onStop.
I'm using the firebase messaging service for messaging and notifications. I can't seem to be able to pass an incoming message from the service to the adapter so that when the message is received it can be inserted into the RecycleView List.
I tried using BroacastIntent as follows :
public class messaging extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage m) {
store(m.getData());
broadcastIntent();
}
public void broadcastIntent() {
Intent intent = new Intent();
intent.setAction("com.myApp.CUSTOM_EVENT");
sendBroadcast(intent);
}
}
and in the Adpter
public class ConvoAdapter extends RecyclerView.Adapter<ConvoHolder> {
private List<Message> list;
private Activity A;
public ConvoAdapter(List<Message> data) {
}
#Override
public ConvoHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
return new ConvoHolder(v);
}
#Override
public void onBindViewHolder(ConvoHolder h, int Position) {
final Message M = list.get(Position);
h.config(A, M);
}
#Override
public int getItemCount() {
return list.size();
}
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
}
}
}
And manifest.
<receiver android:name=".fragments.chats.ConvoAdapter$MyReceiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.CUSTOM_EVENT">
</action>
</intent-filter>
</receiver>
As is, the broadcast receiver is not receiving any messages.
I'd also use any other method that doesn't involve using broadcast receivers.
The flow or architecture of your is not a standard practice.
The standard flow should be
Firebase service
Some activity or fragment with BroadcastReceiver using LocalBroadcastManager
1. Firebase Service
public class messaging extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage m) {
store(m.getData());
broadcastIntent();
}
public void broadcastIntent() {
Intent intent = new Intent();
intent.setAction("com.myApp.CUSTOM_EVENT");
// We should use LocalBroadcastManager when we want INTRA app
// communication
LocalBroadcastManager.getInstance(YOUR_CONTEXT).sendBroadcast(intent);
}
}
2. Activity
Registering Receiver for broadcast from Service
public void onCreate(Bundle savedInstance) {
// REST OF YOUR CODE
IntentFilter if= new IntentFilter("com.myApp.CUSTOM_EVENT");
LocalBroadcastManager.getInstance(this).registerReceiver(onMessage, if);
}
Writing the Receiver in Activity
private BroadcastReceiver onNotice= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Update your RecyclerView here using notifyItemInserted(position);
}};
Summary: The Service sends local broadcast to Activity which in turn receives it and updates or add items using RecyclerView instance
if in case you don't want to use BroadCaseReceiver for your task.Follow below steps:
There are two things to note here.
Firstly,Check whether the Activity is open(Activel visible) or not.
i. When your screen is active.(screen is visible).
1.In your onMessageReceived() store the received message in SQLiteDataBase with time.
2.create a method in your adapter which will update the screen by fetching the data from SQLiteDB.
3.Now whenever a new message is received call this method in Adapter class.
ii.in case the screen is not active:
1.store the received message in sqliteDb and show it as a notification.
note:
1.make sure you write all these in a try catch block.
2.make sure to sync your SQLiteDB on opening the screen for the first time.
3.in case if this does not help you please try this way.. Refreshing activity on receiving gcm push notification
I think the above code didnt worked because the action name above given and the one registered in manifest is different
<action android:name="com.myApp.CUSTOM_EVENT">
</action>
intent.setAction("com.myApp.CUSTOM_EVENT");
give same name i think the above code will work
Right now i'm having :-
1) 1 activity which is the main activity that extends from AppCompactActivity.
2) 1 fragment class that extends from fragment, this is the fragment that being called from main activity (1) - ProfileTeacherActivity.java
3) 1 fragment class that extends from DialogFragment, this dialog getting called from fragment (2) - ModalBox.java
So, basically, this is just a simple flow of execution. At start, the applications showing the main activity (1) having drawer that have a few links as example a profile link, when click this link, the application call the fragment (2) showing details of profile with one edit button. After clicking edit button, the applications will invoke DialogFragment (3) that contains some of EditText for editing user's profile.
What i want to achieve is, after editing user's profile and successful saved into database, i tried to send user's data back to fragment (2) just to show latest updated info, unfortunately it didn't work.
Here is what i'm tried :
1) Creating Interface inside DialogFragment (3) - ModalBox.java
public class ModalBox extends DialogFragment{
....
public interface EditProfileModalBoxInterface {
void onFinishEditProfile( HashMap<String, String> dataPassing );
}
...
...
}
2) Inside DialogFragment also i have .setPositiveButton function for OK button. - ModalBox.java
public class ModalBox extends DialogFragment{
...
...
public Dialog onCreateDialog(Bundle savedInstanceState ) {
...
builder
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, int id) {
// At here i'm using retrofit2 http library
// to do updating stuff
// and inside success's callback of retrofit2(asynchronous)
// here i call the below function to send data
// dataToSend is a HashMap value
sendBackResultToParent( dataTosend );
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
.....
}
// Function called inside success's callback of retrofit2
public void sendBackResultToParent( HashMap<String, String> data ) {
// instantiated interface
EditProfileModalBoxInterface ls=(EditProfileModalBoxInterface)getTargetFragment();
// declaring interface's method
ls.onFinishEditProfile( data );
}
}
3) Finally, i'm implements those interface inside fragment (2) - ProfileTeacherActivity.java
public class ProfileTeacherActivity extends Fragment
implements ModalBox.EditProfileModalBoxInterface{
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState ) {
.....
.....
}
// At here the interface's method did't triggered
#Override
public void onFinishEditProfile( HashMap dataPassedFromDialog ) {
Toast.makeText(getActivity(), "Testing...." , Toast.LENGTH_LONG).show();
}
}
What i'm confuses right now is, the problem happens only when i called this function sendBackResultToParent( dataTosend ); inside retrofit2 success's callback, it does triggered when calling outside of it. I'm assumed the async called caused this. If i could use Promise or something like that or is there any workaround on this?
The following existing solutions didn't work in my case :
Callback to a Fragment from a DialogFragment
How to send data from DialogFragment to a Fragment?
Send Data from DialogFragment to Fragment
Ask me for more inputs if above use case didn't clear enough or misunderstanding. Thanks for the helps. Regards.
This is a sample DialogFragment code used to send message to selected contact. I too required to capture the click event on the DialogFragment and redirect.
Ideally to achieve this , this is what needed to be done
Override the positive/negative button clicks of AlertDialog.Builder and do no action
After this , using getButton method mention AlertDialog.BUTTON_NEGATIVE or AlertDialog.BUTTON_POSITIVE and assign an action
public class SMSDialogFrag extends DialogFragment {
private static String one="one";
private EditText messageContent;
private AlertDialog dialog;
private String mobNumber;
public static SMSDialogFrag showDialog(String mobNumber){
SMSDialogFrag customDialogFrag=new SMSDialogFrag();
Bundle bundle=new Bundle();
bundle.putString(one, mobNumber);
customDialogFrag.setArguments(bundle);
return customDialogFrag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity());
View view = getActivity().getLayoutInflater().inflate(R.layout.sms_dialog, null);
alertDialogBuilder.setView(view);
setupUI(view);
alertDialogBuilder.setTitle("");
alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Do nothing here because we override this button later
}
});
alertDialogBuilder.setPositiveButton("Send", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Do nothing here because we override this button later
}
});
dialog = alertDialogBuilder.create();
dialog.show();
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialog.dismiss();
//else dialog stays open. Make sure you have an obvious way to close the dialog especially if you set cancellable to false.
}
});
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMessage();//IN YOUR USE CASE YOU CAN REDIRECT TO YOUR CALLER FRAGMENT
}
});
return dialog;
}
void setupUI(View view){
TextView textViewMob=(TextView)view.findViewById(R.id.mobNumber);
messageContent=(EditText)view.findViewById(R.id.messageContent);
mobNumber=getArguments().getString(one);
textViewMob.setText("Send message to : "+mobNumber);
}
void sendMessage(){
if( ! TextUtils.isEmpty(messageContent.getText())){
try {
SmsManager smsManager = SmsManager.getDefault();
Log.v(Constants.UI_LOG,"Number >>>>>>> "+mobNumber);
smsManager.sendTextMessage(mobNumber, null, messageContent.getText().toString(), null, null);
} catch (Exception e) {
e.printStackTrace();
}
Toast.makeText(getActivity(),"Message Sent!",Toast.LENGTH_SHORT).show();
dialog.dismiss();
}else{
Toast.makeText(getActivity(),"Please enter message to send!",Toast.LENGTH_SHORT).show();
}
}
}
Consider using eventBus, for example
Otto
The usage is very simple. All you need to do is create an evenbus:
public static Bus bus = new Bus(ThreadEnforcer.MAIN); //use Dagger2 to avoid static
Then create a receiver method (in fragment 2 in your case):
#Subscribe
public void getMessage(String s) {
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}
Send you message by calling(from DialigFramgent):
bus.post("Hello");
And don't forget to register your eventBus inside onCreate method(of your Fragment):
bus.register(this);
And that is!
From architectural standpoint, 2 fragments should not directly communicate with one another. Container Activity should be responsible for passing data between it's child fragments. So here's how i would do it:
Implement your interface in the container Activity and just attach your interface implementation in the Activity to the Dialog class and call that interface method when required. Something like this :
public static class ModalBox extends DialogFragment {
EditProfileModalBoxInterface mListener;
// Container Activity must implement this interface
public interface EditProfileModalBoxInterface {
void onFinishEditProfile( HashMap<String, String> dataPassing );
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (EditProfileModalBoxInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement EditProfileModalBoxInterface");
}
}
}
Then call mListener.onFinishEditProfile(....) where ever it's required in the DialogFragment class.
This way you will receive the result back in your Activity class from where you can call your desired fragment's relevant method to pass the results to that fragment.
This whole flow has been described here
Finally the culprit founds. All the answers mentioned above were right. And my script also actually works, the problem is related with my API json's response that did't coming with right structure. In my case, i'm using retrofit2 with GSON converter for parsing into POJO. Found the info on log saying about :
Expected BEGIN_OBJECT but was BEGIN_ARRAY
Means that, GSON expecting the json object, which is my API was returned JSON array. Just change the API's structure then all goods to go. Thanks guys for your hard time. Really appreciated, as i'm right now knowing how to deal with eventBus, replacing default OK and Cancel button and the correct way for communicating between fragments.
I have a service
public class GcmIntentService extends IntentService{...}
that manages the notifications.
When a notification arrives if the user is out of the application, with a tap on the notification the main activity is resumed and updated
public class MainActivity extends Activity {
...
lv = (ListView) findViewById(R.id.lv);
adapter = new Adapter(this, item);
lv .setAdapter(adapter);
...
}
but how can I update the activity if the user is already on it?
You have to use BroadcastReciever for this task:http://developer.android.com/reference/android/content/BroadcastReceiver.html
in Activity:
public class MainActivity extends Activity {
public static final String NOTIFY_ACTIVITY_ACTION = "notify_activity";
private BroadcastReciver broadcastReciver;
#Override
protected void onStart() {
super.onStart();
broadcastReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction.equals(NOTIFY_ACTIVITY_ACTION ))
{
//to do smth
}
}
}
IntentFilter filter = new IntentFilter( NOTIFY_ACTIVITY_ACTION );
registerReceiver(broadcastReciver, filter);
}
#Override
protected void onStop()
{
unregisterReceiver(broadcastReciver);
}
}
In Service:
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(MainActivity.NOTIFY_ACTIVITY_ACTION );
broadcastIntent.putExtra("addtional_param", 1);
broadcastIntent.putExtra("addtional_param2", 2); //etc
sendBroadcast(broadcastIntent);
UPDATE
BTW It's better use LocalBroadcastManager for send broadcast inside the app. It uses the same way as normal broadcast, but first you create LocalBroadcastManager:
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(MainActivity.this);
and in the onStart:
manager.registerReciever(broadcastReciver, filter);
and in the onStop:
manager.unregisterBroadcast(broadcastReciver);
and in the service:
manager.sendBroadcast(broadcastIntent);
if you are using the list and you would like to reload the activity just to refresh the list then, I would suggest you to use following methods on your adatper as soon as some message arrives. This would refresh the content in the list, and list will be updated automatically (without restarting of activity)
adapter.notifyDataSetChanged()
This method notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.
It's not a good option to reload the activity, but if there is no other solution rather than recreating the activity i would suggest to refresh the view itself.
Hope this helps.