Settings.canDrawOverlays() allays returns ‘false’ on Android O

I was updating one of my clients app, and testing it how it behaves on Android O (API 26). The app requires permissions to draw over system windows (android.permission.SYSTEM_ALERT_WINDOW). On Android 6 (API 23) and up, you are obligated to request ‘special’ permissions while the app is running.

The ‘SYSTEM_ALERT_WINDOW’ permission is a special permission that breaks the rules set by the new permission model available on Android 6 (API 23) and up. It’s request involves calling Settings.canDrawOverlays() and if it returns ‘false‘ starting ‘Settings‘ where the user can choose to grant your app the ability to draw overlays or not.

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
   // On API 23 and later ask the user to grant us permission to draw system overlay
   // windows.
   if (!Settings.canDrawOverlays(this))
   {
      Intent intent = new Intent(
             Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
             Uri.parse("package:" + getPackageName()));
 
      startActivityForResult(intent, REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT);
   }
}

There is a bug with Settings.canDrawOverlays() (only) on API 26 where it will always return ‘false’ disregarding the actual user decision. The workaround provided here is a bit ugly, but does not involves restarting the app (which will be quite annoying for the user) after the permission is granted.

The code below first checks the result of System.canDrawOverlays() if it returns ‘true’ it continues with the rest of the application flow. If it returns ‘false’ a check if we are running on Android O (API 26) is performed. If that’s the case, we are calling our ‘workaround’ method.

The ‘workaround’ method tries to add an invisible overlay window on the screen, and if that’s OK we assume that we have a permission to draw overlays, else an exception is thrown.

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
 
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; if(requestCode == REQUEST_PERMISSION_SYSTEM_OVERLAY_RESULT) { if(Settings.canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted && m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } else if(Build.VERSION.SDK_INT == Build.VERSION_CODES.O) { // NOTE: This is a workaround to fix the bug in Android O where the // Settings.canDrawOverlays() will always return 'false' if(canDrawOverlays(this)) { m_permissionSystemOverlayWindowGranted = true; if(m_permissionReadPhoneStateGranted && m_permissionProcessOutgoingCallsGranted) { startService(new Intent(this, EstatePlusService.class)); m_layoutNoPermissions.setVisibility(View.INVISIBLE); m_progressBar.setVisibility(View.INVISIBLE); m_layoutLogin.setVisibility(View.VISIBLE); } } } } } /** * Workaround for Android O */ public static boolean canDrawOverlays(Context context) { try { WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); if (windowManager == null) { return false; } final View viewToAdd = new View(context); WindowManager.LayoutParams params = new WindowManager.LayoutParams( 0, 0, android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O ?
                                    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
                            WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
            viewToAdd.setLayoutParams(params);
            windowManager.addView(viewToAdd, params);
            windowManager.removeView(viewToAdd);
            return true;
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
 
        return false;
    }

I brought myself a Christmas present

After being upgraded to June 2017 iMac 27″ 5k Retina at work, and using it for a week, I decided it’s about time to purchase myself an iMac. My first impression with this machine is fantastic, everything just works (fast and smooth) and the 5K Retina is amazing.

Today (after being financially stable, after some time 🙂 ) I ordered 27″ iMac 5K Retina equipped with 4.2GHz Quad-Core Intel Core i7, 16GB 2400MHz DDR4 (will be upgraded to 32 or 64 GB later), 2TB Fusion Drive and Radeon Pro 580 with 8GB VRAM.

It will arrive around 20 Jan 2018. It will be a nice time to start developing apps for iOS and bring VMSoft to the app store as well. Also a lot of my clients were asking me when will I start developing for iOS.

Fighting spammers: Removing Website URL Field from Comment Form

I’m tired of receiving massive amount of spam comments on my blog (1280 spam comments for about a month) and doing:

DELETE FROM `wp_comments` WHERE `comment_approved` = 0

Beside using SI Captcha Anti-Spam which does pretty good job in keeping some of the automated spam bots at bay, I noticed some trend in the spam comments flooding my posts, they are all full of SEO keywords and they are heavy abusing the Website field in the comment form.

Here is a small “plugin” which will remove (unset) the URL field in the comments form:

<?php
/*
Plugin Name: Remove Website field from comments.
*/
function custom_comment_fields( $fields ){
  if(isset($fields['url']))
    unset($fields['url']);
  return $fields;
}
 
add_filter( 'comment_form_default_fields', 'custom_comment_fields' );

Create a file in /wp-content/plugins/ with the code a bough. Go to the ‘Plugins’ section of the admin panel and activate the newly created plugin named “Remove Website field from comments”.

Yey, no more “Website” field in the “Leave a Reply” section 🙂

Java: Parts of the Day

A handy utility method that will return the part of the day, such as morning, afternoon, evening, etc.

public static String getPartOfTheDay(final int hour)
{
    if(hour > 4 && hour < 12)
    {
        if(hour <= 8)
        {
            return "Early Morning";
        }
        else if(hour > 8 && hour < 11)
        {
            return "Morning";
        }
 
        return "Late Morning";        
    }
    else if(hour >= 12 && hour < 17)
    {
        if(hour >= 13 && hour <= 15)
        {   
            return "Early Afternoon";
        }
        else if(hour >= 16)
        {
            return "Late Afternoon";
        }
 
        return "Afternoon";
    }
    else if(hour >= 17 && hour <= 21)
    {
        if(hour <= 19)
        {   
            return "Early Evening";
        }
 
        return "Evening";
    }
    else
    {
        return "Night";
    }
}

The method is based on the following logic, that many people would agree with:

Morning: 5 to 12

  • Early morning: 5 to 8
  • Late morning: 11 to 12

Afternoon: 12 to 17

  • Early afternoon: 13 to 15
  • Late afternoon: 16 to 17

Evening: 17 to 21

  • Early evening: 17 to 21

Night: 21 to 4