Отображать AlertDialog как окно оверлея системы из службы

У меня проблема с отображением AlertDialog из Service. Я могу показать окно пользовательского макета с помощью Toast или с помощью WindowManager (TYPE_SYSTEM_ALERT или TYPE_SYSTEM_OVERLAY). Но я не хочу использовать пользовательский макет, я предпочитаю использовать красивый графический интерфейс AlertDialog напрямую.

Сценарий:

  • Запуск службы. Активная активность отсутствует.
  • В случае какого-либо внешнего события служба отправляет уведомление
  • Когда пользователь нажимает "Уведомление", служба информируется через PendingIntent, и AlertDialog должен отображаться (создается с помощью AlertDialog.Builder(this))

Ошибка:

ERROR/AndroidRuntime(1063): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application

Поиск ответа приводит к тому, что я пытаюсь сделать то, что в настоящее время невозможно (Android 2.2). Или может быть.

Ответы

Ответ 1

найдено решение, соответствующее описанному сценарию.

  • Новая активность была создана и запущена с сервиса. Однако это активность с полупрозрачным фоном. Такая операция не имеет строки super.setContentView() в onCreate(). Более важно, чтобы обеспечить прозрачность.

    @android: стиль /Theme.Translucent

вводится в теге Тема для этого действия в графическом интерфейсе AndroidManifest.xml. Итак, новая строка, добавленная в манифест xml,

android:theme="@android:style/Theme.Translucent"

  • В onCreate() происходит фактическое отображение AlertDialog

  • Нажатие на кнопки AlertDialog приводит к закрытию и отправке диалога и активности Intent (или использованию некоторых других средств) для доставки результата в Сервис.

  • Убедитесь, что вы определили setOnDismissListener() для диалогового окна (реализация должна вызвать функцию finish() для активности). Если вы этого не сделаете, нажмите клавишу "Назад", чтобы отменить диалог, но оставит вас в текущей активности, которая прозрачна и выглядит для пользователя, как будто что-то действительно не так.

Ответ 2

Спасибо за ваше решение. Я столкнулся с той же проблемой и разработал с вашей помощью.

Я поставил этот ответ, чтобы поделиться своим путем, чтобы передать результат обратно в службу.

Я не создавал дополнительный пользовательский класс намерений и не решал проблему прохождения результатов только с помощью методов Intent.putExtra() с некоторыми трюками.

В этой службе используйте этот код, чтобы запустить DialogActivity, который отображает диалоговое окно предупреждения в onCreate().

Intent intent = new Intent(this.getApplicationContext(), DialogActivity.class); 
intent.putExtra(DialogActivity.CLASS_KEY, this.getClass().getCanonicalName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

И в DialogActivity, закончите так:

private void returnOk(boolean ok) {
    Intent srcIntent = this.getIntent();
    Intent tgtIntent = new Intent();
    String className = srcIntent.getExtras().getString(CLASS_KEY);
    Log.d("DialogActivity", "Service Class Name: " + className);
    ComponentName cn = new ComponentName(this.getApplicationContext(), className);
    tgtIntent.setComponent(cn);
    tgtIntent.putExtra(RESULT_KEY, ok ? RESULT_OK : RESULT_CANCEL);
    this.startService(tgtIntent);
    this.finish();
}

Наконец, в службе переопределите метод onStartCommand() и получите результат от намерения.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    int ret = super.onStartCommand(intent, flags, startId);
    Bundle extras = intent.getExtras();
    if (extras != null) {
        int result = extras.getInt(DialogActivity.RESULT_KEY, -1);
        if (result >= 0) {
            if (result == DialogActivity.RESULT_OK) {
                // Your logic here...
            }
        } else {
            // Your other start logic here...
        }
    }
    return ret;
}

Я не уверен, является ли этот способ хорошим решением, по крайней мере, он работает для меня. Надеюсь, это будет полезно для кого-то вроде меня.

Полный источник можно найти здесь: