Android Question Highlight or enlarge or set focus to "active" button in Android TVs

luisftv

Member
Licensed User
Longtime User
Hi,

I searched (maybe not deep enough) through the forum about this question but I didn't find anything.

I recently bought a Sony Android TV with the intention to use the apps I made with B4A. They work flawlessly! Thanks Erel! Your programming languages are awesome.

Now the question...

On the Sony Android TV, the icon or button I am about to press (like the Netflix icon, or the TV Settings icon/button, etc) becomes highlighted or enlarged as I navigate through the options. My apps do not react that way. Once my app is running, inside my app, I have to guess which button I am about to press by counting how many times I press the remote control button (either left or right or up or down).

How or what's the code to make the buttons on my apps show a "highlight" or "Enlarge" or something indicating that if I tap, click, or press "that" button it will activate, and remain highlighted or enlarged (indicating where the cursor or active button is) until I press another button, just like all other apps in the Android TV do?

By the way, I noticed that many apps on phones/tablets do have this "active" or "current" button status highlighting or enlarging feature. How do they do it?

Thanks in advanced.
 

DonManfred

Expert
Licensed User
Longtime User
I would try to use the FOCUS Event to know which button has got the focus...

And then, when a button gets the focus i would try to give the button another color or slightly change its size.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
Here is a sample of my code already working and in place.

I am trying to set into focus (by a highlight around the box, or by changing color, or by enlarging, etc.) Sub Help_Click, Sub GN_Click, Sub GC_Click, and Sub Exit_Click... as I enter the layout. In other words, if I start my app, as I see the main screen, I need one of the options or buttons to be already highlighted or set into focus to let me know what will be activated if I press the enter button on my TV remote control, or which button is being highlighted as I move the "cursor" with the remote control.

B4X:
Sub Process_Globals
End Sub

Sub Globals
    Dim Exit As Button
    Private Help As Button
    Private GN As Button
    Private GC As Button
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("gmain")
End Sub

Sub Help_Click
    StartActivity("Help1")
End Sub

Sub GN_Click
    StartActivity("G_N")
End Sub

Sub GC_Click
    StartActivity("G_C")
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub Exit_Click
    ExitApplication
End Sub


NOTE: for example, the Sub Help_Click is a button on my main layout that loads another layout. I need the Sub Help_Click button to be already highlighted or shown focus of as I enter the screen... and if I move with the remote to Sub GC_Click, another button on my layout, then that one will be brought into focus waiting for me to press enter with my remote control for the TV.

Thanks.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
RequestFocus seems to work only with EditText items... I've tested it in multiple devices.

Seriously, isn't there a way to at least change the color of a "BUTTON" or to put a hallo around it to show it as the current active button just like Microsoft Excel does to the active cell? In MS Excel, the active cell is noticed by a black rectangle around the cell. It does not mean that the cell is being used/edited at the moment. It only shows where the "cursor", if you will, is located among all the cells. Picture this: You enter the app, and then you notice that one of the buttons is glowing, or has a white frame around it, or the text on the button is changing colors. This will tell you which button would be activated if pressing "Enter" on the TV remote control. How can this be done?

Listdrawable only seems to work with EditText... I am using "buttons" only. Listdrawable does the trick to set order when using tabs, so that while using the remote control the EditText boxes go in the desired sequence.

Any more ideas? For a button, that is... not an EditText.

Thanks.

p.s. If it cannot be done (at the moment with the current version of B4A) it is fine, but please let me know. Then I'll move on to other... features.
 
Upvote 0

sonicmayne

Member
Licensed User
Longtime User
Call the following sub with the Button you want to focus.

For example: FocusButton(Help)

Requires the JavaObject library

B4X:
Sub FocusButton(Button As Button)
    Dim JO As JavaObject = Button
    JO.RunMethod("setFocusable",Array As Object(True))
    JO.RunMethod("setFocusableInTouchMode",Array As Object(True))
    Button.RequestFocus
End Sub
 
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Longtime User
In a library that I intend to release some time soon (hopefully), I use the following routine to change the border colour of the selected item. By using this method I can make an item selected even of it has not been clicked by the user i.e. given the focus. I'm using Labels because my requirement is different to yours but it will your just the same for Buttons.
B4X:
Private Sub setActivePallet(intIndex As Int)
    Dim lbl As Label
    Dim cd As ColorDrawable
    Dim intColor  As Int

    ' Store index of selected user pallet
    intActiveUserPallet = intIndex

    ' Loop through each user pallet to set/reset border colour
    For intIndex = 0 To mapUserColors.Size - 1
        intColor = mapUserColors.GetValueAt(intIndex)
        lbl = mapUserColors.GetKeyAt(intIndex)
        If lbl.Tag = intActiveUserPallet Then
            ' Highlighted border of selected pallet
            cd.initialize2(intColor, 5dip, 2dip, Colors.Yellow)
            intARGB = getARGBColor(intColor)
        Else
            ' Reset border color if not selected
            cd.initialize2(intColor, 5dip, 1dip, Colors.Red)
        End If
        lbl.Background = cd
        mapUserColors.Put(lbl, intColor)
    Next
End Sub

