Skip to end of metadata
Go to start of metadata

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

Creating an ADA-Compliant app with Mapping SDK 3.0

 

The Mapping SDK 3.0 is written to allow developers total control over their UI so they can create the best possible experience for their users. Most of the work necessary to make an app accessible for visually impaired users is not specific to this SDK, and information regarding best practices can be found here: Developing Accessible apps

 

We have created an ADA Sample App demonstrating how the Mapping SDK can be used in a way that is useful to visually impaired individuals. Some of the recommended implementation details used in the sample app are:

  • Saving Floor and POI data to a SQL database after the first call to addBuilding, allowing for faster data loading on subsequent app launches and making information available in areas of poor internet connectivity.
  • Setting a LocationProviderFactory on the PhunwareMapManager before calling addBuilding, so turn-by-turn navigation functionality is available.
  • Implementing Navigator.OnManeuverChangedListener in a navigation list view to update and read out maneuvers as the users moves along a route

 

How would a visually-impaired user navigate the app?

  • After moving past the onboarding screen, the user is initially brought to the directory list of all POIs in the building (On first app load this list is initially blank, then populates as soon as data is downloaded from the network).

  • Users can switch to the “Around Me” tab to explore what is near their current location (once acquired) or navigate through the Directory list to hear details about the building’s POIs. If they are looking for something specific they can use the search and filter functions on the directory to more easily find the intended POI.

  • Activating a POI from either the Around Me or Directory list will open a new view with details about that point. If we are able to determine the user’s current location, a “Get Directions” button will be activated on this screen.

  • Activating the “Get Directions” button will bring up a route preview screen. Navigating from the top down, Talkback will read out a description of the route (start and end points, distance, floor changes), let the user know about the two preview tabs, and notify them of the “start navigation” button. If they choose to continue scrolling through the page they can hear all the route steps listed out.

  • Activating the “Start navigation” button opens the navigation view, with the listview tab active. The list of route steps is initially covered by a blocker view which tells the user which direction they should face before they start moving. Once they are oriented correctly, route steps will be read off as they approach each one. Long steps (over 20 ft) will have reminders read out at 20 foot intervals so the user knows when to turn and what direction.  Straight maneuvers will also have warnings at 50 foot intervals telling the user how much longer to continue straight.  When the user reaches their destination they will hear “You have arrived” and navigation will stop. If the SDK detects that the user has gone off route they will be automatically rerouted to their destination.

UI Elements developers may wish to reuse

