Постоянно показывать расширенную строку (погоду) в строке состояния на прозрачном фоне, все символы правильного размера и видимые - PullRequest
1 голос
/ 02 июля 2019

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

Обратите внимание на следующее:

1) приложение предназначено для Android 8.0 и выше

2) это приложение Xamarin.Android

Используя TextDrawable , мне удалось динамически создать Drawable, который преобразуется в растровое изображение, показывающее текущую температуру, которая принята функцией Notification.Builder SetSmallIcon ():

Notification.Builder builder = new Notification.Builder(context, channelId)
    .SetContentText(text)
    .SetOngoing(true);

var bld = Android.Ui.TextDrawable.TextDrawable.TextDrwableBuilder.BeginConfig().FontSize(72).UseFont(Typeface.Create("sans-serif-condensed", TypefaceStyle.Normal)).EndConfig();
var drawable = bld.BuildRect(title, Color.Red);
builder.SetSmallIcon(Icon.CreateWithBitmap(Helper_Icon_Droid.drawableToBitmap(drawable)));

Это работает:

enter image description here

Но это не идеально, потому что:

1) размер текста в идеале должен быть максимально возможным в строке состояния, то есть таким же, как

2) доступная ширина означает, что если температура составляет 100 ° + или -10 ° или менее и, возможно, определенные пары двузначных чисел, то она не будет соответствовать и будет обрезана

3) текст будет виден только в том случае, если для фона задан цвет, который не является черным, белым или прозрачным, что не очень хорошо, поскольку для этого важно не иметь сплошной цвет фона

ОБНОВЛЕНИЕ 1

Итак, как прокомментировал Раймо ниже, SetTicker () не является правильным решением. Не то чтобы я обнаружил это, но совет Saamer WindowManager привел к тому, что я надеюсь приблизиться к желаемому результату.

Я добавил следующие разрешения в Манифест:

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

Я запрашиваю разрешение в рамках своей настройки:

// permissions
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
private string[] _permissions =
{
      Manifest.Permission.SystemAlertWindow
};


protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE)
    {
        if ( Android.Provider.Settings.CanDrawOverlays(MainApplication.Context) )
        {
            JobManager.Instance().Scheduler().setJobTemp();
        }
    }
}

public void checkDrawOverlayPermission()
{
    try
    {
        // check if we already have permission to draw over other apps 
        // if we don't, we need to get system permission
        if ( !Android.Provider.Settings.CanDrawOverlays(MainApplication.Context) )
        {
            var intent = new Intent(Android.Provider.Settings.ActionManageOverlayPermission, Android.Net.Uri.Parse("package:" + Android.App.Application.Context.PackageName));
            StartActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
        }
        // otherwise, set up the job
        else
        {
            JobManager.Instance().Scheduler().setJobTemp();
        }
    }
    catch (Exception ex)
    {

    }
}

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

public void setJobTemp()
{
    try
    {
        if (_scheduler.GetPendingJob(Helper_Notification._NOTIFICATION_ID_TEMP) == null)
        {
            _jobTemp = _context.CreateJobBuilderUsingJobId<Job_Temp>(Helper_Notification._NOTIFICATION_ID_TEMP);
            bool success = Helper_Job.ScheduleJob(_context, _jobTemp, 60, 5);

            // besides setting the hourly job above, we want to immediately push it out too
            Helper_Notification.Push( ViewModel._instance._http.Response.Hourly.Data[ViewModel._instance._http.Response.Hourly._indexStartFrom].Temperature );
        }
    }
    catch (Exception ex)
    {

    }
}

Это файл макета ресурса Resource.Layout.StatusBar_Temp, который я создал для использования (используя значение заполнителя для TextView в целях тестирования):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root"
    style="@style/LayoutWrap">
    <TextView
        android:id="@+id/value"
        style="@style/TextWrap"
        android:text="abc"/>
</LinearLayout>

И, наконец, этот метод используется для отправки обновления в строку состояния:

public static void Push( string text )
{
    IWindowManager windowManager = MainApplication.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
    var temp = LayoutInflater.From(MainApplication.Context).Inflate(Resource.Layout.StatusBar_Temp, null);

    var layoutParams = new WindowManagerLayoutParams(1, 1, WindowManagerTypes.ApplicationOverlay, WindowManagerFlags.NotFocusable, Format.Translucent);
    layoutParams.Gravity= GravityFlags.Top | GravityFlags.Left;
    layoutParams.X = 0;
    layoutParams.Y = 0;

    windowManager.AddView(temp, layoutParams);
}

Я использую WindowManagerTypes.ApplicationOverlay, потому что другие типы систем, которые, по-видимому, были предложены в прошлом, больше не могут использоваться с версии 8.0 и выше (когда я пробовал их изначально, возникали исключения).

На данный момент, с помощью приведенного выше кода, я не сталкиваюсь ни с какими исключениями, и кажется, что все работает гладко, как при выполнении задания, так и при первоначальном нажатии. Однако есть большая проблема: кажется, что ничего не нарисовано. Что бы это ни стоило, изначально я попытался сделать это путем создания LinearLayout, содержащего TextView программным способом (в отличие от использования существующего макета), но с той же проблемой, когда ничего не было видно.

Есть идеи?

1 Ответ

0 голосов
/ 02 июля 2019

Вы пытались использовать SetTicker()? Таким образом, ваш код будет выглядеть примерно так (возможно, придется использовать Unicode 00B0 для символа степени)

Notification.Builder builder = new Notification.Builder(context, channelId)
    .SetContentText(text)
    .SetOngoing(true);
    .SetTicker("17°");

Существует альтернативный способ выяснить StatusBarHeight и LayoutParams, а затем добавить TextView к LinearLayout, который затем добавляется к WindowManager, используя всю информацию.

...