Problem with shortcut images

Kevin

Well-Known Member
Licensed User
Longtime User
I'm allowing the user to create shortcuts using Erel's example here.

The problem I am having is that on some AVDs the shortcut image appears correctly but on others it is way too large. And that took a lot of messing around just to get to that point. I'm trying to do this with my own PNG images (have tried with sizes 72x72 and 96x96) but even using Erel's 48x48 PNG icon does not appear correctly on all AVDs.

I've tried the following using the reflector object but still didn't work:

B4X:
Sub CreateScaledBitmap(Original As Bitmap, Width As Int, Height As Int, Filter As Boolean) As Bitmap

    Dim r As Reflector
    Dim b As Bitmap
    b = r.RunStaticMethod("android.graphics.Bitmap", "createScaledBitmap", _
        Array As Object(Original, Width, Height, Filter), _
        Array As String("android.graphics.Bitmap", "java.lang.int", "java.lang.int", "java.lang.boolean"))
    Return b
End Sub

I've tried the following, but still didn't work:

B4X:
tbmp = Common.GetAppIcon ' Get's the app's icon to see what size it is
scSize = tbmp.Height
   
ToastMessageShow (tbmp.Height & "h X " & tbmp.Width & "w", True)
' Works for most but not all ----
tImg.InitializeMutable (scSize, scSize)
tImg = LoadBitmapSample (File.DirAssets, imageFN, scSize, scSize)

... and of course in all case, the icon is assigned to tImg then applied to the shortcut like this:

B4X:
in.PutExtra("android.intent.extra.shortcut.ICON", tImg)

I've tried so many combinations of things and no matter what I do, I can get it to look right on some devices but then it messes it up on others. I've tried LoadBitmap and LoadBitmapSample. I've tried basing things off of the activity "scale" but that didn't help. I thought about basing things off of the apparent density but I know that won't work because of the ones that display correctly, the density is all over the place.

I just don't get what I'm doing wrong. I spent about 8 hours yesterday just messing with these icons, sure that I would figure it out eventually. :BangHead:

My suspicion is that ordinarily when you provide an icon with your app, Android scales it for you, but it seems that when you provide an icon programmatically (as in when passing it to an intent to create a shortcut) then it doesn't do any scaling for you at all. But if this is the case, then I haven't got a clue how to figure out what size it should be. Even if I could, LoadBitmapSample does not return the exact requested size. The routine using the reflection object probably does but it also washes out the colors anyway. But besides that, it didn't solve my problem. :(

:sign0085:
 

thedesolatesoul

Expert
Licensed User
Longtime User
Okay, right now its hard to answer your question because you are all over the place.
Lets get some perspective.
Start off with a Launcher icon size of 48x48 pixels (Android Design - Iconography)
This 48x48 pixel icon will look good on any device with Scale=1.
For scale = 1.5, we will need a 72x72 pixel icon (but dont just scale the icon up) unless you dont mind scaling artifacts.
Make sure the icon on 48x48 looks good and not stretched.
Apply that without scaling to the shortcut across all versions.
Then post some screenshots on what is wrong with each.
 
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
Sorry for the confusion. I was in a bit of a hurry this morning and didn't want to forget anything.

Anyway, the problem continues.

Here is my code:

B4X:
Dim scSize As Int, scOutputSize As Int, tImg As Bitmap, tbmp As Bitmap, OutPNG As Bitmap
tbmp = Common.GetAppIcon
scSize = tbmp.Height

Dim dScale As Float
dScale = GetDeviceLayoutValues.Scale
' _m = 72x72; _h = 96x96 ' Adds to the filename depending on what icon size we want (medium or high density icons)
' _m works for most but not all

Select Case dScale
Case 1
scOutputSize = 48
imageFN = imageFN & "_m.png"
Case 1.5
scOutputSize = 72
imageFN = imageFN & "_m.png"
Case Else
scOutputSize = 96
imageFN = imageFN & "_h.png"
End Select
tImg = LoadBitmap(File.DirAssets, imageFN)
tImg = Common.CreateScaledBitmap (tImg, scOutputSize, scOutputSize,True)
OutPNG = tImg

ToastMessageShow ("Device scale is " & dScale & CRLF & "App shortcut icon size is " & tbmp.Height & "h X " & tbmp.Width & "w" & CRLF & "Shortcut icon size assigned was " & tImg.Height & "h X " & tImg.Width & "w", True)

