Android: отображение вида на экране блокировки (например, Google Maps) - PullRequest
2 голосов
/ 18 марта 2020

Я хотел бы отобразить персонализированный вид (который отображает некоторые данные в режиме реального времени) на экране блокировки (после этого пользователь заблокировал телефон во время действия).
Карты Google и карты Baidu (и некоторые другие приложения, которые Я забыл название) точно реализовал то, что хочу.

Поэтому я пытаюсь добавить представление, когда запускается мой BroadcastReceiver.
Есть некоторые моменты, которые я пытался из этого ответа и этого . Я пытался этот тоже.
Прежде чем я получил какую-то ошибку в соответствии с разрешением:

В типе окна 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()
    }
}

Манифест. 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 Ответ

4 голосов
/ 20 марта 2020

Вы можете отобразить вид на экране блокировки, когда устройство заблокировано, используя службы уведомлений переднего плана, вы можете настроить представление уведомлений по умолчанию с помощью пользовательского макета уведомлений 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

Чтобы создать настраиваемый макет уведомления с помощью службы переднего плана, следуйте приведенным ниже инструкциям.

Разрешения

<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

...