安卓:在锁屏界面上显示视图(如Google地图)

3
我想在锁屏界面上显示一个个性化视图(即实时显示一些数据),就像谷歌地图、百度地图(以及其他一些我忘记名称的应用程序)所实现的那样。
因此,当我的广播接收器被触发时,我正在尝试添加一个视图。我尝试了this answerthis one中的一些点。我也尝试了this one
之前,我遇到了一些权限问题:

permission denied for window type 2XXX

现在我没有任何错误,但是我的视图没有显示出来。这是我的情况:
MainActivity.kt:
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {
    private val REQUEST_CODE = 10001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        checkDrawOverlayPermission()
    }
    fun checkDrawOverlayPermission() {
        /** check if we already  have permission to draw over other apps */
        if (!Settings.canDrawOverlays(this)) {
            /** if not construct intent to request permission */
            val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
            /** request permission via start activity for result */
            startActivityForResult(intent, REQUEST_CODE);
        }
        else {
            startService(Intent(this, LockScreenService::class.java))
        }
    }

    override fun onActivityResult(
        requestCode: Int,
        resultCode: Int,
        data: Intent?
    ) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE) {
            if (Settings.canDrawOverlays(this)) {
                startService(Intent(this, LockScreenService::class.java))
            }
        }
    }
}

LockSceenService.kt:

class LockScreenService : Service() {
    private var mReceiver: BroadcastReceiver? = null
    private var isShowing = false
    override fun onBind(intent: Intent): IBinder? {
        // TODO Auto-generated method stub
        return null
    }

    private var windowManager: WindowManager? = null
    private var textview: TextView? = null
    private var mView: View? = null
    var params: WindowManager.LayoutParams? = null
    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

        mView = View.inflate(baseContext, R.layout.lockscreen_view, null)
        mView!!.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                or View.SYSTEM_UI_FLAG_FULLSCREEN
                or View.SYSTEM_UI_FLAG_VISIBLE
                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
        mView!!.visibility = View.VISIBLE
        //set parameters for the textview

        val flag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            WindowManager.LayoutParams.TYPE_PHONE
        }
        params = WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            flag,
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                    or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
                    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
            PixelFormat.TRANSLUCENT
        )
        params!!.gravity = Gravity.BOTTOM

        //Register receiver for determining screen off and if user is present
        mReceiver = LockScreenStateReceiver()
        val filter = IntentFilter(Intent.ACTION_SCREEN_ON)
        filter.addAction(Intent.ACTION_USER_PRESENT)
        registerReceiver(mReceiver, filter)
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        return START_STICKY
    }

    inner class LockScreenStateReceiver : BroadcastReceiver() {
        override fun onReceive(
            context: Context,
            intent: Intent
        ) {
            if (intent.action == Intent.ACTION_SCREEN_ON) {
                //if screen is turn off show the textview
                if (!isShowing) {
                    windowManager!!.addView(mView, params)
                    isShowing = true
                }
            } else if (intent.action == Intent.ACTION_USER_PRESENT) {
                //Handle resuming events if user is present/screen is unlocked remove the textview immediately
                if (isShowing) {
                    windowManager!!.removeViewImmediate(textview)
                    isShowing = false
                }
            }
        }
    }

    override fun onDestroy() {
        //unregister receiver when the service is destroy
        if (mReceiver != null) {
            unregisterReceiver(mReceiver)
        }

        //remove view if it is showing and the service is destroy
        if (isShowing) {
            windowManager!!.removeViewImmediate(textview)
            isShowing = false
        }
        super.onDestroy()
    }
}

Manifest.xml:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.INTERNET" />

如果您有任何建议或其他方法,例如“在锁定屏幕上透明活动”(?),或者只是一个赞也可能很有用。
谢谢您的时间!
编辑:这里有一个我想要的视频
1个回答

10
您可以使用前台通知服务在设备锁定时显示视图,您可以使用自定义XML通知布局自定义默认通知视图,并且前台服务具有实时更改视图内容的功能。
为了在锁定屏幕上显示活动,您必须按照以下方式进行操作:
在您想要在锁定屏幕上显示的活动中设置以下属性。
清单文件
<activity
  android:name=".MainActivity"
  android:screenOrientation="fullSensor"
  android:showOnLockScreen="true">

在您的活动的onCreate方法中添加以下行:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON|
            WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD|
            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|
            WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

当您的设备在活动打开时被锁定,它将保持在锁屏界面,并带有返回按钮,以便您可以再次导航到锁屏界面(如果需要的话)。

锁屏工作示例如下:

Lock screen activity

来自Google Maps和我们自定义通知警报的示例通知

Google Maps Sample Custom Sample

您可以按照以下Gist创建具有前台服务的自定义通知布局。

权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

清单文件

<service
    android:name=".MyForegroundService"
    android:icon="@drawable/ic_notification"
    android:label="MFS" />

服务

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

import androidx.core.app.NotificationCompat;

public class MyForegroundService extends Service {

    private static final String TAG = "MyForegroundService";

    private static final int NOTIFICATION_ID = 2999;
    private static final String CHANNEL_ID = "MyForegroundService_ID";
    private static final CharSequence CHANNEL_NAME = "MyForegroundService Channel";
    private final IBinder mBinder = new LocalBinder();

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

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate ");
        Toast.makeText(this, "The service is running", Toast.LENGTH_SHORT).show();
        startForeground(NOTIFICATION_ID, createNotification("The service is running"));
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return Service.START_STICKY;
    }

    private Notification createNotification(String message) {

        // Get the layouts to use in the custom notification
        RemoteViews notificationLayout = new RemoteViews(getPackageName(), R.layout.notification_main);
        notificationLayout.setTextViewText(R.id.txtTitle, message);

        NotificationManager mNotificationManager;
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);

        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 125, notificationIntent, 0);

        Bitmap payableLogo = BitmapFactory.decodeResource(getResources(), R.drawable.ic_notification);

        mBuilder.setContentTitle("My Service")
                .setContentText(message)
                .setPriority(Notification.PRIORITY_HIGH)
                .setLargeIcon(payableLogo)
                .setSmallIcon(R.drawable.ic_notification)
                .setContentIntent(pendingIntent)
                .setAutoCancel(false)

                .setCustomBigContentView(notificationLayout);

        mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = CHANNEL_ID;
            NotificationChannel channel = new NotificationChannel(channelId, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            mNotificationManager.createNotificationChannel(channel);
            mBuilder.setChannelId(channelId);
        }

        return mBuilder.build();
    }

    private void showNotification(String message) {
        NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(NOTIFICATION_ID, createNotification(message));
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

通知布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimaryDark"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingHorizontal="20dp"
        android:paddingVertical="15dp">

        <TextView
            android:id="@+id/txtTitle"
            style="@style/TextAppearance.Compat.Notification.Info.Media"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="This is from my service"
            android:textColor="#fff" />

        <TextView
            android:id="@+id/txtResult"
            style="@style/TextAppearance.Compat.Notification.Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="Any text goes here"
            android:textColor="#fff" />

    </LinearLayout>

</LinearLayout>

参考资料:https://gist.github.com/aslamanver/f32a0bb8461c250d4a945e11f6771456 这里是关于IT技术的内容。

嗨!谢谢您的回答和时间,我很感激。但这不是我想要的。我想要全屏显示。我在我的问题中添加了一个视频,让您看到我的期望。如果您的答案可以像视频一样做到(全屏显示),那么我会接受它! - Itoun
@Itoun,请参考更新后的答案,其中包含示例视频。 - Googlian
1
这正是我想要的!非常感谢你! - Itoun

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