Всплывающее окно по внешнему экрану входящих вызовов Android, например, приложение для Android-звонящего

Обновление - 09 мая 2013 г. * Я попытался внедрить Toast в приемник широковещательной передачи onReceive, потому что тост является родным компонентом Android, но он также не отображается в Android 4.1 (Jelly Bean).

Моя идея заключалась в том, чтобы внедрить Toast в приемник широковещательного приема onReceive, а затем изменить его дизайн в соответствии с нашими потребностями и настроить его продолжительность отображения. Но еще одна проблема заключается в том, что findViewById не работает в широковещательном приемнике, поэтому я думаю, что мы должны сделать LinearLayout программным способом для настройки тоста. *

Обновление. После щедрости также я не получаю то, что ищу, но я вернусь ко всем; Я работаю над этим. Во всяком случае, этот код работает для большинства телефонов Android. Если кто-то собирается использовать и поймать решение для этого, напишите здесь, чтобы каждый мог получить выгоду. Спасибо за все ваши ответы.

Я разрабатываю широковещательный приемник для входящих звонков в Android и при поступлении входящих звонков, которые я хочу раздуть всплывающее окно по собственному экрану входящих вызовов.

Я завершил этот код. Но теперь проблема заключается в том, что в Android 4.1 (Jelly Bean) уровне API 17, когда телефон звонит, PHONE_STATE подходит как OFF HOOK, и если я вызываю действие, он вызывается, но код под ним не выполняется. Я перечисляю код:

Мой вещательный приемник

package com.example.popwindowonincomingcallscreen;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log;

public class IncomingBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        Log.d("IncomingBroadcastReceiver: onReceive: ", "flag1");

        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        Log.d("IncomingBroadcastReceiver: onReceive: ", state);
        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)
                || state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {

            Log.d("Ringing", "Phone is ringing");

            Intent i = new Intent(context, IncomingCallActivity.class);
            i.putExtras(intent);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
            Wait.oneSec();
            context.startActivity(i);
        }
    }
}

Я выполняю действия, которые я вызываю:

import android.app.Activity;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View.MeasureSpec;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

public class IncomingCallActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        try {
            Log.d("IncomingCallActivity: onCreate: ", "flag2");

