Hi, I am currently building an Android App using ArcGIS runtime SDK for Android. The app has a MapView on the layout and I plan to make it work offline without wifi. Thus, I have provided myself several .tpk files as offline areas. Under working WiFi connection, it worked well (the map is displayed, along with the offline tiles). But, when I turned off the WiFi and restarted the app, it only shows blank (black) map
I used the OnStatusChangedListener to listen to the MapView status. Here is the code snippet
mapView.setOnStatusChangedListener(new OnStatusChangedListener() { @Override public void onStatusChanged(Object o, STATUS status) { //initialization if (o == mapView && status == STATUS.INITIALIZED){ //setup everything, including adding offline maps here //...
I was under impression that the INITIALIZED status requires connecting to the internet. That's why I couldn't use the map or anything because I ran it without the WiFi turned on.
If that's the case, how can I use the MapView without using internet connection, when initializing the map itself requires internet connection? Is it actually one of the limitation of the SDK?
THX before
Solved! Go to Solution.
package com.arcgis.androidsupportcases.offlinestarter;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISLocalTiledLayer;
public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
MapView mMapView;
final static int EXTERNAL_STORAGE_PERMISSION_CODE = 3774;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapView = (MapView) findViewById(R.id.map);
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
loadLayer();
} else {
requestStorage();
}
}
private void requestStorage() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//TODO: Explanation if wanted.
} else {
ActivityCompat.requestPermissions(this,
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
EXTERNAL_STORAGE_PERMISSION_CODE);
}
}
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {@link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
* @param permissions The requested permissions. Never null.
* @param grantResults The grant results for the corresponding permissions
* which is either {@link PackageManager#PERMISSION_GRANTED}
* or {@link PackageManager#PERMISSION_DENIED}. Never null.
* @see #requestPermissions(String[], int)
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == EXTERNAL_STORAGE_PERMISSION_CODE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadLayer();
} else {
Log.e("ESS", "Permission Denied");
}
}
}
private void loadLayer() {
mMapView.addLayer(new ArcGISLocalTiledLayer(Environment.getExternalStorageDirectory().getPath() + "/ArcGIS/" + "custom.tpk"));
}
}
I was able to write a really quick sample to test this out. I ignored the check to the initialization and was able to load my TPK without issue.
I am seeing the same behavior that you explained when doing the initialization check.
The grids seem to only appear when a dataset is loaded. Are you loading any datasets when you see the black map? (I have only tested with a TPK thus far)
Are you inflating the MapView from XML or are you creating the MapView dynamically?
If you are inflating from XML can you not include the basemap URL in the XML?
Does this help?
I have checked on my code. There's no basemap URL included there. Without Wifi or internet connection, it fails to initialize the map. I put all of the map setup code inside the if (status == STATUS.INITIALIZED) block, so it doesn't load up properly due to the calling IF statement is false.
I have tried moving it out of the IF block (so it will initialize the map regardless of the status change), but it's still the same old black map (doesn't even show the grids).
package com.arcgis.androidsupportcases.offlinestarter;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISLocalTiledLayer;
public class MainActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
MapView mMapView;
final static int EXTERNAL_STORAGE_PERMISSION_CODE = 3774;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMapView = (MapView) findViewById(R.id.map);
int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
loadLayer();
} else {
requestStorage();
}
}
private void requestStorage() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//TODO: Explanation if wanted.
} else {
ActivityCompat.requestPermissions(this,
new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},
EXTERNAL_STORAGE_PERMISSION_CODE);
}
}
/**
* Callback for the result from requesting permissions. This method
* is invoked for every call on {@link #requestPermissions(String[], int)}.
* <p>
* <strong>Note:</strong> It is possible that the permissions request interaction
* with the user is interrupted. In this case you will receive empty permissions
* and results arrays which should be treated as a cancellation.
* </p>
*
* @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
* @param permissions The requested permissions. Never null.
* @param grantResults The grant results for the corresponding permissions
* which is either {@link PackageManager#PERMISSION_GRANTED}
* or {@link PackageManager#PERMISSION_DENIED}. Never null.
* @see #requestPermissions(String[], int)
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == EXTERNAL_STORAGE_PERMISSION_CODE) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadLayer();
} else {
Log.e("ESS", "Permission Denied");
}
}
}
private void loadLayer() {
mMapView.addLayer(new ArcGISLocalTiledLayer(Environment.getExternalStorageDirectory().getPath() + "/ArcGIS/" + "custom.tpk"));
}
}
I was able to write a really quick sample to test this out. I ignored the check to the initialization and was able to load my TPK without issue.
I am seeing the same behavior that you explained when doing the initialization check.
The grids seem to only appear when a dataset is loaded. Are you loading any datasets when you see the black map? (I have only tested with a TPK thus far)
I have some .tpk files that I manage to load by selecting them from a list. Whenever the map is blank (no internet connection), the .tpk file was not shown on the map. If it has internet connection, the grid shows up and the selected .tpk file can be shown correctly.
What kind of datasets are we talking about?
The stuff I did after the initialization (which is now moved outside the OnStatusChangedListener), which is related to the MapView, is just adding Graphics Layer for marker and regions, and turning on the location listener.
EDIT: Can u also provide me the XML file you use for that example? Thank you
Any offline datasets should be available. TPK's, offline features, etc...
If you follow the sample from above, are you able to see your TPK loaded? Are you getting any errors when loading your TPK? At this point, I would like to know how testing outside of the mapViews statusChanges listener worked for you.
Better yet, could you share your TPK with me and I can test with my sample and see if I reproduce the same issue that you are experiencing?
I will try this tomorrow, as I am currently not on my workspace. I'll also provide a sample tpk I used for this
EDIT:
I tried loading the .tpk directly when offline and not using the OnStatusChangeListener. It shows the grid and offline map without internet connection.
I cannot help but wonder, that u need to load any offline features just to get the map started. In my opinion, it should have started regardless whether you have any offline features or not (and by then you can dynamically add more offline features on the run, not adding 1 or all of them on first init).
EDIT2:
Now came another problem. I have some TPK files that are spread across a country. The problem is, I cannot pan the map to other location other than my location.
E.g. My last position is at Kansas and I have 3 TPK files for Colorado, Oklahoma, and Arizona. I cannot scroll or pan my map to Oklahoma or Arizona because it's quite far from my current position. It seems that there's this invisible boundary that limits my panning
EDIT3:
last edit. Finally got it working. I just need to programamtically set the max extent of the map using MapView.setMaxExtent(). I took the (xmin,ymin) and (xmax, ymax) from ArcGIS street map's REST API and now I could navigate to any location on the map
I am inflating the map from XML and I don't include any basemap URL in the XML. It's just a MapView with usual id, width, height, mapOptions set to Street and zoom set to 15. No basemap URL
The STATUS.INITIALIZED is not triggerred on the code. Instead, without internet connection, it will give you something like "Init failed". I am not on my workspace right now. I will provide better explanation once I get there.
> It's just a MapView with usual id, width, height, mapOptions set to Street and zoom set to 15. No basemap URL
You dont need an internet connection to initialize a MapView. However, if you have used the mapOptions attribute, then the map view will be initialized using one of the Esri default basemap map services - so would require an internet connection. I would remove the mapOptions attribute (you can leave the MapView definition there). Then in your code, check if you have an internet connection - if so, you can programmatically add one of the services (Folder: / ) to your map as the base layer, which will initialize the map. If you dont have a connection, then you can go ahead and programmatically add your local data layers.
Hope this helps,
Shelly