Hopefully you get the idea from the code above, this is my actual working code which is for a user colour pallet and so not only sets the border but also the inner panel colour based on what it was previously or what the user has just changed it too.
The basic idea is simple though, I use the Tag to identify which item I want to be highlighted. I then loop through each View and if the Tag matches the selected index then I make the border Yellow and for all others I make the border red.
The beauty of doing it this way is that the item does not necessarily have to have the Focus, it can be selected by code.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
sonicayne & RandomCoder, I'm experimenting now with both your suggestions... to no good result. I'm betting is my fault, as usual.

RandomCoder, what libraries are required? The code you provided goes red all over the place.

I attached here a sample project with sonicayne sugestion... but it is not working on my devices or emulators.

I also researched the suggestions of DonManfred but I got nowhere, for what I need...
 

Attachments

  • MyApp.zip
    430.3 KB · Views: 487
Upvote 0

sonicmayne

Member
Licensed User
Longtime User
In the sample you uploaded, you are never calling the sub, you have got the code, but you're missing this:

B4X:
FocusButton(Help)

Which means the code is never called.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
I had tried that.

Here is the sample again with the - FocusButton(Help) - enabled.

Please, disregard the extra code... I have been trying many options. I left in place the code for Ripple Effect, which I will use in another Activity.

I also changed the color of the "Help" button to blu-ish while working on the Ripple Effect.


I also think that I am missing something else... on your code
B4X:
Sub FocusButton(Button As Button)

The (Buttons As Button) are both sort of green... on my end, only the second one is green.


I appreciate your help. I do.
 

Attachments

  • MyApp.zip
    438.3 KB · Views: 485
Last edited:
Upvote 0

sonicmayne

Member
Licensed User
Longtime User
This is the same as before, except it'll log if the button has focus or not.

B4X:
Sub SetUpOnFocusListener(Button As Button, EventName As String)
    FocusButton(Button) 'This will let us focus the button.
    Dim JO As JavaObject = Button
    Dim event As Object = JO.CreateEvent("android.view.View.OnFocusChangeListener",EventName,False)
    JO.RunMethod("setOnFocusChangeListener",Array As Object(event))
End Sub

Sub Help_Event(MethodName As String, Args() As Object)
    Log("Button has focus: " & Args(1)) 'Args(1) is True if button has focus, false otherwise.
End Sub


Call:
B4X:
SetUpOnFocusListener(Help,"Help")
 
Upvote 0

RandomCoder

Well-Known Member
Licensed User
Longtime User
I've noticed from your sample that you are using a GradientDrawable for each state and with this you cannot set the border colour. Therefore I've come up with what I think would be a nice solution. You could add each Button and Panel to a Panel placed behind the view but slightly larger and set the panel Background to be transparent when not selected then show the panel when selected. With this approach you could have a really nice halo effect glowing around the Button or Panel.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
That's another great suggestion...

I'll put for now here the code, but later (in a few days) I'll also give a good and final working sample. I found the solution based on the explanation RandomCoder gave in another post. sonicmayne and DonManfred also contributed with their suggestion... after trying all suggestions and examples, and really trying them on emulators and real devices, specially Android TVs, I've found the following and simple solution.

Remember, this thread was about bringing into focus, or showing by highlighting, or changing color, or enlarging, or putting a border, or by any means necessary, which button on the Android TV was the "cursor" resting upon. As you know, with the TV control remote, you can navigate the Home menu, select Netflix, or YouTube, or go to the settings, or online with the internet browser, and it shows by enlarging the icon where the cursor is resting upon... but on my apps, you couldn't tell which button withing my app you were moving to with the cursor/focus (on regular phones and tablet, it is not needed since all you do is tap the needed button).

The solution was, as sonicmayne suggested at the very beginning, to use a StateListDrawable. The problem was I didn't know what that was at the time and all the examples in the forum were not meant towards my particular problem (Android TVs or D-pads). Also, that StateListDrawable had to use the "Focused" command just as DonManfred suggested. Again, I didn't know what that was or how to use it. Now I have a pretty good general idea after experimenting with all the suggestions. I then stumble upon an example by Erel which I couldn't decipher (some threads are for experts, as you know, and I'm in diapers here, as you can see), but almost at the end of it, LucaMs showed his way of using Erel's code which was easier for me to "read" through... and then RamdomCoder kindly offered an awesome explanation that finally put my issue to rest...

Anyways, since I bet you cannot wait... just like me... here is the work in progress code and sample.

NOTE: If the button is not a DefaultDrawable in the B4A Designer, then this code will override the button's settings and show it as a DefaultDrawable after compile. It will not show the changes to the linked emulator in real time.

Libraries needed: Core, Phone, Accessibility. If you want the ripple effect on buttons: RippleEffect (by the way, you can only see the ripple effect on the Android TV when using a Bluetooth mouse; pressing the D-Pad or remote control will not show it; this effect will also work if using .png .jpg etc)

In the Manifest...

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14"/>
<uses-feature android:name="android.hardware.gamepad" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
'SetApplicationAttribute(android:hardwareAccelerated, "false")
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.
AddPermission(com.android.launcher.permission.UNINSTALL_SHORTCUT)
'SetApplicationAttribute(android:theme, "@android:style/Theme.Holo")


