如何在Android 5.1以下版本中将应用程序运行在前台

4
我已经开发了一个小型的计时器应用程序,并且每当我接到电话时,它就会启动。例如,在接收到呼入电话后,我会启动我的计时器应用并在前台显示计时器。

但是它只能在 lollipop 5.1 版本中在后台运行,而在低端版本中它却在后台运行。

我需要在所有设备上都将应用程序运行在前台,请问如何实现?

我的代码:

Intent it = new Intent("intent.my.action");
it.setComponent(new ComponentName(context.getPackageName(), timer.class.getName()));
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.getApplicationContext().startActivity(it);

我的接收器:

public class CallerToActivity extends BroadcastReceiver {
    static boolean wasRinging = false;
    static boolean finish = false;
    SessionManager session;
    private boolean enable;

    public void onReceive(Context context, Intent intent) {
        session = new SessionManager(context);
        String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
        enable = session.Is_Enabled();
        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {

            Log.d("Status", "Phone is Ringing");
        } else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {

                    Intent it = new Intent("intent.my.action");
                    it.putExtra("Call", "true");
                    it.setComponent(new ComponentName(context.getPackageName(), timer.class.getName()));
                    it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    context.getApplicationContext().startActivity(it);



        } else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
            // Call Dropped or rejected
            Toast.makeText(context, "phone is neither ringing nor in a call",
                    Toast.LENGTH_SHORT).show();
            // wasRinging = false;
            finish = true;

            System.exit(0);
            Log.d("Status", "Phone is dropped");

        }

    }
}

1
当接收到任何电话时,它具有高优先级,因此它会出现在前台。因此,如果您想在电话运行时显示计时器,则需要使用SYSTEM_ALERT标志将您的UI设置为窗口覆盖,以便它成为最顶部的视图并覆盖设备默认的电话呼叫视图。 - Hitesh Bhalala
你能给我推荐一些关于SYSTEM_ALERT的链接或示例吗? - Piku
抱歉,我在调用和接收时启动了我的应用程序。我有点困惑,请检查我的更新代码。 - Piku
请查看我的PhoneStatReceiver.java类中的解释。 - Hitesh Bhalala
我在这里启动计时器,有时它会在前台运行。我想现在你明白我的问题了。 - Piku
显示剩余4条评论
1个回答

4
以下是解决问题的步骤:
1) 创建接收器以监听呼叫(PhoneStatReceiver.java
2) 创建服务以在您的电话呼叫视图上方显示视图(例如计时器视图)(HBFloatingHead.java
3) 在HBFloatingHead服务中创建计时器,并根据计时器更新窗口布局。
4) 更新您的AndroidManifest.xml文件。 示例代码: 1) PhoneStatReceiver.java
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.util.Log;


public class PhoneStatReceiver extends BroadcastReceiver{

    private static final String TAG = "PhoneStatReceiver";

//        private static MyPhoneStateListener phoneListener = new MyPhoneStateListener();

    private static boolean incomingFlag = false;

    private static String incoming_number = null;

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

        if(intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){
            incomingFlag = false;
            String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
            Log.i(TAG, "call OUT:"+phoneNumber);
            startService(context);
        }else{

            TelephonyManager tm =
                    (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);

            switch (tm.getCallState()) {
                case TelephonyManager.CALL_STATE_RINGING:
                    incomingFlag = true;
                    incoming_number = intent.getStringExtra("incoming_number");
                    Log.i(TAG, "RINGING :"+ incoming_number);
                    startService(context);
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    if(incomingFlag){
                        Log.i(TAG, "incoming ACCEPT :"+ incoming_number);
                    }
                    break;

                case TelephonyManager.CALL_STATE_IDLE:
                    if(incomingFlag){
                        Log.i(TAG, "incoming IDLE");
                    }
                    break;
            }
        }
    }

    public void startService(Context context){
        Intent intent = new Intent(context, HBFloatingHead.class);
        context.startService(intent);
    }
}

