Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I display a dialog in android without an Activity context?

This seems like it should be simple, but I'm not finding an answer anywhere. I have an Android application that performs network tasks in the background. If an error comes back, I want to display an error dialog. When the task returns, I don't know which Activity is in the foreground. Based on this post, it looks like we can't use the application context to display a dialog (and indeed I do get the crash if I try).

So how can I get the context of the current activity? Again, the receiver for the network task is running in the Application context, not in a particular Activity. Any other ideas?

Edit: I should clarify. I don't want to display an error dialog if I'm not the foreground application. I'm only interested in the case where our app is in the foreground for now.

like image 785
ManicBlowfish Avatar asked Mar 17 '12 15:03

ManicBlowfish


People also ask

Can we show dialog with application context?

Afaik there is no way to display a Dialog w/o being the active foreground app.

How do I show custom dialog?

This example demonstrate about how to make custom dialog in android. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml.


4 Answers

If an error comes back, I want to display an error dialog.

Please only do this if you know that the user is actively using your application. The user will be very very annoyed if you interrupt them in the middle of something else (playing a game, watching a movie, reading a book).

So how can I get the context of the current activity?

You don't. At most, you let the current activity know that it needs to do something.

Any other ideas?

One possibility is to use an ordered broadcast, so if you have a foreground activity, it gets control, otherwise you raise a Notification to let the user know about the problem without popping a dialog. The activity that receives the ordered broadcast can display an AlertDialog or otherwise let the user know about the problem. I wrote about the details of how to do this in a blog post (and a book chapter, for that matter), and here is a sample application demonstrating the technique.

Or, have the service call startActivity() to start up a dialog-themed activity.

like image 152
CommonsWare Avatar answered Oct 17 '22 03:10

CommonsWare


I've created a helper class that implements CommonsWare's idea. Activities that wish to display alerts just need to call Alerts.register() and Alerts.unregister(). Then anyone can call Alerts.displayError().

Comments welcome.

public class Alerts {

    private static class AlertReceiver extends BroadcastReceiver {

        private static HashMap<Activity, AlertReceiver> registrations;
        private Context activityContext;

        static {
            registrations = new HashMap<Activity, AlertReceiver>();
        }

        static void register(Activity activity) {
            AlertReceiver receiver = new AlertReceiver(activity);
            activity.registerReceiver(receiver, new IntentFilter(MyApplication.INTENT_DISPLAYERROR));
            registrations.put(activity, receiver);
        }

        static void unregister(Activity activity) {
            AlertReceiver receiver = registrations.get(activity);
            if(receiver != null) {
                activity.unregisterReceiver(receiver);
                registrations.remove(activity);
            }
        }

        private AlertReceiver(Activity activity) {
            activityContext = activity;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            abortBroadcast();
            String msg = intent.getStringExtra(Intent.EXTRA_TEXT);
            displayErrorInternal(activityContext, msg);
        }
    }

    public static void register(Activity activity) {
        AlertReceiver.register(activity);
    }

    public static void unregister(Activity activity) {
        AlertReceiver.unregister(activity);
    }

    public static void displayError(Context context, String msg) {
        Intent intent = new Intent(MyApplication.INTENT_DISPLAYERROR);
        intent.putExtra(Intent.EXTRA_TEXT, msg);
        context.sendOrderedBroadcast(intent, null);
    }

    private static void displayErrorInternal(Context context, String msg) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("Error")
               .setMessage(msg)
               .setCancelable(false)
               .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       dialog.cancel();
                   }
               });
        final AlertDialog alert = builder.create();

        alert.show();
    }

}
like image 27
ManicBlowfish Avatar answered Oct 17 '22 01:10

ManicBlowfish


I'm using custom created Dialog. It will be called by Service or Handler even in the case if the current Activity will lose focus.

Activity of my custom Dialog:

public class AlertDialogue extends AppCompatActivity {

    Button btnOk;
    TextView textDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE); //comment this line if you need to show Title.
        setContentView(R.layout.activity_alert_dialogue);

        textDialog = (TextView)findViewById(R.id.text_dialog) ;
        textDialog.setText("Hello, I'm the dialog text!");

        btnOk = (Button) findViewById(R.id.button_dialog);
        btnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }
}

You can call this dialog using:

Intent intent = new Intent(this, AlertDialogue.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

In the Manifest:

<activity android:name=".AlertDialogue"
    android:theme="@style/AlertDialogMy">

</activity>

style:

<resources>

    <style name="AlertDialogMy" parent="Theme.AppCompat.Light.Dialog">
        <item name="android:windowNoTitle">true</item> //delete this line if you need to show Title.
    </style>

</resources>

Here is the full code of this example.

like image 4
V.March Avatar answered Oct 17 '22 03:10

V.March


While this question is quite old, meanwhile there is a nice solution if you want it to work with any active Activity. So you wouldn't need to register individual Activities like it's proposed in other answers.

I want to emphasize that this is usually something that you should try to avoid - creating dialogs w/o knowing the view context, but there might be special circumstances where this can be useful.

With this solution you're always aware of the active activity on application level and can use the activity context to open dialogs or other activities from e.g. network code where you only have access to the application:

class YourApplication : Application() {

    private val activeActivityCallbacks = ActiveActivityLifecycleCallbacks()

    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(activeActivityCallbacks)
    }

    override fun onTerminate() {
       unregisterActivityLifecycleCallbacks(activeActivityCallbacks)
       super.onTerminate()
    }

    fun getActiveActivity(): Activity? = activeActivityCallbacks.getActiveActivity()
// ...
}


class ActiveActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {

   private var activeActivity: Activity? = null

   fun getActiveActivity(): Activity? = activeActivity

   override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
      activeActivity = activity
   }

   override fun onActivityDestroyed(activity: Activity) {
      if (activity === activeActivity) {
         activeActivity = null
      }
   }
// ...
}

From anywhere:

YourApplication.get().getActiveActivity()?.let { activity ->
    activity.runOnUiThread {
       AlertDialog.Builder(activity).setMessage("test").show()
    }
}

Please check other SO posts how to implement a getter for the application: e.g. Kotlin Singleton Application Class

like image 4
Michael Troger Avatar answered Oct 17 '22 01:10

Michael Troger