Automatic Re-routing

  • Navigator.OnManeuverChangedListener has a callback method onRouteSnapFailed() that will be called on all registered listeners any time a user’s location is too far away from the route to implement route snapping behavior

  • In our ADA sample app we have implemented a threshold count to avoid unnecessary rerouting. Once we have had 5 snap failures in a row, we cancel navigation, recalculate a route to the destination from the user’s new current location, and restart navigating with the new route.

    Re-Routing
    public class ListNavigationFragment extends TabFragment
            implements Navigator.OnManeuverChangedListener {
    	/*
    	...
    	*/
     
    	@Override
    	public void onManeuverChanged(Navigator navigator, int i) {
    	routeFailures = 0;
    	    /* update UI for current Maneuver
    		/*
    	}
    
    	@Override
    	public void onRouteSnapFailed() {
        	routeFailures++;
    	    if (routeFailures == 5) {
    	        navigationListener.reroute(route.getEndPointId());
        	}
    	}
    }
     
     
    public class MainActivity extends AppCompatActivity
    	implements MyMapFragment.onNavigationListener {
    	/* 
    	...
    	/*
     
    	@Override
    	public void reroute(long destId) {
    	    Toast.makeText(this, R.string.recalculating, Toast.LENGTH_SHORT).show();
    	    onNavigationExitSelected();
    	    LatLng currentLocation = new LatLng(mapManager.getCurrentLocation().getLatitude(),
    	            mapManager.getCurrentLocation().getLongitude());
    	    RouteOptions route = mapManager.findRoutes(currentLocation, destId,
    	            mapManager.getCurrentBuilding().getSelectedFloor().getId(), true).shortestRoute();
    	    if (route != null) {
    	        sharedNavigator = mapManager.navigate(route);
    	        pagerAdapter.setCurrentLocation(mapManager.getCurrentLocation());
    	        startNavigation();
    	    } else {
    	        Toast.makeText(this, R.string.no_route_available, Toast.LENGTH_SHORT).show();
    	    }
    	}
    	/* 
    	...
    	/*
    }

     

Navigation Overlay View

 

  • The NavigationOverlayView is a custom ViewPager implementation that displays route step details on top of the map in navigation mode

  • The view is added to the same view hierarchy as the PhunwareMap and visibility is toggled based on whether the user is in navigation mode.

NavigationOverlayView
public class NavigationOverlayView extends ViewPager
        implements Navigator.OnManeuverChangedListener {

    private Navigator navigator;
    private ManeuverPagerAdapter adapter;

    private final OnPageChangeListener pageChangeListener = new SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            if (navigator != null) {
                ManeuverPair pair = adapter.getItem(position);
								// when the user manually advances the maneuver, make sure the navigator is updated as well
                navigator.setCurrentManeuver(pair.mainPos);
            }
        }
    };

    public NavigationOverlayView(Context context) {
        this(context, null);
    }

    public NavigationOverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setNavigator(BuildingOptions building, Navigator navigator) {
        this.navigator = navigator;
        navigator.addOnManeuverChangedListener(this);


        List<ManeuverPair> pairs = new ArrayList<>();
        List<RouteManeuverOptions> maneuvers = this.navigator.getManeuvers();
        for (int i = 0; i < maneuvers.size(); i += 2) {
            ManeuverPair pair = new ManeuverPair();
            pair.mainPos = i;
            pair.mainManeuver = maneuvers.get(i);

            if (i + 1 < maneuvers.size()) {
                RouteManeuverOptions next = maneuvers.get(i + 1);
                if (next.isTurnManeuver() || next.isPortalManeuver()) {
                    pair.turnPos = i + 1;
                    pair.turnManeuver = next;
                } else {
                    i--;
                }
            }
            pairs.add(pair);
        }

        setAdapter(adapter = new ManeuverPagerAdapter(navigator));
        adapter.setManeuvers(building, pairs);
        addOnPageChangeListener(pageChangeListener);
        setCurrentItem(0);
        navigator.setCurrentManeuver(0);
    }

    @Override
    public void onManeuverChanged(Navigator navigator, int position) {
		// the user's position has triggered the Navigator to automatically update the current maneuver. Update the UI accordingly
        for (int i = 0; i < adapter.getCount(); i++) {
            ManeuverPair pair = adapter.getItem(i);
            if (pair.mainPos == position || pair.turnPos == position) {
                setCurrentItem(i);
                return;
            }
        }
    }

    @Override
    public void onRouteSnapFailed() {
        Log.e("OverlayView", "RouteSnapFailed");
    }

		// bundle maneuvers into pairs of a straight maneuver and a turn/floor change maneuver
    private static class ManeuverPair {
        int mainPos;
        int turnPos;
        RouteManeuverOptions mainManeuver;
        RouteManeuverOptions turnManeuver;
    }

    private static final class ManeuverPagerAdapter extends PagerAdapter {


        private final Navigator navigator;
        private final ManeuverDisplayHelper displayHelper;
        private final List<ManeuverPair> maneuvers = new ArrayList<>();

        public ManeuverPagerAdapter(Navigator navigator) {
            super();
            this.navigator = navigator;
            displayHelper = new ManeuverDisplayHelper();
        }

        @Override
        public int getCount() {
            return maneuvers.size();
        }

        public void setManeuvers(BuildingOptions building,
                Collection<? extends ManeuverPair> maneuvers) {
            displayHelper.setBuilding(building);
            this.maneuvers.clear();
            this.maneuvers.addAll(maneuvers);
            notifyDataSetChanged();
        }

        public ManeuverPair getItem(int position) {
            return maneuvers.get(position);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View v = LayoutInflater.from(container.getContext())
                    .inflate(R.layout.item_maneuver, container, false);

            ImageView direction = (ImageView) v.findViewById(R.id.direction);
            TextView maneuver = (TextView) v.findViewById(R.id.maneuver);
            TextView nextManeuver = (TextView) v.findViewById(R.id.next_maneuver);
            ImageView nextDirection = (ImageView) v.findViewById(R.id.next_direction);

            final ManeuverPair m = maneuvers.get(position);
						// update the icon in the current maneuver page to represent the direction of the maneuver
            direction.setImageResource(displayHelper.getImageResourceForDirection(m.mainManeuver));
            maneuver.setText(displayHelper.stringForDirection(m.mainManeuver));

            if (m.turnManeuver == null) {
                nextManeuver.setVisibility(View.GONE);
                nextDirection.setVisibility(GONE);
                v.findViewById(R.id.next).setVisibility(GONE);
            } else {
                v.findViewById(R.id.next).setVisibility(VISIBLE);
                nextManeuver.setVisibility(View.VISIBLE);
                nextDirection.setVisibility(VISIBLE);
							  // update the icon in the current maneuver page to represent the directon of the maneuver
                nextDirection.setImageResource(displayHelper
                        .getImageResourceForDirection(m.turnManeuver));
                nextManeuver.setText(displayHelper.stringForDirection(m.turnManeuver));
            }

            if (position == maneuvers.size() - 1) {
						// final maneuver - customize directions to tell the user they are arriving at their destination
                v.findViewById(R.id.next).setVisibility(GONE);
                final int pointCount = navigator.getRoute().getPoints().size();
                PointOptions finalPoint = navigator.getRoute().getPoints().get(pointCount - 1);
                String customLocation = container.getContext().getString(R.string.custom_location_title);
                String arrive = container.getContext().getString(R.string.to_arrive,
                        finalPoint.getName() == null ? customLocation : finalPoint.getName());
                nextManeuver.setVisibility(View.VISIBLE);
                nextManeuver.setText(arrive);
            }

            container.addView(v);
            return v;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }


        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view.equals((View) object);
        }
    }
}

Custom UI when Talkback is turned on

Check if Talkback is on
public static boolean isTalkbackOn(Context context) {
        AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
        boolean isAccessibilityEnabled = am.isEnabled();
        if (isAccessibilityEnabled) {
            List<AccessibilityServiceInfo> list = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_AUDIBLE);
            if (list.size() > 0) {
                for (AccessibilityServiceInfo serviceInfo : list) {
                    if (serviceInfo.getId().contains("TalkBack")) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

 

 

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

Getting Started (Android)

 

This guide is a quick start to adding a Phunware Map to an Android app.  Android Studio is the recommended development environment for building an app with the Phunware Mapping SDK.

Step 1- Add the Phunware Maven remote repository.

Insert this block into allprojects -> repositories:

allprojects {
    repositories {
 
		maven {
    		url "https://nexus.phunware.com/content/groups/public/"
		}
    }
}

 

Step 2 - Add the Mapping SDK as a dependency in your app's build.gradle file

This one line includes all dependencies necessary to use the Phunware Mapping SDK.

apply plugin: 'com.android.application'

android {
	...
}

dependencies {
	...
    compile 'com.phunware.mapping:mapping:3.1.2'
    ...
}

 

Step 3 - Add your Google Maps Api Key to AndroidManifest.xml

Create a string resource that contains your Google Maps API Key.  

If you need help getting a Google Maps API Key,  please find instructions here: Get a Google Maps API key

<meta-data android:name="com.google.android.geo.API_KEY" 
           android:value="@string/google_maps_api_key"/>

 

Step 4 - Add Phunware keys for App Id, Access Key, Signature Key and Encryption Key

Your App Id, Access Key, Signature Key and Encryption Key are found on the MaaS portal on the Applications tab. 

See: Config Guides (Core)

<meta-data android:name="com.phunware.APPLICATION_ID" android:value="@string/app_id" />
<meta-data android:name="com.phunware.ACCESS_KEY" android:value="@string/access_key" />
<meta-data android:name="com.phunware.SIGNATURE_KEY" android:value="@string/signature_key" />
<meta-data android:name="com.phunware.ENCRYPTION_KEY" android:value="@string/encrypt_key" />

 

Step 5 - Add MapFragment to your layout

This is where the Phunware Mapping SDK will render the map.  

NOTE: the fragment will be found via the R.id.map id.

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:name="com.phunware.mapping.MapFragment"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

 

Step 6 - Get the Map asynchronously in your activity:

The PhunwareMapManager is created and the Phunware API keys are registered in this step.

Then the MapFragment is located and the map is loaded asynchronously.  

Your implementation of OnPhunwareMapReadyCallback will be called when the map is ready.

	private PhunwareMapManager mapManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

		...

        mapManager = PhunwareMapManager.create(this);

        // register the Phunware API keys
        PwCoreSession.getInstance().registerKeys(this);

        MapFragment mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.map);
        if (mapFragment != null) {
            mapFragment.getPhunwareMapAsync(this);
        }
    }

 

 

Step 7 - Make sure your activity implements OnPhunwareMapReadyCallback

Once the PhunwareMap is ready, set it in the PhunwareMapManager and add a building by id.

Building additions are also asynchronous,  so a callback will notify you of success or failure.

public class MainActivity extends AppCompatActivity implements OnPhunwareMapReadyCallback {

	...
	
    @Override
    public void onPhunwareMapReady(PhunwareMap phunwareMap) {
        mapManager.setPhunwareMap(phunwareMap);

        mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
                new Callback<Building>() {
            @Override
            public void onSuccess(Building building) {
            }

            @Override
            public void onFailure(Throwable throwable) {
            }
        });        
    }

 

 

Step 8 - Do something interesting with the Building

Once the map and building are loaded, move and zoom the view so you can see the initial floor.

public class MainActivity extends AppCompatActivity implements OnPhunwareMapReadyCallback {
    @Override
    public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
        mapManager.setPhunwareMap(phunwareMap);

        mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
                new Callback<Building>() {
            @Override
            public void onSuccess(Building building) {
                FloorOptions initialFloor = building.initialFloor();
                building.selectFloor(initialFloor.getLevel());
                // animate the camera to the building at an appropriate zoom level
                // so we can see the building
                CameraUpdate cameraUpdate = CameraUpdateFactory
                        .newLatLngBounds(initialFloor.getBounds(), 4);
                phunwareMap.getGoogleMap().animateCamera(cameraUpdate);
            }
            @Override
            public void onFailure(Throwable throwable) {
            }
        });        
    }

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

(v3.0.0-3.1.0 of Location SDK-Android)

 

Integrating a Location Provider

This guide provides instructions for integrating Location SDK for routing. It is only applicable for users of the Android Location SDK v 3.0.0-3.1.0.

*If you are using a more recent version of the Android Location SDK, view the Location SDK-Managed Provider Integration Guide.

*If you are using an earlier version of the Location SDK for Android contact Phunware Support (support@phunware.com). 

 

In order to show the user's current location,  you must add a Location Provider, which updates location information.

Phunware's location providers:

Provider
Settings/Keys
Compile Statement
Description
BLE
  • Confidence Factor
  • Acceptance Radius
  • Map Key
  • Customer ID
com.phunware.location:provider-senion:3.0.0
Senion Lab BLE (bluetooth low energy) location provider
CMX - Hyperlocation (MSE)
  • Confidence Factor
  • Venue GUID
  • Minimum Stationary Time
  • Maximum Lingering time
com.phunware.location:provider-cmx:3.0.0
Cisco Hyperlocation (wifi) location provider
GPS 
com.phunware.location:provider-gps:3.0.0
GPS location provider

 

 

Step 1 -  Add a location provider as a compile dependency

apply plugin: 'com.android.application' 
android {
    ...
}
 
dependencies {
    ...
    compile 'com.phunware.mapping:mapping:3.1.2'
    compile 'com.phunware.location:provider-senion:3.1.0'
    ...
}

 

Step 2 - Set the Location Provider and Enable Location Updates

This step tells the PhunwareMapManager what provider will be used to get location updates.  In this case,  the Senion Location Provider requires a customer id and map id.  The method getSenionFloorMap() returns a HashMap of Senion floor ids to MaaS floor ids.

It's important to note that you must pass a building object to the setLocationManager() method on PhunwareMapManager.

The call to setMyLocationEnabled() allows you to control when the blue dot is rendered.

@Override    
public void onPhunwareMapReady(PhunwareMap phunwareMap) {
	mapManager.setPhunwareMap(map);
	mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
        new Callback<Building>() {
            @Override
            public void onSuccess(final Building building) {
				...

                mapManager.setLocationProvider(SenionProviderFactory.create(this,
                        getString(R.string.sl_customer_id),
                        getString(R.string.sl_map_id),
                        getSenionFloorMap()).createLocationProvider(), building);

                // enable my location (blue dot)
                mapManager.setMyLocationEnabled(true);

				...
            }

            @Override
            public void onFailure(Throwable e) {
            }
        });
}

 

Step 3 - Manage Location Updates when in the Background

Constant location updates when in the background will drain a battery.  In order to prevent this, we intercept activity lifecycle methods to disable and enable location updates.

@Override
protected void onPause() {
    super.onPause();
    if (mapManager != null) {
        mapManager.setMyLocationEnabled(false);
    }
}

@Override
protected void onResume() {
    super.onResume();

    if (mapManager != null) {
        mapManager.setMyLocationEnabled(true);
    }
}

 

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

Integrating a Managed Provider

 

Step 1 - Add the Phunware Maven remote repository. Insert this block into allprojects -> repositories:

allprojects {
    repositories {
        maven {
            url "https://nexus.phunware.com/content/groups/public/"
        }
    }
}

Step 2 - Add Phunware key resources to strings.xml for App Id, Access Key, Signature Key

Navigate to portal, find your app, and add your access key and signature key to setup your application.

<string name="app_Id">APPID</string>
<string name="access_Key">ACCESSKEY</string>
<string name="sig_Key">SIGKEY</string>

Step 3 - Add Phunware keys for App Id, Access Key, and Signature Key to Manifest

<meta-data
    android:name="com.phunware.APPLICATION_ID"
    android:value="@string/app_id"/>
<meta-data
    android:name="com.phunware.ACCESS_KEY"
    android:value="@string/access_key"/>
<meta-data
    android:name="com.phunware.SIGNATURE_KEY"
    android:value="@string/signature_key"/>
<meta-data
    android:name="com.phunware.ENCRYPTION_KEY"
    android:value="@string/encrypt_key"/>

Note that encryption key may not be provided in portal under your app's settings. If it isn't, you may leave it empty.

Step 4 - Add managed provider as a compile dependency.

apply plugin: 'com.android.application'
android {
	...
}
 
dependencies {
	...
	compile 'com.phunware.mapping:mapping:3.1.2'
	compile 'com.phunware.location:provider-managed:3.1.1'
	...
} 

Step 5 - Set the Location Provider on the PhunwareMapManager

@Override
public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
    mapManager.setPhunwareMap(phunwareMap);
    this.phunwareMap = phunwareMap;

    mapManager.addBuilding(buildingId, venueGuid,
            new Callback<Building>() {
                @Override
                public void onSuccess(Building building) {
                    if (building == null) {
                        Toast.makeText(MainActivity.this, "No building", Toast.LENGTH_LONG)
                                .show();
                        return;
                    }

                    // ManagedProvider must be set after the map is loaded
                    ManagedProviderFactory.ManagedProviderFactoryBuilder builder =
                            new ManagedProviderFactory.ManagedProviderFactoryBuilder();
                    builder.application(getApplication())
                            .context(new WeakReference<Context>(getApplication()))
                            .venueId(String.valueOf(buildingId)));
                    ManagedProviderFactory factory = builder.build();
                    PwManagedLocationProvider provider = (PwManagedLocationProvider) factory
                            .createLocationProvider();
                    mapManager.setLocationProvider(provider, building);
						...

It's important to note that Managed Provider must be built and set on my MapManager after the the map is ready and the building has been loaded.

There are a few values that will be needed to build a ManagedProvider, they are as follows:

  • application: reference to the users application
  • context: Context of the application
  • buildingId: Id of the venue/building you currently have loaded

Step 6 - Enable Location Updates

@Override
public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
	...
    mapManager.addBuilding(buildingId, venueGuid,
            new Callback<Building>() {
                @Override
                public void onSuccess(Building building) {
					...
					// enable my location (blue dot) after setting the location provider
					mapManager.setMyLocationEnabled(true);

					}

				}

}

Enable location updates after we have the map and building as well as after setting the provider

Step 7 - Manage Location Updates when in the Background

@Override
protected void onPause() {
    super.onPause();
	

	// If you have permission to access location and you don't have a building then proceed

	if (canAccessLocation() && building == null) {
    	if (mapFragment != null) {
        	mapFragment.getPhunwareMapAsync(this);
    	}
	}
    if (mapManager != null) {
        mapManager.setMyLocationEnabled(false);
    }
}
 
@Override
protected void onResume() {
    super.onResume();
 
    if (mapManager != null) {
        mapManager.setMyLocationEnabled(true);
    }
}

In order to ensure that we handle lifecycles correctly we must stop requesting location updates when we background, and begin requesting them when we are in the foreground. 

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

Using a MapView in a Custom Layout

 

MapView is a subclass of the Google Maps MapView class.  It can be used to place a map in any Android View.

Users of the MapView class must forward all the activity lifecycle methods to the corresponding methods in the MapView class. Examples of these methods include onCreate(), onDestroy(), onResume() and onPause().

 

SimpleMapViewFragment.java

public class SimpleMapViewFragment extends Fragment implements OnPhunwareMapReadyCallback {

    private static final String TAG = SimpleMapViewFragment.class.getSimpleName();

    private PhunwareMapManager mapManager;
    private MapView mapView;

    public static SimpleMapViewFragment newInstance() {
        SimpleMapViewFragment fragment = new SimpleMapViewFragment();

        Bundle args = new Bundle();
        fragment.setArguments(args);

        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mapManager = ((App)getActivity().getApplication()).getMapManager();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_map, container, false);

        mapView = (MapView)view.findViewById(R.id.map_view);
        mapView.onCreate(savedInstanceState);

        mapView.getPhunwareMapAsync(this);

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
    }

    @Override
    public void onPause() {
        mapView.onPause();
        super.onPause();
    }

    @Override
    public void onDestroy() {
        mapView.onDestroy();
        super.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
    }

    @Override
    public void onPhunwareMapReady(final PhunwareMap map) {

        mapManager.setPhunwareMap(map);
        mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
                new Callback<Building>() {
                    @Override
                    public void onSuccess(final Building building) {
                        if (building == null) {
                            Toast.makeText(getActivity(), "No building", Toast.LENGTH_LONG)
                                    .show();
                            return;
                        }

                        // animate the camera to the new building
                        final CameraUpdate cameraUpdate;
                        FloorOptions initialFloor = building.initialFloor();
                        if (initialFloor == null) {
                            // if we don't have a floor with reference points, just zoom in on the
                            // building, at a relatively safe distance
                            cameraUpdate = CameraUpdateFactory
                                    .newLatLngZoom(building.getLocation(), 17);
                        } else {
                            building.selectFloor(building.getInitialFloor());
                            cameraUpdate = CameraUpdateFactory
                                    .newLatLngBounds(initialFloor.getBounds(), 4);
                        }
                        map.getGoogleMap().setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
                            // Wait to animate the camera until the map is rendered.
                            @Override
                            public void onMapLoaded() {
                                map.getGoogleMap().animateCamera(cameraUpdate);
                            }
                        });

                        // enable my location (blue dot)
                        mapManager.setMyLocationEnabled(true);
                    }

                    @Override
                    public void onFailure(final Throwable e) {
                        getActivity().runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getActivity(), e.getLocalizedMessage(),
                                        Toast.LENGTH_LONG).show();
                            }
                        });
                    }
                });
    }
}

 

fragment_map.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.phunware.mapping.MapView
        android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingBottom="48dp" />

</android.support.design.widget.CoordinatorLayout>

 

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

Getting POI info from a Map Marker

 

The Mapping SDK associates a PointOptions object with its associated Google map marker object.

When setting up InfoWindow click listeners,  you can access the POI info from the marker by calling the getTag() method on the marker and cast it to PointOptions.

map.getGoogleMap().setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
    @Override
    public void onInfoWindowClick(Marker marker) {
        PointOptions point = (PointOptions) marker.getTag();
		if (point != null) {
	        long floorId = point.getFloorId();
    	    String name = point.getName();
		}
    }
});

This documentation is no longer actively supported and may be out of date. Going forward, please visit and bookmark our new site (https://docs.phunware.com/) for up-to-date documentation.

(v 3.1.1 and Newer of Location SDK-Android)

 

 

This guide provides instructions for getting started with the Location SDK and integrating Location SDK for routing. It is only applicable for users of the Android Location SDK v 3.1.1 and newer.

*If you are using version 3.0.0 - 3.1.0 of the Android Location SDK, view Integrating a Location Provider

*If you are using a version of the Location SDK for Android earlier than 3.0.0 contact Phunware Support for SDK assistance (support@phunware.com). 

 

Android Mapping SDK Installation - Getting Started

This guide is a quick start to adding a Phunware Map to an Android app.  Android Studio is the recommended development environment for building an app with the Phunware Mapping SDK.

 


Step 1- Add the Phunware Maven remote repository.

Insert this block into allprojects -> repositories:

allprojects {
    repositories {
 
		maven {
    		url "https://nexus.phunware.com/content/groups/public/"
		}
    }
}

Step 2 - Add the Mapping SDK as a dependency in your app's build.gradle file

This one line includes all dependencies necessary to use the Phunware Mapping SDK.

apply plugin: 'com.android.application'

android {
	...
}

dependencies {
	...
    compile 'com.phunware.mapping:mapping:3.1.2'
    ...
}

Step 3 - Add your Google Maps Api Key to AndroidManifest.xml

Create a string resource that contains your Google Maps API Key.  

If you need help getting a Google Maps API Key,  please find instructions here: Get a Google Maps API key

 

 

<meta-data android:name="com.google.android.geo.API_KEY" 
           android:value="@string/google_maps_api_key"/>

Step 4 - Add Phunware keys for App Id, Access Key, Signature Key and Encryption Key

Your App Id, Access Key, Signature Key and Encryption Key are found on the MaaS portal on the Applications tab. 

See: Config Guides (Core)

<meta-data android:name="com.phunware.APPLICATION_ID" android:value="@string/app_id" />
<meta-data android:name="com.phunware.ACCESS_KEY" android:value="@string/access_key" />
<meta-data android:name="com.phunware.SIGNATURE_KEY" android:value="@string/signature_key" />
<meta-data android:name="com.phunware.ENCRYPTION_KEY" android:value="@string/encrypt_key" />

Step 5 - Add MapFragment to your layout

This is where the Phunware Mapping SDK will render the map.  

NOTE: the fragment will be found via the R.id.map id.

<fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:name="com.phunware.mapping.MapFragment"
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

Step 6 - Get the Map asynchronously in your activity:

The PhunwareMapManager is created and the Phunware API keys are registered in this step.

Then the MapFragment is located and the map is loaded asynchronously.  

Your implementation of OnPhunwareMapReadyCallback will be called when the map is ready.

 

	private PhunwareMapManager mapManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

		...

        mapManager = PhunwareMapManager.create(this);

        // register the Phunware API keys
        PwCoreSession.getInstance().registerKeys(this);

        MapFragment mapFragment = (MapFragment) getFragmentManager()
                .findFragmentById(R.id.map);
        if (mapFragment != null) {
            mapFragment.getPhunwareMapAsync(this);
        }
    }

Step 7 - Make sure your activity implements OnPhunwareMapReadyCallback

Once the PhunwareMap is ready, set it in the PhunwareMapManager and add a building by id.

Building additions are also asynchronous,  so a callback will notify you of success or failure.

public class MainActivity extends AppCompatActivity implements OnPhunwareMapReadyCallback {

	...
	
    @Override
    public void onPhunwareMapReady(PhunwareMap phunwareMap) {
        mapManager.setPhunwareMap(phunwareMap);

        mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
                new Callback<Building>() {
            @Override
            public void onSuccess(Building building) {
            }

            @Override
            public void onFailure(Throwable throwable) {
            }
        });        
    }

Step 8 - Do something interesting with the Building

Once the map and building are loaded, move and zoom the view so you can see the initial floor.

public class MainActivity extends AppCompatActivity implements OnPhunwareMapReadyCallback {
    @Override
    public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
        mapManager.setPhunwareMap(phunwareMap);

        mapManager.addBuilding(getResources().getInteger(R.integer.building_id),
                new Callback<Building>() {
            @Override
            public void onSuccess(Building building) {
                FloorOptions initialFloor = building.initialFloor();
                building.selectFloor(initialFloor.getLevel());
                // animate the camera to the building at an appropriate zoom level
                // so we can see the building
                CameraUpdate cameraUpdate = CameraUpdateFactory
                        .newLatLngBounds(initialFloor.getBounds(), 4);
                phunwareMap.getGoogleMap().animateCamera(cameraUpdate);
            }
            @Override
            public void onFailure(Throwable throwable) {
            }
        });        
    }

Integrating a Managed Provider

 

Managed Providers offer a combination of signal location providers. This combination results in higher accuracy rates for location in dynamic way finding.

Phunware Managed Providers:

Provider
Settings/Keys
Description
Cisco Beacon Point
  • Confidence Factor
  • Mobile SDK Key
 

Cisco vBLE (virtual bluetooth low energy) location provider

Mist
  • Confidence Factor
  • Mobile SDK Key
 
Mist vBLE (virtual bluetooth low energy) location provider

 

 


Step 1 - Add the Phunware Maven remote repository. Insert this block into allprojects -> repositories:

allprojects {
    repositories {
        maven {
            url "https://nexus.phunware.com/content/groups/public/"
        }
    }
}

Step 2 - Add Phunware key resources to strings.xml for App Id, Access Key, Signature Key

Navigate to portal, find your app, and add your access key and signature key to setup your application.

<string name="app_Id">APPID</string>
<string name="access_Key">ACCESSKEY</string>
<string name="sig_Key">SIGKEY</string>


Step 3 - Add Phunware keys for App Id, Access Key, and Signature Key to Manifest

Note that encryption key may not be provided in portal under your app's settings. If it isn't, you may leave it empty.



<meta-data
    android:name="com.phunware.APPLICATION_ID"
    android:value="@string/app_id"/>
<meta-data
    android:name="com.phunware.ACCESS_KEY"
    android:value="@string/access_key"/>
<meta-data
    android:name="com.phunware.SIGNATURE_KEY"
    android:value="@string/signature_key"/>
<meta-data
    android:name="com.phunware.ENCRYPTION_KEY"
    android:value="@string/encrypt_key"/>


Step 4 - Add managed provider as a compile dependency.


apply plugin: 'com.android.application'
android {
	...
}
 
dependencies {
	...
	compile 'com.phunware.mapping:mapping:3.1.2'
	compile 'com.phunware.location:provider-managed:3.1.0'
	...
} 


Step 5 - Set the Location Provider on the PhunwareMapManager

It's important to note that Managed Provider must be built and set on my MapManager after the the map is ready and the building has been loaded.


There are a few values that will be needed to build a ManagedProvider, they are as follows:

  • application: reference to the users application
  • context: Context of the application
  • buildingId: Id of the building you currently have loaded


@Override
public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
    mapManager.setPhunwareMap(phunwareMap);
    this.phunwareMap = phunwareMap;

    mapManager.addBuilding(buildingId, venueGuid,
            new Callback<Building>() {
                @Override
                public void onSuccess(Building building) {
                    if (building == null) {
                        Toast.makeText(MainActivity.this, "No building", Toast.LENGTH_LONG)
                                .show();
                        return;
                    }

                    // ManagedProvider must be set after the map is loaded
                    ManagedProviderFactory.ManagedProviderFactoryBuilder builder =
                            new ManagedProviderFactory.ManagedProviderFactoryBuilder();
                    builder.application(getApplication())
                            .context(new WeakReference<Context>(getApplication()))
                            .buildingId(String.valueOf(buildingId));
                    ManagedProviderFactory factory = builder.build();
                    PwManagedLocationProvider provider = (PwManagedLocationProvider) factory
                            .createLocationProvider();
                    mapManager.setLocationProvider(provider, building);
						...


Step 6 - Enable Location Updates

Enable location updates after we have the map and building as well as after setting the provider



@Override
public void onPhunwareMapReady(final PhunwareMap phunwareMap) {
	...
    mapManager.addBuilding(buildingId, venueGuid,
            new Callback<Building>() {
                @Override
                public void onSuccess(Building building) {
					...
					// enable my location (blue dot) after setting the location provider
					mapManager.setMyLocationEnabled(true);

					}

				}

}


Step 7 - Manage Location Updates when in the Background

In order to ensure that we handle lifecycles correctly we must stop requesting location updates when we background, and begin requesting them when we are in the foreground. 


@Override
protected void onPause() {
    super.onPause();
	

	// If you have permission to access location and you don't have a building then proceed

	if (canAccessLocation() && building == null) {
    	if (mapFragment != null) {
        	mapFragment.getPhunwareMapAsync(this);
    	}
	}
    if (mapManager != null) {
        mapManager.setMyLocationEnabled(false);
    }
}
 
@Override
protected void onResume() {
    super.onResume();
 
    if (mapManager != null) {
        mapManager.setMyLocationEnabled(true);
    }
}

 

 

  • No labels