            */ After this line, the code is not executed in Android 4.1 (Jelly Bean) only/*

            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);

            getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);

            Log.d("IncomingCallActivity: onCreate: ", "flagy");

            setContentView(R.layout.main);

            Log.d("IncomingCallActivity: onCreate: ", "flagz");

            String number = getIntent().getStringExtra(
                    TelephonyManager.EXTRA_INCOMING_NUMBER);
            TextView text = (TextView) findViewById(R.id.text);
            text.setText("Incoming call from " + number);
        } 
        catch (Exception e) {
            Log.d("Exception", e.toString());
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

После

try {
    Log.d("IncomingCallActivity: onCreate: ", "flag2");
}

Код не выполняется в Android 4.1 (Jelly Bean), но в других версиях он работает.

Я пробовал почти все, что могу. Этот код отображает полупрозрачную активность на экране собственных вызовов, и он не блокирует фоновые элементы управления, например, поднимает телефон. Но я хочу, чтобы он был истинным абонентом. Я приложил снимок о том, как истинный вызывающий абонент отображает окно на экране входящих вызовов.

Как я могу достичь этой функциональности для Android-приложения?

Вот как работает истинный вызывающий абонент:

Enter image description here

Мой текущий вывод:

Enter image description here

Ответ 1

Я не уверен, что ваш пользовательский графический интерфейс всегда будет поверх стандартного, потому что системный широковещательный приемник и ваш ресивер пытаются отобразить его графический интерфейс поверх экрана. Мы не уверены, какой из них называется первым, но одна сложная работа, чтобы сделать ваш графический интерфейс сверху экрана, когда телефон звонит, вызываете свою активность через 1-2 секунды (-ых) для этого обработчика.

new Handler().postDelayed(new Runnable() {

     @Override
     public void run() {
         // TODO Auto-generated method stub
         Intent intent = new Intent(context, AcceptReject.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         context.startActivity(intent);
     }
 }, 2000);

Я надеюсь, что это может вам помочь.

Ответ 2

Попробуйте выполнить код перед методом super.onCreate. Я думаю, что после вызова супер код пропускается. Когда-то этот трюк работал у меня.

Ответ 3

Я только что протестировал эмулятор Android 4.2 (Jelly Bean), и он отлично работает, блокируя весь экран входящих вызовов, как truecaller:

public void onReceive(Context context, Intent intent) {

    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        LayoutParams.MATCH_PARENT,
        LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSPARENT);

    params.height = LayoutParams.MATCH_PARENT;
    params.width = LayoutParams.MATCH_PARENT;
    params.format = PixelFormat.TRANSLUCENT;

    params.gravity = Gravity.TOP;

    LinearLayout ly = new LinearLayout(context);
    ly.setBackgroundColor(Color.RED);
    ly.setOrientation(LinearLayout.VERTICAL);

    wm.addView(ly, params);
}

В манифесте:

<receiver android:name=""  android:enabled="true" >
    <intent-filter android:priority="-1">
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>

Ответ 4

Я пытаюсь что-то подобное, добавляя дополнительную кнопку на экран входящего вызова.

Ответ, написанный Sam Adams, работает для меня, хотя я вызываю код из PhoneStateListener. Кроме того, единственное реальное отличие от его кода - это раздувание макета:

overlay = (RelativeLayout) inflater.inflate(R.layout.overlay, null);
wm.addView(overlay, params);

Он работает как с эмуляторами, так и с HTC One S (работает под управлением Android 4.1.1).

Что-то, о чем вам нужно помнить, это сохранить ссылку на отображаемое оверлейное представление, которое вы добавляете, и снова удалить его (вызовите removeView() в экземпляре windowmanager), когда телефон возвращается в режим ожидания (когда слушатель получает TelephonyManager.CALL_STATE_IDLE), в противном случае ваш оверлей останется на экране.

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    if(overlay!=null)
    {
        wm.removeView(overlay);
        overlay = null;
    }

Ответ 5

Я думаю, вы не должны начинать активность, чтобы достичь описанного результата. Вам нужно иметь отдельный вид, содержащий LayoutParams.TYPE_SYSTEM_OVERLAY в его параметрах макета.

Вы можете расположить этот вид везде, где хотите на экране, или просто закрыть весь экран.

Вот несколько строк кода:

 _av = new ActivatorView(this);
 _avLayoutParams = new WindowManager.LayoutParams(0, 0, 0, 0,
     WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
     PixelFormat.OPAQUE);
 _avLayoutParams.screenBrightness = _fScreenBrightness = 20f;

 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
 wm.addView(_av, _avLayoutParams);

https://bitbucket.org/gyrussolutions/yaab/src/f01cc8aff690cae1b1107287cb17835b8a3c1643/src/biz/gyrus/yaab/LightMonitorService.java?at=default#cl-338 - полный исходный код, рассмотрите его как образец.

Ответ 6

Я также работаю над этим (возможно, я ошибаюсь, чтобы понять вас здесь). То, что вы хотите достичь, - показать эту активность в Android 4.2 (Jelly Bean). Я просто поместил задержку для отображения активности. Я использовал PhoneStateListener в разных классах. Я могу отображать новую активность на экране вызывающего абонента. Вот мой полный код:

Enter image description here

Файл MyBroadcastReceiver.java

public class MyBroadcastReceiver extends BroadcastReceiver {
    static CustomPhoneStateListener phoneStateListener;
    Context context;
    Intent intent;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;
        this.intent = intent;
        // TODO Auto-generated method stub

            TelephonyManager telephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);           
            phoneStateListener = new CustomPhoneStateListener(context);
            telephonyManager.listen(phoneStateListener,
                    PhoneStateListener.LISTEN_CALL_STATE);
    }
}

Файл CustomPhoneStateListener.java

public class CustomPhoneStateListener extends PhoneStateListener {

    // private static final String TAG = "PhoneStateChanged";
    Context context; // Context to make Toast if required
    private AudioManager amanager;
    Intent i1;

    public CustomPhoneStateListener(Context context) {
        super();
        this.context = context;
        i1 = new Intent(context, YourActivity.class);       
        i1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    }

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);

        switch (state) {
        case TelephonyManager.CALL_STATE_IDLE:
            Toast.makeText(context, "Phone state Idle", Toast.LENGTH_LONG)
                    .show();

            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:

            Toast.makeText(context, "Phone state Off hook", Toast.LENGTH_LONG)
                    .show();

            break;
        case TelephonyManager.CALL_STATE_RINGING:           
            try {
                Thread.sleep(3000);
                context.startActivity(i1);              
            } catch (Exception e) {
                e.getLocalizedMessage();
            }

        default:
            break;
        }
    }

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

  • Когда закрытый вызов закрыт (пропущенный или отклоненный), активность не закрывается.
  • Я не могу нажать "Активность" (я хочу добавить одну кнопку для своего приложения)
  • Он работает только в первый раз. Когда я делаю вызов второй раз, мое приложение останавливается (я думаю, это потому, что действие не закрывается, когда вызов отклоняется).

(Помощь, принятая для этих проблем. Спасибо. Может помочь кому-то)

UPDATE

ЗДЕСЬ ЛИНИЯ МАЛЫЙ ДЕМО КАК ДОСТИГНУТЬ ЭТО.

  • Когда закрытый вызов закрыт (пропущенный или отклоненный), активность не закрывается. - SOLVED
  • Я не могу нажать на Activity (я хочу добавить одну кнопку для своего приложения) - SOLVED
  • Он работает только в первый раз. Когда я делаю вызов второй раз, мое приложение останавливается (я думаю, это потому, что действие не закрывается, когда вызов уволен) - SOLVED

Ответ 7

Мой метод:

  • использовать приемник для получения событий телефонного звонка
  • использовать сервис, чтобы сделать наложение

    ps:wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; 
    

Ответ 8

Мы также столкнулись с аналогичной проблемой, что наложение не было отображено на устройстве с блокировкой контактов. Решение, которое сработало для нас, ниже:

mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    mParams = new LayoutParams(
        LayoutParams.MATCH_PARENT,
        LayoutParams.WRAP_CONTENT,
        LayoutParams.TYPE_SYSTEM_ERROR,
        LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT);

Это было LayoutParams.TYPE_SYSTEM_ERROR, что имело значение.

Ответ 9

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Intent i = new Intent(context, CallingIncoming.class);
        i.putExtras(intent);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK /*| Intent.FLAG_ACTIVITY_CLEAR_TASK*/);
        context.startActivity(i);
    }
}, 450);//It will help you to delay your screen and after it your screen will be top of default app screen

Ответ 10

Используйте простой широковещательный приемник и поместите этот код в широковещательный приемник:

public void onReceive(final Context context, final Intent intent) {

    Log.v(LOG_TAG, "Receieved notification about network status");
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
            WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSPARENT);

    params.height = WindowManager.LayoutParams.MATCH_PARENT;
    params.width = WindowManager.LayoutParams.MATCH_PARENT;
    params.format = PixelFormat.TRANSLUCENT;

    params.gravity = Gravity.TOP;

    LinearLayout ly = new LinearLayout(context);
    ly.setBackgroundColor(Color.RED);
    ly.setOrientation(LinearLayout.VERTICAL);

    wm.addView(ly, params);

}