Dim shortcutIntent As Intent
shortcutIntent.Initialize("", "")
shortcutIntent.SetComponent(Common.ShortcutPackage & "/.shortcutexec")
shortcutIntent.PutExtra("from_shortcut", True)
shortcutIntent.PutExtra("sc_type", SCType)
shortcutIntent.PutExtra("sc_value", SCValue)
shortcutIntent.PutExtra("sc_destination", SCDestination)
shortcutIntent.PutExtra("sc_toast", SCToast)
shortcutIntent.PutExtra("sc_notification", SCNotification)
shortcutIntent.PutExtra("sc_extra1", "")
shortcutIntent.PutExtra("sc_extra2", "")
Dim in As Intent
in.Initialize("", "")
in.PutExtra("android.intent.extra.shortcut.INTENT", shortcutIntent)
in.PutExtra("android.intent.extra.shortcut.NAME", txtName.Text)
in.PutExtra("android.intent.extra.shortcut.ICON", OutPNG)
Activity.SetActivityResult(-1, in)
Activity.Finish

Here are subs GetAppIcon (used to see what size icon the OS used) and CreateScaledBitmap, which I am trying to use to ensure the icon is the correct size.
B4X:
Sub GetAppIcon As Bitmap

Try
Dim pm As PackageManager, temp As BitmapDrawable, temp2 As Bitmap, sMsg As String, ans As Int
temp = pm.GetApplicationIcon(ShortcutPackage) 'directvremotepro
Return temp.Bitmap
Catch
End Try

End Sub
Sub CreateScaledBitmap(Original As Bitmap, Width As Int, Height As Int, Filter As Boolean) As Bitmap
Dim r As Reflector
Dim b As Bitmap
b = r.RunStaticMethod("android.graphics.Bitmap", "createScaledBitmap", _
Array As Object(Original, Width, Height, Filter), _
Array As String("android.graphics.Bitmap", "java.lang.int", "java.lang.int", "java.lang.boolean"))
Return b
End Sub

My results are in the screenshot. This is on my EVO 4G which Android reports as scale 1.5

The icons in question are the ones that say CC On. The large one was the result of trying to set the size to 72x72. The one that is too small (just below the large one) is the result of setting the size to 48x48. Over to the left of the small one is what it should look like.

I was able to get it working on some devices (such as my EVO) but that code produced a humongous icon on some AVDs. It was so large that all you saw was the upper-left corner or the orange button. Basically it was a quarter of the button in a pie wedge shape. What works on my EVO is if I just set the size to 96x96 and use the 96x96 icon file. The EVO reports its scale as 1.5 with a shortcut icon size of 72x72.
 

Attachments

  • IconProblem.jpg
    IconProblem.jpg
    38.9 KB · Views: 248
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
For example, the following works on an AVD set to WVGA854 (265 density) but is way over-sized on WVGA800 (252 density). Both are considered scale 1.5 and both report the app's icon size as 72x72. They are both loading a 96x96 png.

B4X:
Select Case dScale
Case 1
scOutputSize = 48
imageFN = imageFN & "_h.png"
Case 1.5
scOutputSize = 96
imageFN = imageFN & "_h.png"
Case Else
scOutputSize = 96
imageFN = imageFN & "_h.png"
End Select

'tImg.InitializeMutable (scSize, scSize)
tImg = LoadBitmapSample (File.DirAssets, imageFN, scSize, scSize)
If scOutputSize <> 96 Then
tImg = Common.CreateScaledBitmap (tImg, scOutputSize, scOutputSize,True)
End If
OutPNG = tImg
 
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
Anyone have any ideas? I've just about got this project wrapped up but this issue has me at a dead stop. :(

Is there a way I can pass a file in the resource folder to the shortcut intent? If so, if I were to put the different icon sizes in the "res/drawable-hdpi", "res/drawable-mdpi" and "res/drawable-ldpi" folders then write-protect them (to keep B4A from writing over) would Android then choose the correct file for me?

:sign0085:
 
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
I stumbled on a library to retrieve drawables but it's not working for me. Anyone have any idea what I'm doing wrong?

This uses the AndroidResources library...

B4X:
Dim OutPNG As Bitmap, tImg As BitmapDrawable, tImg2 As BitmapDrawable, tbmp As Bitmap
tImg = aResource.GetApplicationDrawable (imageFN)
Dim bitmapValid As Boolean
 
Try
tbmp.Initialize3 (tImg.bitmap)
bitmapValid = True
Catch
bitmapValid = False
End Try
 
If bitmapValid = False Then
Log ("Bitmap resource not found: " & imageFN)
' Do nothing
Else
Try
ToastMessageShow ("Applying " & imageFN & " to shortcut...", True)
OutPNG = tbmp
bitmapValid = True
Catch
Log ("Error applying bitmap resource: " & LastException)
bitmapValid = False
End Try
End If
 