In the B4A code editor:

B4X:
#Region  Project Attributes
   #FullScreen: False
   #IncludeTitle: True
   #ApplicationLabel: My App
   #VersionCode: 1
   #VersionName:
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
   '#DebuggerForceStandardAssets: true
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
#End Region

Sub Process_Globals

End Sub

Public Sub CreateButtonColor(FocusedColor As Int, EnabledColor As Int, DisabledColor As Int, PressedColor As Int) As StateListDrawable

   Dim res As StateListDrawable
   res.Initialize
   Dim drwFocusedColor, drwEnabledColor, drwDisabledColor, drwPressedColor As ColorDrawable

   'drwFocusedColor.Initialize2(FocusedColor, 5, 0, Colors.LightGray) 'border roundness, thickness, and color on Android TV
   'drwEnabledColor.Initialize2(EnabledColor, 5, 0, Colors.DarkGray)
   'drwDisabledColor.Initialize2(DisabledColor, 5, 0, Colors.White)
   'drwPressedColor.Initialize2(PressedColor, 5, 0, Colors.Black)

   drwFocusedColor.Initialize2(FocusedColor, 0, 1, Colors.RGB(110,110,110))
   drwEnabledColor.Initialize2(EnabledColor, 0, 0, Colors.RGB(110,110,110))
   drwDisabledColor.Initialize2(DisabledColor, 0, 0, Colors.RGB(110,110,110))
   drwPressedColor.Initialize2(PressedColor, 0, 0, Colors.RGB(110,110,110))

   res.AddState(res.State_Focused, drwFocusedColor)
   res.AddState(res.State_Pressed, drwPressedColor)
   res.AddState(res.State_Enabled, drwEnabledColor)
   res.AddState(res.State_Disabled, drwDisabledColor)
   res.AddCatchAllState(drwEnabledColor)
   Return res

End Sub

Sub Globals

   Private CreateShortcut As Button
   Private Help As Button
   Private Exit As Button
   Private Panel1 As Panel

End Sub

Sub Activity_Create(FirstTime As Boolean)

   Activity.LoadLayout("myappmain")

   Dim re1 As RippleView 'RippleEffect for buttons that stay on same screen
   re1.Initialize(CreateShortcut, Colors.Blue, 300, False) 'True will center the ripple, False will start the ripple where tapped

   Dim re2 As RippleView
   re2.Initialize(Help, Colors.Blue, 300, False)

   Dim re3 As RippleView
   re3.Initialize(Panel1, Colors.Green, 300, False)

   'ExitB.Background = CreateButtonColor(Colors.RGB(0,51,102), Colors.RGB(0,33,88), Colors.White, Colors.RGB(0,76,153)) '<--from Public Subs
   'Help.Background = CreateButtonColor(Colors.LightGray, Colors.DarkGray, Colors.White, Colors.Black) '<--from Public Subs
   'Help.Background = CreateButtonColor(Colors.RGB(96,96,96), Colors.DarkGray, Colors.White, Colors.RGB(120,120,120)) '<--from Public Subs
   Help.Background = CreateButtonColor(Colors.RGB(0,51,102), Colors.RGB(0,33,88), Colors.White, Colors.RGB(0,76,153)) '<--from Public Subs
   CreateShortcut.Background = CreateButtonColor(Colors.RGB(0,51,102), Colors.RGB(0,33,88), Colors.White, Colors.RGB(0,76,153)) '<--from Public Subs

End Sub


Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub Help_Click
   StartActivity("Help1")
End Sub

Sub Exit_Click
   ExitApplication
End Sub

Sub CreateShortCut_Click

   Dim shortcutIntent As Intent
   shortcutIntent.Initialize("", "")
   shortcutIntent.SetComponent("My.App/.main") '<------change My.App for your own app file name here, like "My.Game", look into Tools, Build Configurations for your app's name

   Dim in As Intent
   in.Initialize("", "")
   in.PutExtra("duplicate",False)
   in.PutExtra("android.intent.extra.shortcut.INTENT", shortcutIntent)
   in.PutExtra("android.intent.extra.shortcut.NAME", "My App") '<-----Type here the name of the shortcut, "My App" for "Your App"
   in.PutExtra("android.intent.extra.shortcut.ICON", LoadBitmap(File.DirAssets, "color01.png")) '<----your app image here, "color01.png" for "yourpic.png"
   in.Action = "com.android.launcher.action.INSTALL_SHORTCUT"

   Dim p As Phone
   p.SendBroadcastIntent(in)
   DoEvents

End Sub


Now, on the Android TV or phones with a D-Pad, the buttons show where the cursor is. The - drwFocusedColor.Initialize2(FocusedColor, 0, 1, Colors.RGB(110,110,110)) - code allows for a border of any color to be added (the 0, 1, part) to the button with no need for an additional panel underneath, and also allows control of the button corner, and you can also use any color you want and not just the ones on the pup up that comes up as you type.

I'll include here the sample I'm using now, but it is rough... it will not resize properly on large screens because I have not made the layouts for them... but it will still show that the buttons now work/focus properly.

