Class LocationButton

All Implemented Interfaces:
Animation, Editable, StyleListener, Iterable<Component>

public class LocationButton extends Container

A button that asks for the device location once, for a single transaction.

This is the Codename One face of the platform's own location button. From Android 17 (API level 37) Google Play requires transactional precise-location use -- "find restaurants near me", filling in an address, tagging a photo -- to go through a button the system draws, rather than through an app-held ACCESS_FINE_LOCATION grant. A tap on a button the system drew earns a session-scoped grant, so the application never has to hold precise location permanently.

LocationButton b = new LocationButton(LocationButton.TEXT_USE_PRECISE_LOCATION);
b.addLocationSharedListener(loc -> {
    if (loc != null) {
        search(loc.getLatitude(), loc.getLongitude());
    }
});
form.add(b);
Where the system draws it, and where it does not

On a platform that has such a control -- today only Android 17 and up -- this component is that control: it is rendered by the system in its own process and this application cannot restyle its label or intercept its taps, which is exactly what makes the grant trustworthy. Everywhere else, and on older Android, the component is an ordinary Codename One Button that asks for the location permission the usual way. Either way the listener receives a Location or null, so an application is written once.

isSystemRendered() reports which of the two this button ended up with, for an application that wants to say something different about it. Ask com.codename1.ui.Display#isLocationButtonSupported() instead when the question is what the platform can do rather than what happened here.

On Android

Referencing this class makes the build add USE_LOCATION_BUTTON to the manifest, which the platform requires before it will render the control. Nothing else is needed.

The build also decides how to declare ACCESS_FINE_LOCATION. An application that uses this component and nothing else from the location or maps packages gets it declared onlyForLocationButton, which means the system grants precise location through the button and never any other way -- no "allow precise location" question, and nothing to justify to Google Play. An application that also tracks, navigates or geofences needs the ordinary grant and gets the ordinary declaration. The class scan the build already runs answers this, so neither case needs a build hint.

android.locationButton.exclusive (auto, true, false) overrides it, for an application whose location use that scan cannot see -- native Android code reaching the platform's own location APIs is the case that matters. Writing the declaration by hand through android.xpermissions is not the way to ask for it: the build cannot check a hand-written fragment against the compile SDK, and onlyForLocationButton is an API 37 value that AAPT rejects below it.

  • Field Details

    • TEXT_NONE

      public static final int TEXT_NONE
      An icon with no label. The narrowest form of the control.
      See Also:
    • TEXT_PRECISE_LOCATION

      public static final int TEXT_PRECISE_LOCATION
      Labelled "Precise location".
      See Also:
    • TEXT_USE_PRECISE_LOCATION

      public static final int TEXT_USE_PRECISE_LOCATION
      Labelled "Use precise location". The default.
      See Also:
    • TEXT_SHARE_PRECISE_LOCATION

      public static final int TEXT_SHARE_PRECISE_LOCATION
      Labelled "Share precise location".
      See Also:
    • TEXT_NEAR_MY_PRECISE_LOCATION

      public static final int TEXT_NEAR_MY_PRECISE_LOCATION
      Labelled "Near my precise location".
      See Also:
    • TEXT_NEAR_YOUR_PRECISE_LOCATION

      public static final int TEXT_NEAR_YOUR_PRECISE_LOCATION
      Labelled "Near your precise location".
      See Also:
  • Constructor Details

    • LocationButton

      public LocationButton()
      A button labelled "Use precise location".
    • LocationButton

      public LocationButton(int textType)

      A button with one of the labels the platform offers.

      Parameters
      • textType: one of the TEXT_ constants
      Throws
      • IllegalArgumentException: if textType is not one of them
  • Method Details

    • isSystemRendered

      public boolean isSystemRendered()

      Whether a tap on this button goes through a control the system itself drew.

      False means this component is not showing the system's control: every platform other than Android, Android below API level 37, before the component has been shown, in the moment between the control being created and the system opening its session, and on a device where the platform HAS the control but its session failed and useFallback() replaced it.

      That last case is why this is an instance question rather than a static one. Display.isLocationButtonSupported() answers what the platform can do and is the right call to make before building anything; only the component knows what actually ended up on screen.

      Returns

      whether the system drew the control this button is showing

    • getTextType

      public int getTextType()

      The label this button carries.

      Returns

      one of the TEXT_ constants

    • setTextType

      public void setTextType(int textType)

      Chooses the label this button carries.

      Parameters
      • textType: one of the TEXT_ constants
      Throws
      • IllegalArgumentException: if textType is not one of them
    • setButtonBackgroundColor

      public void setButtonBackgroundColor(int color)

      Asks the platform to draw the button on this background colour.

      Only reaches the system-rendered control; the fallback button is themed through its LocationButton UIID like any other component.

      Parameters
      • color: an RRGGBB colour, or -1 to let the system choose
    • getButtonBackgroundColor

      public int getButtonBackgroundColor()

      The background colour asked of the platform, or -1.

      Returns

      an RRGGBB colour or -1

    • setButtonTextColor

      public void setButtonTextColor(int color)

      Asks the platform to draw the button's label in this colour.

      Parameters
      • color: an RRGGBB colour, or -1 to let the system choose
    • getButtonTextColor

      public int getButtonTextColor()

      The label colour asked of the platform, or -1.

      Returns

      an RRGGBB colour or -1

    • getTimeout

      public long getTimeout()

      How long to wait for a fix once the request has been granted.

      Returns

      the timeout in milliseconds, or -1 to wait indefinitely

    • setTimeout

      public void setTimeout(long timeout)

      How long to wait for a fix once the request has been granted.

      A cold GPS fix is legitimately slow, so this is deliberately generous; on expiry the listener is invoked with null rather than left hanging.

      Parameters
      • timeout: milliseconds, or -1 to wait indefinitely
    • addLocationSharedListener

      public void addLocationSharedListener(LocationSharedListener l)

      Registers a listener for the location this button obtains.

      Parameters
      • l: the listener
    • removeLocationSharedListener

      public void removeLocationSharedListener(LocationSharedListener l)

      Removes a previously registered listener.

      Parameters
      • l: the listener
    • calcPreferredSize

      protected Dimension calcPreferredSize()

      The size an ordinary themed button with the same label would have, but never below the platform's minimum touch target.

      The system-rendered control is given whatever size we ask for, so this answer is used for both paths and the two look alike. The floor is not a nicety: Android clamps a location button up to 48dp and says so in the log ("Clamping height up from 60 to 126 px"), and the button it then draws overflows the slot Codename One laid out for it -- observed on an Android 17 emulator, where the control was visibly cut in half.

      Returns

      the preferred size

      Overrides:
      calcPreferredSize in class Container
    • initComponent

      protected void initComponent()
      Description copied from class: Component
      Allows subclasses to bind functionality that relies on fully initialized and "ready for action" component state
      Overrides:
      initComponent in class Component