'ToastMessageShow ("Device scale is " & dScale & CRLF & "App shortcut icon size is " & tbmp.Height & "h X " & tbmp.Width & "w" & CRLF & "Shortcut icon size assigned was " & OutPNG.Height & "h X " & OutPNG.Width & "w", True)
End If
 
Dim shortcutIntent As Intent
shortcutIntent.Initialize("", "")
shortcutIntent.SetComponent(Common.ShortcutPackage & "/.shortcutexec")
shortcutIntent.PutExtra("from_shortcut", True)
shortcutIntent.PutExtra("sc_type", SCType)
shortcutIntent.PutExtra("sc_value", SCValue)
shortcutIntent.PutExtra("sc_destination", SCDestination)
shortcutIntent.PutExtra("sc_toast", SCToast)
shortcutIntent.PutExtra("sc_notification", SCNotification)
shortcutIntent.PutExtra("sc_extra1", "")
shortcutIntent.PutExtra("sc_extra2", "")
Dim in As Intent
in.Initialize("", "")
in.PutExtra("android.intent.extra.shortcut.INTENT", shortcutIntent)
in.PutExtra("android.intent.extra.shortcut.NAME", txtName.Text)
'in.PutExtra("android.intent.extra.shortcut.ICON", OutPNG)
If bitmapValid = True Then in.PutExtra("android.intent.extra.shortcut.ICON", OutPNG)
Activity.SetActivityResult(-1, in)
Activity.Finish
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
You can use this code to get a custom drawable:
B4X:
Sub GetDrawable(ImageName As String) As Object
    Dim r As Reflector
    Dim package As String
    Dim id As Int
    package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
    id = r.GetStaticField(package & ".R$drawable", ImageName)
    r.Target = r.GetContext
    r.Target = r.RunMethod("getResources")
    return r.RunMethod2("getDrawable", id, "java.lang.int")
End Sub

ImageName is the file name without the extension.
 
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
Thank you!!!!!!!!!!!!!!!!!!!!!!!!

Two days wasted trying to get this working and ultimately saved by the master!

:wav:
 
Upvote 0

Woinowski

Active Member
Licensed User
Longtime User
Would you share a fully working example

Hi, I had decided to ignore thid scaling issue, and it seems you have finally solved i. Would you like to share a complete running sample? That'd be great :)
 
Upvote 0

Woinowski

Active Member
Licensed User
Longtime User
late in the evening

Ok, it was too late yesterday, now I get it: you get the size of the launcher icon of the app and then use it ;)
But then a question: Doesn't PackageManager.GetApplicationIcon work the same way?
 
Last edited:
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
Ok, it was too late yesterday, now I get it: you get the size of the launcher icon of the app and then use it ;)
But then a question: Doesn't PackageManager.GetApplicationIcon work the same way?

Actually, I was barking up the wrong tree with the idea of getting the app's icon to see what size it was. It didn't matter if I knew the size and resized another bitmap... it just wasn't scaling.

Assuming you are doing what I was doing (wanting to create a shortcut through code), this is what I did, which was based off of Erel's code that prevented me from pulling that last couple strands of hair out of my head. :D

I don't know if I needed both of these or should have used more, but they work on all tested AVDs, so...........

1) In the Objects\res folder, create the following folders:
drawable-hdpi
drawable-ldpi
drawable-mdpi
drawable-xhdpi

2) Put the following copies of any shortcut icons you want to create in these folders. They must have the exact same name.
drawable-hdpi [72x72 icons (I used 96 DPI)]
drawable-xhdpi [96x96 icons (I used 96 DPI)]

Note: I created folders for ldpi and mdpi but I did not create any icons at the appropriate sizes for those. I wanted to save on space because I had 49 icons needed for each resolution. Using only the two sizes, they seemed to scale fine on everything I tested.

3) IMPORTANT: In Windows, mark these folders and all files inside them as READ-ONLY. Otherwise they will be lost when you compile your project.

And here is the code that retrieves the images:

B4X:
Sub GetDrawable(ImageName As String) As Object
  Dim r As Reflector, package As String, id As Int
  package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
 
  Try
    id = r.GetStaticField(package & ".R$drawable", ImageName)
    r.Target = r.GetContext
    r.Target = r.RunMethod("getResources")
    Return r.RunMethod2("getDrawable", id, "java.lang.int")
      Catch
        Return Null
  End Try
End Sub
 
 
Sub GetPngFromResource (DrawableName As String) As Bitmap
  Dim BitmapDrawable1 As BitmapDrawable
  Dim Object1 As Object
  Object1 = GetDrawable(DrawableName)
  If Object1 = Null Then
    'Log("Drawable NOT FOUND: " & DrawableName)
    Return Null
      Else
        BitmapDrawable1=Object1
        'Log("Retrieved Drawable: " & DrawableName)
        Return BitmapDrawable1.Bitmap
  End If