I could have not found the solutions without the amazing assistance of (in order of appearance) DonManfred, sonicmayne, RandomCoder, Erel, and LucaMs. Thank you, thank you, thanks a million.

I guess not every one cares about apps for Android TVs yet...

QUESTION: How do you make this code work with .png or .jpg. or gif, etc? If it is not too much trouble, can you provide a working project sample too? I only need the border around the .png or .jpg Can the - drwFocusedColor.Initialize2(FocusedColor, 0, 1, Colors.RGB(110,110,110)) - be used only to apply the border? How?

Anyways, now to my next dilemma: Android TVs home-screen shortcuts... (I know, I know, in another thread...).

Thank you all again.

p.s. I'm extremely pragmatic... I like short solutions (if possible). If any one knows a shorter one, please, I beg you, share a working sample.
 

Attachments

  • MyApp.zip
    469.9 KB · Views: 462
Last edited:
Upvote 0

luisftv

Member
Licensed User
Longtime User
I tried the Panel idea... worked on the phones and tablets and the emulators. But it did not work on the Android TV (the actual-real TV).

But, after digging deeply through the forum, I found some code posted by "strat" that has finally put the issue to rest...

However, I need help understanding how a section of the code works.

Here is the situation. I have 5 buttons:

Help Button (Button)
Exit Button (Button)
Button1 (Button)
Button2 (Button)
Text Button (Edit Text)

Not to complicate my explanation, I only need to understand the section that applies to Button1 and Button2, which are the only two buttons using the code.

With those two buttons (Button1, Button2), I need them to have: Focused - Enabled - Pressed - Disabled

I have four images, one for each state.

For Focused: Metal_Red_Brushed_01_1.jpg <---red copper
For Enabled: Brushed_Copper.jpg <---brown copper
For Pressed: Brushed_Steel_Blue_01.jpg <---blue copper
For Disabled: Brushed_Steel_Black_09.jpg <---black copper

On a phone or tablet, the default state for buttons is Enabled and it will show the corresponding brown copper image. If you tap (Pressed state) on that same button, then it will flicker for a moment with another image (blue copper). If the button is Disabled, then the black copper image will be shown.

On an Android TV, in order to see where the "cursor" is, I need the Focused state, which is the red copper image.

The code I need to understand how it works is this:

B4X:
    Dim states(4) As Int
    states(0) = stdBitmap.state_Focused
    states(1) = stdBitmap.state_Enabled
    states(2) = -stdBitmap.state_Pressed
    states(3) = -stdBitmap.state_Disabled
    stdBitmap.addState2(states, bdwFocused)

    Dim states(2) As Int
    states(0) = stdBitmap.state_Enabled
    states(1) = -stdBitmap.state_Pressed
    stdBitmap.addState2(states, bdwEnabled)

    Dim states(1) As Int
    states(0) = stdBitmap.state_Pressed
    stdBitmap.addState2(states, bdwPressed)

    btn.background=stdBitmap


Which is found at the bottom of this code:

B4X:
Sub Buttondraw(btn As Button,btntxt As String,fns As Int,cl As Int)

   Dim bx,by As Int

   Dim bdwFocused As BitmapDrawable

   Dim bdwEnabled As BitmapDrawable

   Dim bdwPressed As BitmapDrawable

   Dim bdwDisabled As BitmapDrawable

   buttonlabel.Text=btntxt
   buttonlabel.TextSize=fns

   Dim Height_, Width_ As Int
   buttonlabel.Width = -2
   buttonlabel.Height = -2
   DoEvents

   obj.Target = buttonlabel
   Width_ = obj.RunMethod("getWidth")
   Height_ = obj.RunMethod("getHeight")
   buttonpic.Width=btn.Width
   buttonpic.Height=btn.Height

'================This will show text on the button
'   buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Brushed_Steel_Blue_01.jpg") '?????? Not needed
   c.Initialize(buttonpic)
   bx=(btn.Width-Width_)/2
   by=(btn.Height+fns)/2
   c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
'================

'This section inludes: FOCUSED, ENABLED, PRESSED, & DISABLED

   bdwEnabled.Initialize(buttonpic.Bitmap) 'It will not work without this code HERE.

   Dim Focused As BitmapDrawable
   buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Metal_Red_Brushed_01_1.jpg")
   c.Initialize(buttonpic)
   c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
   bdwFocused.Initialize(buttonpic.Bitmap)

   bdwEnabled.Initialize(buttonpic.Bitmap)  'It will not work without this code HERE.

   Dim bdwEnabled As BitmapDrawable
   buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Brushed_Copper_06.jpg")
   c.Initialize(buttonpic)
   c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
   bdwEnabled.Initialize(buttonpic.Bitmap)

   Dim bdwPressed As BitmapDrawable
   buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Brushed_Steel_Blue_01.jpg")
   c.Initialize(buttonpic)
   c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
   bdwPressed.Initialize(buttonpic.Bitmap)

   Dim bdwDisabled As BitmapDrawable
   buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Brushed_Steel_Black_09.jpg")
   c.Initialize(buttonpic)
   c.DrawText(btntxt,bx,by,myfont, fns, Colors.Gray, "LEFT")
   bdwDisabled.Initialize(buttonpic.Bitmap)

   Dim stdBitmap As StateListDrawable
   stdBitmap.Initialize

   Dim states(4) As Int
   states(0) = stdBitmap.state_Focused
   states(1) = stdBitmap.state_Enabled
   states(2) = -stdBitmap.state_Pressed
   states(3) = -stdBitmap.state_Disabled
   stdBitmap.addState2(states, bdwFocused)

   Dim states(2) As Int
   states(0) = stdBitmap.state_Enabled
   states(1) = -stdBitmap.state_Pressed
   stdBitmap.addState2(states, bdwEnabled)

   Dim states(1) As Int
   states(0) = stdBitmap.state_Pressed
   stdBitmap.addState2(states, bdwPressed)

   btn.background=stdBitmap

