Wednesday, 7 August 2019

Show extended string (weather temperature) permanently on the Status Bar over a transparent background with all characters right size and visible

I'm Appy Weather's developer, and looking at giving its users the ability to show the temperature permanently on the Status Bar. Unfortunately, it doesn't appear to be straightforward, and I'm not even sure possible (though the fact other apps allow for this makes me think it must be somehow).

Please note the following:

1) the app targets Android 8.0 upwards

2) this is a Xamarin.Android app

Using TextDrawable, I've managed to dynamically create a Drawable that's converted to a Bitmap showing the current temperature that is accepted by the Notification.Builder's 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)));

This works:

enter image description here

But it isn't perfect because:

1) text size would ideally be the maximum possible in the Status Bar i.e. same as the clock's

2) width available means if the temperature is 100°+, or -10° or less, and possibly certain double digit number pairings, then it wouldn't fit and gets cut-off

3) the text will only be visible if the background's set to a colour that isn't black, white or transparent, which is not good because it's important for this to not have a solid background colour

UPDATE 1

So, as Raimo commented below, SetTicker() isn't the correct solution. Not that I've discovered it yet, but Saamer's WindowManager tip has resulted in me hopefully getting closer to the desired outcome.

I've added the following permissions to the Manifest:

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

I request permission within my Settings activity:

// 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)
    {

    }
}

I'm taken correctly to the phone's relevant settings permission screen, and when I grant permission and return to Settings it will run the method seen below that schedules an hourly Job (using an internal helper method I created that works fine) when the user ticks the relevant setting, as well as pushing it to the status bar directly and immediately outside the job:

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)
    {

    }
}

This is the Resource.Layout.StatusBar_Temp resource layout file I've created to be used (using a placeholder value for the TextView for testing purposes):

<?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>

And finally, this is the method used to push out the update to the Status Bar:

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);
}

I'm using WindowManagerTypes.ApplicationOverlay because the other system types that seem to have been suggested in the past can no longer be used from 8.0 up anyway (was hitting exceptions when I tried them originally).

At the moment, with the above code, I'm not running into any exceptions, and everything appears to run smoothly both when the Job runs as well as on the initial push. However, there's a big problem: nothing appears to be drawn. For what it's worth, originally I attempted this by creating a LinearLayout containing a TextView programmatically (as opposed to using an existing layout), but that had the same issue with nothing being visible.

Any ideas?



from Show extended string (weather temperature) permanently on the Status Bar over a transparent background with all characters right size and visible

No comments:

Post a Comment