End Sub

Here is the code that creates the shortcuts. It may be a bit sloppy or not streamlined, but it works. I never claimed to be a professional. :D

B4X:
  Dim OutPNG As Bitmap, tbmp As Bitmap, bitmapValid As Boolean
 
  Dim imageFN as String
 
  imageFN = "Filename_of_icon_but_without_the_extension"
  ' I.E. If the filename is "sc_icon.png" then use "sc_icon"
 
 
  bitmapValid = False
  tbmp = GetPngFromResource (imageFN)
 
  Try
    OutPNG = tbmp  'Will cause error if bitmap is invalid/null
    bitmapValid = True
      Catch
        Log ("Error applying bitmap resource: " & LastException)
        bitmapValid = False
  End Try
 
 
 
  Dim shortcutIntent As Intent
  shortcutIntent.Initialize("", "")
  shortcutIntent.SetComponent(Common.ShortcutPackage & "/.shortcutexec")
  shortcutIntent.PutExtra("from_shortcut", True)
  shortcutIntent.PutExtra("sc_type", SCType)
  shortcutIntent.PutExtra("sc_value", SCValue)
  shortcutIntent.PutExtra("sc_destination", SCDestination)
  shortcutIntent.PutExtra("sc_toast", SCToast)
  shortcutIntent.PutExtra("sc_notification", SCNotification)
  shortcutIntent.PutExtra("sc_extra1", "")
  shortcutIntent.PutExtra("sc_extra2", "")
  Dim In As Intent
  In.Initialize("", "")
  In.PutExtra("android.intent.extra.shortcut.INTENT", shortcutIntent)
  In.PutExtra("android.intent.extra.shortcut.NAME", txtName.Text)
  If bitmapValid = True Then
    In.PutExtra("android.intent.extra.shortcut.ICON", OutPNG)
      Else
        ToastMessageShow ("Could not find file '" & imageFN & "'. Using default icon.", True)
  End If
  Activity.SetActivityResult(-1, In)
  Activity.Finish

Obviously the .SetComponent and .PutExtra statements are unique to my shortcuts. If the resource file is not found then Android will supply its own generic Android icon.

I hope this helps!

Kevin
 
Last edited:
Upvote 0

Woinowski

Active Member
Licensed User
Longtime User
thank you

Checked the app icon size today and did not work. Got your solution now!
 
Upvote 0

Woinowski

Active Member
Licensed User
Longtime User
Simpler, and Android version issue

Hi Kevin,

the solution with multiple resolutions did not work (set up multiple directories and so on) on Android 2.2 on emulator. After having set up the app icon in resolution dependent directories with name icon.png, I tested this which is easier and works: Just use the apps icon itself:

B4X:
Sub GetPngFromApp As Bitmap
   Dim pm As PackageManager
   Dim bmD As BitmapDrawable
   Dim bm As Bitmap
   bmD = pm.GetApplicationIcon(packageName) ' That's a constant with my package name 
   bm = bmD.Bitmap
   Return bm
End Sub

For API Levels 10 or lower (you can check this with the reflection library), I use a standard 48x48 picture. Heres the code to get the API Level

B4X:
Sub GetApi As Int
   Dim r As Reflector
    Return r.GetStaticField("android.os.Build$VERSION", "SDK_INT")
End Sub

Actually, I have not checked every API level between 10 and 15 (4.0.3). Maybe it's not 10 but 11 or so that's the point. At least with 10 or lower, no icon appears if I try to get something from the app.
 
Last edited:
Upvote 0

Kevin

Well-Known Member
Licensed User
Longtime User
Interesting results. Using my standard app icon (72x72, I believe), I had very mixed results as shown in my screenshot when trying different APIs and resolution/density settings. My solution posted above worked on every one I tested.

I'm not sure why it didn't work for you but I apologize for sending you down the wrong track in the wrong direction.

I really hate inconsistent results! Drives me nuts! :D
 
Upvote 0

webjefe

Member
Licensed User
Longtime User
How to retrieve a Drawable

:BangHead:
You can use this code to get a custom drawable:
B4X:
Sub GetDrawable(ImageName As String) As Object
    Dim r As Reflector
    Dim package As String
    Dim id As Int
    package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
    id = r.GetStaticField(package & ".R$drawable", ImageName)
    r.Target = r.GetContext
    r.Target = r.RunMethod("getResources")
    return r.RunMethod2("getDrawable", id, "java.lang.int")
End Sub

ImageName is the file name without the extension.

I can run this code with no errors.
Next step. How do I assign it to an ImageView.

Thanks
 
Upvote 0
Top