End Sub

FYI: You call upon the Sub BottonDraw in the Sub Activity_Create(FirstTime As Boolean) with this other code that puts it onto the button:

B4X:
Sub Activity_Create(FirstTime As Boolean)

    Activity.LoadLayout("myappmain")

'==========This section calls the "Sub BottonDraw..."
    myfont = Typeface.LoadFromAssets("ardelaney.ttf")
    buttonpic.Left=3000
    buttonlabel.Left=3000
    Buttondraw(Button1,"",18,Colors.Black) 'NOTE: This button's tile was added in the designer as an alternative
    Buttondraw(Button2,"B2",18,Colors.Black)
   'Usage : Buttondraw(Button_name ,Button Text,Font size,Font color)
'==========

End Sub


Also, why is it that this code will not work on an exit routine button like this one:

B4X:
Sub Exit_Click
    ExitApplication
End Sub


If the button is called "Exit", the "Sub Buttondraw" will work, but the Exit button does nothing, it will not exit the app... but the bottom has to be called "Exit" for the "ExitApplication" to execute properly!!! Don't apply the Sub Buttondraw to the Exit button and it works as intended, it exits the app. Why?

Also, with this code, how do you go on adding a border, of any color, to the buttons?

Can you please help me understand this? Any one?

Thanks in advance.
 
Last edited:
Upvote 0

luisftv

Member
Licensed User
Longtime User
NOTE: Rename the attached file "MyApp.zip" to "Myapp.7z" and use 7zip to extract. 7zip is a free open source archiver - the file was too big and the zip format didn't do, and this forum didn't allow .7z extension files to be attached.


I love sharing, seriously... a member asked me a question today through this forum in a private or direct "conversation" message, and I couldn't fine a button in the conversation reply window to upload a sample code showing a working code... I was going to post it here eventually anyways, so, I will post my replay to him here, and if any one find a more pragmatical/direct way of doing this, please please, share it with me... life it too too too short to waste it in long procedures, specially since Erel has already added so many new awesome features to B4A since the time I found this solution(s).

In reply to that members' question:


As you know, there are two type of buttons: drawable, and using an image. Drawable is when you create a box and apply a color to it and then you assign the property of a button, to be used as a button.

So, the first thing is to decided what kind of buttons you "need". On the Android TVs, the drawable buttons can have colors or borders (directly controlled by code in the designer) to show the focus or active button or normal buttons. You can also have effects, like ripple... but the effects only show when using a mouse to click on the TV buttons... other wise, using the D-pad from the control remote will not show the extra effect on drawable buttons, at least not for now... maybe in a future version of B4A. The only drawback of using drawables is that you can only use colors, not images.

Now, if you want to use images for buttons, the settings (code) for drawables don't apply here. It's a different way of doing it. For starters, to show the active button using images, you have to have 2 different images for the same button, one in the normal color (not being active, not focused) and one for when it becomes active with the D-Pad (if you also want to show the disable state, then you need a third image). Sometimes, that's not enough, and you have to have a slim border around the image to show it as active. In this case, for the second image, the one to be used as being focused on, you must add the border with an image editor (outside of B4A) such as GIMP or Paint Shop Pro or Serif Photo Plus or Xara Designer Pro.

When using images, you also must add on the virtual designer an extra box to be use as the title (label) for the button label and another extra box to "push" or cycle the different images. I bet I don't make sense, right? Say you have two buttons, button-1 and button-2. Each button will show two or three different states (normal, active, and disable - for example) by switching or displaying 3 different images. Well, it will not happen directly on the button but rather "pushed" onto the button by another button that will gather the image. Are you familiar with Databases, like Microsoft Access? It's the same as a query. The table is the main object of the database, but a query simply collect the data from the table and sorts it the specific way you have programmed it to do, each and every time, without doing the actual sorting on he table... although it could be done directly on the table, it is best to use a query. Why? Because using a query allows the table to be in "raw" state so that other queries can use it immediately in any way they want. If you sort the table directly, you first must clear the sorting or filter criteria before you can do a new sort/filter... that's time consuming and only allows on sort/filter "job" at a time. Leave the table alone, and use queries, which gather the data from the table and present it using the filters applied, and you can have as many queries as you need/like... So, those extra boxes that I'm talking about act like the queries in a database. They will gather the title or the image and then push it onto the button.

I apologize if I don't make much sense right now... I'm just back from work and really exhausted. I was going to bed as a matter of fact.