2) 更新 HBFloatingHead.java

 import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.TextView;

public class HBFloatingHead extends Service {

    private WindowManager mhbWindow;
    private TextView mfloatingHead;
    private Intent intent;
    public static final String BROADCAST_ACTION = "com.fragmentsample";


    private final IBinder mBinder = new LocalBinder();

    public class LocalBinder extends Binder {
        HBFloatingHead getService() {
            return HBFloatingHead.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mfloatingHead != null) {
            mhbWindow.removeView(mfloatingHead);
            countDownTimer.cancel();
        }
    }

    WindowManager.LayoutParams params;

    @Override
    public void onCreate() {
        super.onCreate();
        intent = new Intent(BROADCAST_ACTION);

        mhbWindow = (WindowManager) getSystemService(WINDOW_SERVICE);
        mfloatingHead = new TextView(this);
        mfloatingHead.setBackgroundResource(R.drawable.floating4);
        mfloatingHead.setTextColor(Color.WHITE);
        mfloatingHead.setTextSize(20f);
        mfloatingHead.setHint("00.00 sec");
        mfloatingHead.setGravity(Gravity.CENTER);
        mfloatingHead.setPadding(20, 20, 20, 20);

        params = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);

        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.x = 0;
        params.y = 100;

        mhbWindow.addView(mfloatingHead, params);

        try {
            mfloatingHead.setOnTouchListener(new View.OnTouchListener() {

                private WindowManager.LayoutParams paramsF = params;
                private int initialX;
                private int initialY;
                private float initialTouchX;
                private float initialTouchY;

                @Override
                public boolean onTouch(View v, MotionEvent event) {

                    switch (event.getAction()) {
                        case MotionEvent.ACTION_UP:

                            break;

                        case MotionEvent.ACTION_DOWN:

                            initialX = paramsF.x;
                            initialY = paramsF.y;
                            initialTouchX = event.getRawX();
                            initialTouchY = event.getRawY();

                            break;

                        case MotionEvent.ACTION_MOVE:

                            paramsF.x = initialX
                                    + (int) (event.getRawX() - initialTouchX);
                            paramsF.y = initialY
                                    + (int) (event.getRawY() - initialTouchY);
                            mhbWindow.updateViewLayout(mfloatingHead, paramsF);

                            break;
                        default:
                            break;
                    }

                    return false;
                }
            });
        } catch (Exception e) {
            Log.e("#HB#", e.getMessage().toString());
        }

        mfloatingHead.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                //HBFloatingHead.this.stopSelf();


            }
        });
        startTimer(1);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        return START_STICKY;
    }

    android.os.Handler handler = new android.os.Handler(new android.os.Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            try {
                long seconds = msg.what;
                String text = String.format("%02d", seconds / 60) + ":"
                        + String.format("%02d", seconds % 60);
                Log.e("TAG", "Updated text : " + text);
                mfloatingHead.setText(text);
                mhbWindow.updateViewLayout(mfloatingHead, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
    });

    CountDownTimer countDownTimer;

    private void startTimer(final int minuti) {
        countDownTimer = new CountDownTimer(60 * minuti * 1000, 500) {
            @Override
            public void onTick(long millisUntilFinished) {
                int seconds = (int) (millisUntilFinished / 1000);

                if (seconds > 0)
                    handler.sendEmptyMessage(seconds);
                else
                    HBFloatingHead.this.stopSelf();

//              Log.d("TIME", mTvTime.getText().toString());
            }

            @Override
            public void onFinish() {

            }
        };

        countDownTimer.start();
    }
}


3) 按照HBFloatingService类中的注释创建您的布局
4) 更新您的AndroidManifest.xml文件

添加权限

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE">     </uses-permission>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission> 


声明您的Android组件

 <receiver android:name=".PhoneStatReceiver">
      <intent-filter>
         <action android:name="android.intent.action.PHONE_STATE" />
         <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
      </intent-filter>
 </receiver>

 <service
      android:name=".HBFloatingHead"
      android:exported="true" />

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接