I'm developing an Android application where I need to load POIs in Architect view in a fragment using Javascript SDK. I have followed all the steps while creating it. But I'm able to see only blank camera screen with blank radar.None of the POIs are loading on screen but the status message at the bottom shows "20 places loaded".
I'm attaching Fragment code below, please have a look:
public class WikitudeFragment extends Fragment implements ArchitectViewHolderInterface.ILocationProvider {
/** * holds the Wikitude SDK AR-View, this is where camera, markers, compass, 3D models etc. are rendered */ private ArchitectView architectView;
/** * sensor accuracy listener in case you want to display calibration hints */ private ArchitectView.SensorAccuracyChangeListener sensorAccuracyListener;
/** * last known location of the user, used internally for content-loading after user location was fetched */ private Location lastKnownLocaton;
/** * sample location strategy, you may implement a more sophisticated approach too */ public ArchitectViewHolderInterface.ILocationProvider locationProvider;
/** * location listener receives location updates and must forward them to the architectView */ protected LocationListener locationListener;
private long lastCalibrationToastShownTimeMillis = System.currentTimeMillis();
private boolean isLoading = false; public static final int CULLING_DISTANCE_DEFAULT_METERS = 50 * 1000; private static final int WIKITUDE_PERMISSIONS_REQUEST_CAMERA = 1; private static final int WIKITUDE_PERMISSIONS_REQUEST_GPS = 2; private static final int WIKITUDE_PERMISSIONS_REQUEST_EXTERNAL_STORAGE = 3; private static final int WIKITUDE_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 4; protected Bitmap screenCapture = null; String permissions = {"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.CAMERA" , "android.permission.ACCESS_GPS" ,"android.permission.ACCESS_FINE_LOCATION"}; int permsRequestCode = 200;
public static final String WIKITUDE_SDK_KEY = "EKZPG8BYCNzlt07uwSftel6fJJW2MM8UV+KsQff1G1UsVU6tN0fpjUaXo5YGsxxqUfSbM5tAdd1hK/J0bzF5ZaMGPKng4mUcaNsbgZWFMvTj2v9J8K4Wh14WhEhobYHXBg7+7++dfxEgERPTQjKYwZ8dD9Q3CNNHPeQNuH5qgIFTYWx0ZWRfXy2B7agUsZSUDGch/uargutzvX18ojzwKkVFqSRn/aXp//mAFflOJD+vyZSsdX7hvImbpUQpgxfTJ/5jnJoEkN0jcTWb11TkzaIuqPFlYesDy1QUuFCBhEEyYaEEwB4cyIygsfP9SV3rBGGshWEcUw5PsCE3GilmLb49iqn/wfTXbiUy15+sJtycd+ybGgrohESXQMkhe6jsiaIqYhOx/nmMjSnMtDy6LYVcasp+OMQROYZUoC7I9WriaqFUVfj0PuyRSF+zY/YaQmL10YLPsFy4ifA1sXLDSfzInMgmu1woe46B2f66kyifeBp9h/AVwKdeIXZeUg8MIi1Be9Fhmx4oHE3Gr4vfEKmcSZuiOkqWpB1RCpfQcvzhmOSg+Ylw+WPfzjlXKXLlzKiwZeHDeosY/7crORSyR9v/vqMK7uFw7u+Lgjgk5omjWATWRN1v9xtt9v8ysPlqo4ZjL5RTmtDbFgYKl3R3FUFEoYCxxvHlQsbnwYECbT4="; private MainActivity mainAct;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_wikitude, container, false);
mainAct = (MainActivity) getActivity(); /* set AR-view for life-cycle notifications etc. */ architectView = (ArchitectView)view.findViewById(R.id.architectView); return view; }
public void verifyAccess() { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { requestPermissions(permissions, permsRequestCode); } }
private void initARView() {
verifyAccess();
/*pass SDK key if you have one, this one is only valid for this package identifier and must not be used somewhere else */ final StartupConfiguration config = new StartupConfiguration( WIKITUDE_SDK_KEY, 0 , StartupConfiguration.CameraPosition.DEFAULT );
@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);
/*initialize AR View*/ initARView();
if ( this.architectView != null ) {
// call mandatory live-cycle method of architectView this.architectView.onPostCreate();
try { // load content via url in architectView, ensure '<script src="architect://architect.js"></script>' is part of this HTML file, have a look at wikitude.com's developer section for API references this.architectView.load("samples/PointOfInterestMultiplePois/index.html");
if (this.getInitialCullingDistanceMeters() != CULLING_DISTANCE_DEFAULT_METERS) { // set the culling distance - meaning: the maximum distance to render geo-content this.architectView.setCullingDistance( this.getInitialCullingDistanceMeters() ); }
// set accuracy listener if implemented, you may e.g. show calibration prompt for compass using this listener this.sensorAccuracyListener = this.getSensorAccuracyListener();
// set urlListener, any calls made in JS like "document.location = 'architectsdk://foo?bar=123'" is forwarded to this listener, use this to interact between JS and native Android activity/fragment urlListener = getUrlListener();
// register valid urlListener in architectView, ensure this is set before content is loaded to not miss any event if (this.urlListener != null && this.architectView != null) { this.architectView.registerUrlListener( this.getUrlListener() ); }
// listener passed over to locationProvider, any location update is handled here this.locationListener = new LocationListener() {
@Override public void onStatusChanged( String provider, int status, Bundle extras ) { }
@Override public void onProviderEnabled( String provider ) { }
@Override public void onProviderDisabled( String provider ) { }
@Override public void onLocationChanged( final Location location ) { // forward location updates fired by LocationProvider to architectView, you can set lat/lon from any location-strategy if (location!=null) { // sore last location as member, in case it is needed somewhere (in e.g. your adjusted project) lastKnownLocaton = location; if ( architectView != null ) { // check if location has altitude at certain accuracy level & call right architect method (the one with altitude information) if ( location.hasAltitude() && location.hasAccuracy() && location.getAccuracy()<7) { architectView.setLocation( location.getLatitude(), location.getLongitude(), location.getAltitude(), location.getAccuracy() ); } else { architectView.setLocation( location.getLatitude(), location.getLongitude(), location.hasAccuracy() ? location.getAccuracy() : 1000 ); } } } } };
// locationProvider used to fetch user position locationProvider = getLocationProvider( this.locationListener );
public float getInitialCullingDistanceMeters() { // you need to adjust this in case your POIs are more than 50km away from user here while loading or in JS code (compare 'AR.context.scene.cullingDistance') return CULLING_DISTANCE_DEFAULT_METERS; }
@Override public void onResume() { super.onResume();
// call mandatory live-cycle method of architectView if ( this.architectView != null ) { this.architectView.onResume();
// register accuracy listener in architectView, if set if (this.sensorAccuracyListener!=null) { this.architectView.registerSensorAccuracyChangeListener( this.sensorAccuracyListener ); } }
// tell locationProvider to resume, usually location is then (again) fetched, so the GPS indicator appears in status bar if ( this.locationProvider != null ) { this.locationProvider.onResume(); } }
@Override public void onPause() { super.onPause(); // call mandatory live-cycle method of architectView if ( this.architectView != null ) { this.architectView.onPause();
// unregister accuracy listener in architectView, if set if ( this.sensorAccuracyListener != null ) { this.architectView.unregisterSensorAccuracyChangeListener( this.sensorAccuracyListener ); } }
// tell locationProvider to pause, usually location is then no longer fetched, so the GPS indicator disappears in status bar if ( this.locationProvider != null ) { this.locationProvider.onPause(); } }
@Override public void onDestroy() { super.onDestroy();
// call mandatory live-cycle method of architectView if ( this.architectView != null ) { this.architectView.onDestroy(); } }
@Override public void onLowMemory() { super.onLowMemory();
if ( this.architectView != null ) { this.architectView.onLowMemory(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions, @NonNull int grantResults) { //super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case WIKITUDE_PERMISSIONS_REQUEST_CAMERA: { if ( grantResults.length > 0 && grantResults == PackageManager.PERMISSION_GRANTED ) { if ( ContextCompat.checkSelfPermission(mainAct.getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ) { ActivityCompat.requestPermissions(mainAct, new String{Manifest.permission.ACCESS_FINE_LOCATION}, WIKITUDE_PERMISSIONS_REQUEST_GPS); }
} else { Toast.makeText(mainAct.getApplicationContext(), "Sorry, augmented reality doesn't work without reality.n\nPlease grant camera permission.", Toast.LENGTH_LONG).show(); } return; } case WIKITUDE_PERMISSIONS_REQUEST_GPS: { if ( grantResults.length > 0 && grantResults == PackageManager.PERMISSION_GRANTED ) { } else { Toast.makeText(mainAct.getApplicationContext(), "Sorry, this example requires access to your location in order to work properly.\n\nPlease grant location permission.", Toast.LENGTH_SHORT).show(); } return; }
case WIKITUDE_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: { if ( grantResults.length > 0 && grantResults == PackageManager.PERMISSION_GRANTED ) { /*this.saveScreenCaptureToExternalStorage(screenCapture);*/ } else { Toast.makeText(mainAct.getApplicationContext(), "Please allow access to external storage, otherwise the screen capture can not be saved.", Toast.LENGTH_SHORT).show(); } return; }
case WIKITUDE_PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { if ( grantResults.length > 0 && grantResults == PackageManager.PERMISSION_GRANTED ) { } else { Toast.makeText(mainAct.getApplicationContext(), "Sorry, this example requires access to your location in order to work properly.\n\nPlease grant location permission.", Toast.LENGTH_SHORT).show(); } return; } } }
private void injectData() { if (!isLoading) { final Thread t = new Thread(new Runnable() {
@Override public void run() {
isLoading = true;
final int WAIT_FOR_LOCATION_STEP_MS = 2000;
while (lastKnownLocaton==null && !mainAct.isFinishing()) {
mainAct.runOnUiThread(new Runnable() {
@Override public void run() { Toast.makeText(mainAct.getApplicationContext(), R.string.location_fetching, Toast.LENGTH_SHORT).show(); } });
if (lastKnownLocaton!=null && !mainAct.isFinishing()) { // TODO: you may replace this dummy implementation and instead load POI information e.g. from your database poiData = getPoiInformation(lastKnownLocaton, 20); callJavaScript("World.loadPoisFromJsonData", new String { poiData.toString() }); }
isLoading = false; } }); t.start(); } }
/** * call JacaScript in architectView */ private void callJavaScript(final String methodName, final String arguments) { final StringBuilder argumentsString = new StringBuilder(""); for (int i= 0; i<arguments.length; i++) { argumentsString.append(arguments); if (i<arguments.length-1) { argumentsString.append(", "); } }
public static JSONArray getPoiInformation(final Location userLocation, final int numberOfPlaces) {
if (userLocation==null) { return null; }
final JSONArray pois = new JSONArray();
// ensure these attributes are also used in JavaScript when extracting POI data final String ATTR_ID = "id"; final String ATTR_NAME = "name"; final String ATTR_DESCRIPTION = "description"; final String ATTR_LATITUDE = "latitude"; final String ATTR_LONGITUDE = "longitude"; final String ATTR_ALTITUDE = "altitude";
for (int i=1;i <= numberOfPlaces; i++) { final HashMap<String, String> poiInformation = new HashMap<String, String>(); poiInformation.put(ATTR_ID, String.valueOf(i)); poiInformation.put(ATTR_NAME, "POI#" + i); poiInformation.put(ATTR_DESCRIPTION, "This is the description of POI#" + i); double poiLocationLatLon = getRandomLatLonNearby(userLocation.getLatitude(), userLocation.getLongitude()); poiInformation.put(ATTR_LATITUDE, String.valueOf(poiLocationLatLon)); poiInformation.put(ATTR_LONGITUDE, String.valueOf(poiLocationLatLon)); final float UNKNOWN_ALTITUDE = -32768f; // equals "AR.CONST.UNKNOWN_ALTITUDE" in JavaScript (compare AR.GeoLocation specification) // Use "AR.CONST.UNKNOWN_ALTITUDE" to tell ARchitect that altitude of places should be on user level. Be aware to handle altitude properly in locationManager in case you use valid POI altitude value (e.g. pass altitude only if GPS accuracy is <7m). poiInformation.put(ATTR_ALTITUDE, String.valueOf(UNKNOWN_ALTITUDE)); pois.put(new JSONObject(poiInformation)); }
return pois; }
/** * helper for creation of dummy places. * @param lat center latitude * @param lon center longitude * @return lat/lon values in given position's vicinity */ private static double getRandomLatLonNearby(final double lat, final double lon) { return new double { lat + Math.random()/5-0.1 , lon + Math.random()/5-0.1}; } }
Bkumari3