So, there you have it. Now I'll give you a working (crude) code sample. See attached. The example I give you here is working as of right now. I just tested it. If it does not work for you, then check the version of Java (i'm using jdk1.8.0.60) and android.jar (I'm using ver 22) in the B4A, under "Tool", "Configure Paths". It will not work if you are using older versions. The B4A version I just used is 5.80.

The sample app I'm giving here, although crude, has every thing you need (code wise, the images will be your own, I mean, you will replace my images here with yours in your apps) to make it work just nice in an Android TV or a Smart TV. Analyze every bit of the code, and then modify it to your needs. Just rename the buttons and copy those new names onto the appropriate code sections in the designer, etc.

The app sample covers:
1. Button focus for drawables or images
2. It will also show the App Icon on the TV menu (only tested with Android TVs though)
3. This app has a button that makes a home icon on regular Android devices (phones and tablets) - I thought to make this sample app cover as many "needed" options as possible, and which could be applied to all other apps.
4. It has it's own exit button, to force the app to unload from the device memory.
5. The buttons have a ripple effect when tapped (on phones and tablets). On TVs, the ripple effect will only show when using a Bluetooth mouse.

I'm sorry that at this moment I cannot post the actual code and and make a quick tutorial, but I'm really exhausted... However, better than posting the code and you trying to figure out how to apply it to your app, is having a short sample app than you can compile and see it in action.

I hope I was of some help. Please, share your findings and new ideas/ways on the subject. As little as you can share is of BIG help. Take care.


NOTE: Rename the attached file "MyApp.zip" to "Myapp.7z" and use 7zip to extract. 7zip is a free open source archiver - the file was too big and the zip format didn't do, and this forum didn't allow .7z extension files to be attached.
 

Attachments

  • MyApp.zip
    435.4 KB · Views: 503
Last edited:
Upvote 0

CyclopDroid

Well-Known Member
Licensed User
Longtime User
Hi Luisftv.
Has it ever happened that your Apps ending, with no errors in the log, on your TV? In the game I'm doing, on smartphone works without problems, while on the TV ends randomly. :(
Here, my topics for help request.
 
Upvote 0

bobsimoneau

Member
Licensed User
Longtime User
I am still not getting it. The app I am trying to convert to Fire TV is: https://play.google.com/store/apps/details?id=simoneau.apps.Animals
You tap on an animal and it plays the sound for that animal. The application was written with each animal on a imageview. I have now changed to images on buttons. I took each picture and created a black and white version for when the button has focus and color when it does not. I need a way to tell which button has focus while using a D-Pad.
 
Upvote 0

luisftv

Member
Licensed User
Longtime User
The app sample I gave you covers and DOES exactly what you want.

However, is not as simple as copy/paste my code into your app. Based on what you described, the trick is in the sections:

Public Sub CreateButtonColor(FocusedColor As Int, EnabledColor As Int, DisabledColor As Int, PressedColor As Int) As StateListDrawable

and

Sub Buttondraw(btn As Button,btntxt As String,fns As Int,cl As Int)

of the code I gave you in the "MyApp."


Here are the codes of those sections, which are found in the "Main Activity Window":

This is for DRAWALBES

B4X:
Public Sub CreateButtonColor(FocusedColor As Int, EnabledColor As Int, DisabledColor As Int, PressedColor As Int) As StateListDrawable

    Dim res As StateListDrawable
    res.Initialize
    Dim drwFocusedColor, drwEnabledColor, drwDisabledColor, drwPressedColor As ColorDrawable

    'drwFocusedColor.Initialize2(FocusedColor, 5, 0, Colors.LightGray) 'border roundness, thickness, and color on Android TV
    'drwEnabledColor.Initialize2(EnabledColor, 5, 0, Colors.DarkGray)
    'drwDisabledColor.Initialize2(DisabledColor, 5, 0, Colors.White)
    'drwPressedColor.Initialize2(PressedColor, 5, 0, Colors.Black)

    drwFocusedColor.Initialize2(FocusedColor, 0, 1, Colors.White) 'border roundness, thickness, and color on Android TV
    drwEnabledColor.Initialize2(EnabledColor, 0, 0, Colors.RGB(110,110,110))
    drwDisabledColor.Initialize2(DisabledColor, 0, 0, Colors.RGB(110,110,110))
    drwPressedColor.Initialize2(PressedColor, 0, 0, Colors.RGB(110,110,110))

    res.AddState(res.State_Focused, drwFocusedColor)
    res.AddState(res.State_Pressed, drwPressedColor)
    res.AddState(res.State_Enabled, drwEnabledColor)
    res.AddState(res.State_Disabled, drwDisabledColor)
    res.AddCatchAllState(drwEnabledColor)
    Return res

End Sub


and...

THIS IS FOR IMAGES


B4X:
Sub Buttondraw(btn As Button,btntxt As String,fns As Int,cl As Int)

    Dim bx,by As Int

    Dim bdwFocused As BitmapDrawable

    Dim bdwEnabled As BitmapDrawable

    Dim bdwPressed As BitmapDrawable

    Dim bdwDisabled As BitmapDrawable

    buttonlabel.Text=btntxt
    buttonlabel.TextSize=fns

    Dim Height_, Width_ As Int
    buttonlabel.Width = -2
    buttonlabel.Height = -2
    DoEvents

    obj.Target = buttonlabel
    Width_ = obj.RunMethod("getWidth")
    Height_ = obj.RunMethod("getHeight")
    buttonpic.Width=btn.Width
    buttonpic.Height=btn.Height

    '=========== This will show text on the button ===============================
'    buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Brushed_Steel_Blue_01.jpg") '?????? Not needed
    c.Initialize(buttonpic)
    bx=(btn.Width-Width_)/2
    by=(btn.Height+fns)/2
    c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
    '=============================================================================

    '=========== This section inludes: FOCUSED, ENABLED, PRESSED, & DISABLED =====
    bdwEnabled.Initialize(buttonpic.Bitmap) 'It will not work without this code HERE.

    Dim Focused As BitmapDrawable
    buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Metal_Copper_Brown_Brushed_07_Focused.jpg")
    c.Initialize(buttonpic)
    c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
    bdwFocused.Initialize(buttonpic.Bitmap)

    bdwEnabled.Initialize(buttonpic.Bitmap)  'It will not work without this code HERE.

    Dim bdwEnabled As BitmapDrawable
    buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Metal_Copper_Brown_Brushed_01_Enabled.jpg")
    c.Initialize(buttonpic)
    c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
    bdwEnabled.Initialize(buttonpic.Bitmap)

    Dim bdwPressed As BitmapDrawable
    buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Metal_Steel_LightBlue_Brushed_01_Pressed.jpg")
    c.Initialize(buttonpic)
    c.DrawText(btntxt,bx,by,myfont, fns, cl, "LEFT")
    bdwPressed.Initialize(buttonpic.Bitmap)

    Dim bdwDisabled As BitmapDrawable
    buttonpic.Bitmap = LoadBitmap(File.Dirassets,"Metal_Steel_Brushed_08_Disabled.jpg")
    c.Initialize(buttonpic)
    c.DrawText(btntxt,bx,by,myfont, fns, Colors.Gray, "LEFT")
    bdwDisabled.Initialize(buttonpic.Bitmap)

    Dim stdBitmap As StateListDrawable
    stdBitmap.Initialize

    Dim states(4) As Int
    states(0) = stdBitmap.state_Focused
    states(1) = stdBitmap.state_Enabled
    states(2) = -stdBitmap.state_Pressed
    states(3) = -stdBitmap.state_Disabled
    stdBitmap.addState2(states, bdwFocused)

    Dim states(2) As Int
    states(0) = stdBitmap.state_Enabled
    states(1) = -stdBitmap.state_Pressed
    stdBitmap.addState2(states, bdwEnabled)

    Dim states(1) As Int
    states(0) = stdBitmap.state_Pressed
    stdBitmap.addState2(states, bdwPressed)

    btn.background=stdBitmap
    '=============================================================================

End Sub


Take a good look at the bottom of the second code, right where it says: Dim state(4) As Int You will notice that it calls for four (4) different button states: Focused, Enabled, Pressed, and Disabled. You must provide an image for each state, even though you only need 3, normal, focus, and pressed. If you analyse the code, you will find that I am using 4 images:

1. "Metal_Copper_Brown_Brushed_07_Focused.jpg"
2. "Metal_Copper_Brown_Brushed_01_Enabled.jpg"
3. "Metal_Steel_LightBlue_Brushed_01_Pressed.jpg"
4. "Metal_Steel_Brushed_08_Disabled.jpg"

This one I used for the text on the button, but with or without it works... I simply left it there:
1. "Brushed_Steel_Blue_01.jpg"

The naming of my files is too much maybe, but you can rename your files any way you want, but I suggest to add at the end the "Focused, Enabled, Pressed, and Disabled" to make it easy to understand and locate withing the code.

The real trick is in the last sections of the code: Dim States(4) as Int, Dim States(2) As Int, and Dim States(1) as Int

Just keep it the way I have it and simply replace all of my 4 images with your images and it will work just fine. Your Zebra (for example) will be in 4 versions: one for normal state, one when being focus (which is the same one as the normal state, is just that you either add a border with an image editor or change the intensity of the color and save it as a different image), one for when being pressed (the same as the original normal image, but you must edit the photo and make the colors very bright to show that it is being focus - or something to that effect) and save that image as a new file, and maybe a gray zebra for the disabled state even though it might not be used at all depending on the requirements of your app. I'm not using it in the app I gave you. BUT it still needs to be there.

I suggest this: use my sample app and inject your "zebra" images in it, and play with the code until you understand what's going on and get the results you are looking for. Install the sample "MyApp" to the TV to make sure it is working fine. Then, bring more of your own code (browsing through images, going to the next level, setting levels of difficulties, etc,) into my sample app and "grow it" a bit at a time until you get it to work like your app, or the way you want your app to work. It is easier to tackle a sample of the problem than to face the whole thing at once... some other part of your code might be conflicting and you won't know until you work a bit of code at a time. The main objective is to show focus on the SmartTV, done... MyApp does that. Now add to that "sections" of your code, a bit at a time... until the main sections are all working fine. Once you achieve that, go and modify your actual app code. That's the easiest way... less troubleshooting.

Also, I forgot to mention earlier that you must use the following libraries (or it will not work):
Accessibility
Core
JavaObject
Phone
Reflection
RippleEffect

The "RippleEffect" library is only needed if you use the part of the code for the... wait for it... wait for it... ripple effect. If you don't want that, remove that code and don't install the library.


BY THE WAY, have you looked at the code I have in the "Manifest":

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: http://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14"/>
<uses-feature android:name="android.hardware.gamepad" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"
    android:xlargeScreens="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
SetApplicationAttribute(android:theme, "@android:style/Theme.Holo")
SetApplicationAttribute(android:banner, "@drawable/banner")
AddActivityText(TvActivity,
                <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
                </intent-filter>)



As I said, work with the little -that does work- sample app I sent you and analyze the entire code, then, import your own code a section at a time... make one section work, then proceed to the next one... trust me, I do this for a living (not precisely programming, but troubleshoot computer problems of all sorts, including programming).

I'm going to bed now. Ciao.

p.s. Seriously, install the app I gave you onto your TV and make sure it works. I tried it only on Android TVs, not SamSungs or LGs... if it works, now bring your code a bit at a time into my app and grow it... that's the fastest way to find a solution to your dilemma and to understand what's going on. If the app does not work on your TV, well, then I cannot help you since I do not have one of those to try it myself. By the way, I just tried the app in a Sony 4k 55" Android TV and it works flawlessly.

One more time, make 4 distinct images of that Zebra (modify the original in three different ways, each way saved as a different file) with an image editor like Gimp or Adobe Photoshop, etc. That's the final trick (besides the code itself) for showing the image being focused on a SmartTV with the D-Pad, and on regular devices you don't need to show focus since you simply tap, but it can be done too.
 
Last edited:
Upvote 0

luisftv

Member
Licensed User
Longtime User
erosmax...

Yes, that did happened. I solved that by upgrading Java and android.jar to the latest version. At the time when it happened to me, Android TVs were being shipped with the latest versions and I was about a year old. That was an easy fix for me.

But that might not be the only problem, or the problem you have. It can also be an outdated library. It can also be conflict withing your own code.

After spending countless hours troubleshooting and reading through forums, etc, etc, I've come up with the following "habit" to help me troubleshoot and find the problem fast:


I keep samples of individual peaces of code (that work as intended, every time) in dedicated app projects ready to compile.


For example, just an example, I have a little "MyApp" project with an exit button that gives the message "Are you sure you want to quit?" Then, if I want to have that in an app for a SmartTV, I simply combine or import the code that makes the app work in a SmartTV with the one about to exit the app. If they work, now I have two "MyApp" little projects: one for the exit routine (code) and one for making the app compatible with the SmartTVs. By the way, I don't have two or three separate apps, one for the regular devices (phones and tablets) and one for the SmartTVs. I have one single app that automatically will work regardless onto which device you install it. I have seen on the Play store two or three apps: one for phones, one for tablets, and one for SmartTVs... I don't get it. Why? Job security? Lack of knowledge on how to do it? It is only a peace of code and three different layouts to design to make it work on the main three type of Android devices... what am I missing here???? Laziness???

Here is another example on my "coding habit": say I have a code that plays a sound when tapping on the right answer and it is working fine, but I also want to show the score (how many right or wrong). So, I will make a "MyApp" project and ONLY have in there two or three buttons to tap and pick from and with the code for playing the sound if correct. Nothing else, no exit button code, no special effects, no good looks, nada. Just a crude but working sample of the code already working. Now I add the new code for keeping score that, say, I found in the forums or that I am putting together from some ideas I have, etc. If it doesn't work, I know for sure is the new code since the first part was already working. So, I start to troubleshoot the new code until I get it to work. By the way, this implies that the original and functional code was written correctly and that you don't have a potential "waiting" to show problem. But if so, if it is the original code that is at fault, then I try to make another simple app, much simpler, to use the "keep score" code and see if it works. Anything can go wrong... that's why is best to compartmentalize solutions, but, keep in mind that those solutions are good until a new version of the compiler renders them invalid!!!! But at least, when that happens, it is much easier to pin-point the problem, and to upgrade the code.

Right now, the problem you are getting could come from so many angles... maybe the last code injected is correct, and the problem is an old library that you should ditch for a newer library that can do both things at the same time... etc., etc., etc.

Without looking at the whole code of the entire app, and just with what you said... witch is not specific enough, I am afraid I cannot be of much help. Sorry.

Narrow the problem, compartmentalize the routines you are trying to have working together, then post it as a small project app here in the forums and some one will definitely will find the solution, and, you don't have to show the entire code of you app. That's one great thing of "OOP". Always program that way, and you will spend less time finding solutions.
 
Last edited:
Upvote 0

bobsimoneau

Member
Licensed User
Longtime User
luisftv,
Got it going. Thanks for all your help. Just need to clean up. The button are different size. Needed to figure out how to make the buttons all the same size. There is no height and width property. I also do not have a good FireTV emulator. I set up the SDK for Android TV (1080p) and one for Android TV (720p), but when I move to my real Fire TV it is off.
 
Upvote 0
Top