I have a WMS tile source overlay on my Osmdroid map.
On the overridden getTileURLString function I add time dimension to get a tile from a specific time.
#Override
public String getTileURLString(long pMapTileIndex) {
//https://stackoverflow.com/questions/28436050/how-to-work-osmdroid-with-web-map-service-wms-used-by-the-private-provider
String baseUrl = getBaseUrl();
StringBuilder sb = new StringBuilder(baseUrl);
if (!baseUrl.endsWith("&"))
sb.append("&");
sb.append("request=GetMap&width=").append(getTileSizePixels()).append("&height=").append(getTileSizePixels()).append("&version=").append("1.3.0");
sb.append("&layers=").append("radar-swiss:radarswiss");
sb.append("&bbox=");
MapView.WebMercatorBoundingBox bb = new MapView.WebMercatorBoundingBox(MapTileIndex.getX(pMapTileIndex), MapTileIndex.getY(pMapTileIndex), MapTileIndex.getZoom(pMapTileIndex));
sb.append(bb.getWest()).append(",");
sb.append(bb.getSouth()).append(",");
sb.append(bb.getEast()).append(",");
sb.append(bb.getNorth());
sb.append("&srs=").append("EPSG:3857");
sb.append("&format=image/png&transparent=true");
sb.append("&tiled=true");
if (currentFrameTiming != null) {
String mDateTime = getDateTimeFromTimeStamp(currentFrameTiming.getTimeInMillis(), WMSTimeFormat);
sb.append("&time=").append(mDateTime);
}
L.d("rewritten url: " + sb.toString());
return sb.toString();
}
Everything works fine but even time parameter seems to be ignored when osmdroid caches tiles. Even if time parameter is modified, the tile is not downloaded, but taken from the local cache.
How can I fix this?
Related
I've been trying to simply call an api on an android build supporting 64 bit (IL2CPP build) and the UnityWebRequest class didnt seem to work. It's being called via a simple ui button click. It hits the webRequest.SendWebRequest(); and nothing happens. Ive tried the following samples. One, directly from the Unity docs for UnityWebRequest and others using standard HttpClient.
UnityWebRequest:
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Authorization", "Bearer " + API_KEY);
yield return webRequest.SendWebRequest();
if (webRequest.isNetworkError)
{
debugText.text = ": Error: " + webRequest.error;
coroutineAllowed = false;
}
else
{
debugText.text = ":\nReceived: " + webRequest.downloadHandler.text;
dynamic jsonObj = JsonConvert.DeserializeObject(webRequest.downloadHandler.text);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
}
debugText.text = businessResults.Count.ToString();
//coroutineAllowed = true;
}
debugText.text = "getRequest 4";
}
}
This unfortunately did nothing at the yield return webRequest.SendWebRequest();
The next sample I tried was using HttpClient():
IEnumerator HttpClientCall(string uri) //possibly wrap in IEnumerator
{
debugText.text += "http coroutine started" +Environment.NewLine;
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY);
var response = httpClient.GetAsync(uri);
if (response.Result.StatusCode != HttpStatusCode.OK)
{
debugText.text += "FAILED HTTP GET";
}
yield return response.Result.Content.ReadAsStringAsync();
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
debugText.text += Environment.NewLine + ((string)obj["name"]);
}
}
}
Once again, nothing when it hits yield return response.Result.Content.ReadAsStringAsync();
These all work on PC, and they both return results that i'm expecting.
The next thing i heard was about setting the android manifest application tag with android:usesCleartextTraffic="true"
This unfortunately, also did nothing for me lol. I know it has to be the 64 support, because this works on a standard build. The moment i go to build with 64 support, it doesnt work.
Any help on why it's not returning appropriately would be very helpful.
side note, i know the code is pretty ugly, but after i can figure out why the build doesnt work on the device a heavy refactoring is going to be in play. Thanks in advance!
So after a lot of trouble shooting ive found out why this was not working. The main issue seems to be stemming from my use of the standard Newtonsoft Json package when Unity, apparently, has their own internal JsonUtility class. After changing this:
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
To This:
var js = JsonUtility.FromJson<T>(response.Result.Content.ReadAsStringAsync().Result);
my results are finally showing in the the apk build correctly.
Also, to note that to map correctly, the JsonUtility.FromJson must be typed to a class that exactly mirrors the incoming json object explicitly.
The page article that finally helped me with this issue is here.
P.S.
Thank you to #RetiredNinja for trying to help instead of just downvoting and saying nothing of value. You're amazing!
My goal is to create an Android app which download a map from ArcGIS portal when connected to internet, then use them offline. I would like to use service pattern, so later the app can have synchronization feature. I followed a tutorial from ArcGIS here.
I am currently stuck at downloading the map part. I expect the downloaded map is in mobile map package (.mmpk), but instead my download directory have a package.info file, and a folder of geodatabase and .mmap files as image shown here. Based on my understanding, I should have an .mmpk file to use them offline.
Following the tutorial steps, I am able to (1) create an offline map task, (2) specify the parameters, and (3) examine the offline capabilities. However in step (4) generate and download the offline map, I expect the downloaded map will be in mobile map package (.mmpk) but its not; as i mentioned above with image shown. In step (5) open and use the offline map, i am able to view offline map when using mobile map package (.mmpk) file that i transfer manually into the device. I also tried to open and use my downloaded (.mmap) file but no map showed up.
My full code by steps is shown below:
(1) create an offline map task
// Load map from a portal item
final Portal portal = new Portal("http://www.arcgis.com");
final PortalItem webmapItem = new PortalItem(portal, "acc027394bc84c2fb04d1ed317aac674");
// Create map and add it to the view
myMap = new ArcGISMap(webmapItem);
mMapView = (MapView) findViewById(R.id.mapView);
mMapView.setMap(myMap);
// Create task and set parameters
final OfflineMapTask offlineMapTask = new OfflineMapTask(myMap);
(2) specify the parameters
// Create default parameters
final ListenableFuture<GenerateOfflineMapParameters> parametersFuture = offlineMapTask.createDefaultGenerateOfflineMapParametersAsync(areaOfInterest);
parametersFuture.addDoneListener(new Runnable() {
#Override
public void run() {
try {
final GenerateOfflineMapParameters parameters = parametersFuture.get();
// Update the parameters if needed
// Limit maximum scale to 5000 but take all the scales above (use 0 as a MinScale)
parameters.setMaxScale(5000);
parameters.setIncludeBasemap(false);
// Set attachment options
parameters.setAttachmentSyncDirection(GenerateGeodatabaseParameters.AttachmentSyncDirection.UPLOAD);
parameters.setReturnLayerAttachmentOption(GenerateOfflineMapParameters.ReturnLayerAttachmentOption.EDITABLE_LAYERS);
// Request the table schema only (existing features won't be included)
parameters.setReturnSchemaOnlyForEditableLayers(true);
// Update the title to contain the region
parameters.getItemInfo().setTitle(parameters.getItemInfo().getTitle() + " (Central)");
// Create new item info
final OfflineMapItemInfo itemInfo = new OfflineMapItemInfo();
// Override thumbnail with the new image based on the extent
final ListenableFuture<Bitmap> exportImageFuture = mMapView.exportImageAsync();
exportImageFuture.addDoneListener(new Runnable() {
#Override
public void run() {
try {
Bitmap mapImage = exportImageFuture.get();
// Scale to thumbnail size
Bitmap thumbnailImage = Bitmap.createScaledBitmap(mapImage, 200, 133, false);
// Convert to byte[]
ByteArrayOutputStream stream = new ByteArrayOutputStream();
thumbnailImage.compress(Bitmap.CompressFormat.JPEG, 50, stream);
byte[] thumbnailBytes = stream.toByteArray();
stream.close();
// Set values to the itemInfo
itemInfo.setThumbnailData(thumbnailBytes);
itemInfo.setTitle("Water network (Central)");
itemInfo.setSnippet(webmapItem.getSnippet()); // Copy from the source map
itemInfo.setDescription(webmapItem.getDescription()); // Copy from the source map
itemInfo.setAccessInformation(webmapItem.getAccessInformation()); // Copy from the source map
itemInfo.getTags().add("Water network");
itemInfo.getTags().add("Data validation");
// Set metadata to parameters
parameters.setItemInfo(itemInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
});
(3) examine the offline capabilities
final ListenableFuture<OfflineMapCapabilities> offlineMapCapabilitiesFuture =
offlineMapTask.getOfflineMapCapabilitiesAsync(parameters);
offlineMapCapabilitiesFuture.addDoneListener(new Runnable() {
#Override
public void run() {
try {
OfflineMapCapabilities offlineMapCapabilities = offlineMapCapabilitiesFuture.get();
if (offlineMapCapabilities.hasErrors()) {
// Handle possible errors with layers
for (java.util.Map.Entry<Layer, OfflineCapability> layerCapability :
offlineMapCapabilities.getLayerCapabilities().entrySet()) {
if (!layerCapability.getValue().isSupportsOffline()) {
showMessage(layerCapability.getKey().getName() + " cannot be taken offline.");
showMessage("Error : " + layerCapability.getValue().getError().getMessage());
}
}
// Handle possible errors with tables
for (java.util.Map.Entry<FeatureTable, OfflineCapability> tableCapability :
offlineMapCapabilities.getTableCapabilities().entrySet()) {
if (!tableCapability.getValue().isSupportsOffline()) {
showMessage(tableCapability.getKey().getTableName() + " cannot be taken offline.");
showMessage("Error : " + tableCapability.getValue().getError().getMessage());
}
}
} else {
// All layers and tables can be taken offline!
showMessage("All layers are good to go!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
(4) generate and download the offline map
String mExportPath = String.valueOf(getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)) + File.separator + "New";
showMessage(mExportPath);
// Create and start a job to generate the offline map
final GenerateOfflineMapJob generateOfflineJob =
offlineMapTask.generateOfflineMap(parameters, mExportPath);
// Show that job started
final ProgressBar progressBarOffline = (ProgressBar) findViewById(R.id.progressBarOffline);
progressBarOffline.setVisibility(View.VISIBLE);
generateOfflineJob.start();
generateOfflineJob.addJobDoneListener(new Runnable() {
#Override
public void run() {
// Generate the offline map and download it
GenerateOfflineMapResult result = generateOfflineJob.getResult();
if (!result.hasErrors()) {
showMessage("no error");
mobileMapPackage = result.getMobileMapPackage();
// Job is finished and all content was generated
showMessage("Map " + mobileMapPackage.getItem().getTitle() +
" saved to " + mobileMapPackage.getPath());
// Show offline map in a MapView
mMapView.setMap(result.getOfflineMap());
// Show that job completed
progressBarOffline.setVisibility(View.INVISIBLE);
} else {
showMessage("error");
// Job is finished but some of the layers/tables had errors
if (result.getLayerErrors().size() > 0) {
for (java.util.Map.Entry<Layer, ArcGISRuntimeException> layerError : result.getLayerErrors().entrySet()) {
showMessage("Error occurred when taking " + layerError.getKey().getName() + " offline.");
showMessage("Error : " + layerError.getValue().getMessage());
}
}
if (result.getTableErrors().size() > 0) {
for (java.util.Map.Entry<FeatureTable, ArcGISRuntimeException> tableError : result.getTableErrors().entrySet()) {
showMessage("Error occurred when taking " + tableError.getKey().getTableName() + " offline.");
showMessage("Error : " + tableError.getValue().getMessage());
}
}
// Show that job completed
progressBarOffline.setVisibility(View.INVISIBLE);
}
}
});
(5) open and use the offline map
// Create the mobile map package
final MobileMapPackage mapPackage = new MobileMapPackage(mobileMapPackage.getPath());
// Load the mobile map package asynchronously
mapPackage.loadAsync();
// Add done listener which will invoke when mobile map package has loaded
mapPackage.addDoneLoadingListener(new Runnable() {
#Override
public void run() {
// Check load status and that the mobile map package has maps
if(mapPackage.getLoadStatus() == LoadStatus.LOADED && mapPackage.getMaps().size() > 0){
// Cdd the map from the mobile map package to the MapView
mMapView.setMap(mapPackage.getMaps().get(0));
}else{
// Log an issue if the mobile map package fails to load
showMessage(mapPackage.getLoadError().getMessage());
}
}
});
showMessage() in my code is showing Toast.
public void showMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
I worry if my .mmpk expectation is wrong, or my step goes wrong somewhere because I still not fully understand the whole process. This is my first time working with ArcGIS map in Android. I could not find much sample code to experiment, so really appreciate someone who could help.
Thank you!
The task created an exploded mobile map package, which works just the same as a .mmpk file. Open it like this:
final MobileMapPackage mapPackage =
new MobileMapPackage("/data/com.geoinfo.asmasyakirah.arcgis/files/Documents/New");
(If you can't access it there, you might want to generate the mobile map package in Environment.getExternalStorageDirectory() instead of Environment.DIRECTORY_DOCUMENTS.)
According to the documentation for the MobileMapPackage constructor:
Creates a new MobileMapPackage from the .mmpk file or exploded mobile map package at the given path.
If you really must have it as a .mmpk file, simply zip it using an Android API for making zip files and name it .mmpk instead of .zip.
Kinda late on the topic but i had several days working on this and found out something that may help some of you :
I created my mapData via this class : https://github.com/Esri/arcgis-runtime-samples-java/blob/master/src/main/java/com/esri/samples/map/generate_offline_map/GenerateOfflineMapSample.java
As you can see it creates a folder containing package.info + p13 (in which you find geodatabase file + mmap file)
WHen i tried offline to load this data, no errors appeared but the layer was empty and i could just see the carroying.
In fact after much more tries, i had to check that besides geodatabase and mmap file i could find a .tpk file (TilePackaged)
This one was never available (somehow due to network issues during the online download) and nothing alerted me.
Now that this tpk file is there, all items are clearly displayed like 'water network'
TL;DR; : check that tpk file is donwloaded during the online preparation.
I am writing an Android App to access Google Drive Photos via CloudRail service. I am able to authenticate to the Google account in question and see all my files/folders in the Google Drive, but I can't access photos from Google Photos.
While browsing through the Drive API documentation, it makes a reference to spaces, specifically 3 spaces are defined: drive, photos and allDataFolder.
Where do I specify the spaces that I am interested in? But default, the drive space is being accessed. Even though, I specifically specified scope for photos:
https://www.googleapis.com/auth/drive.photos.readonly
And when Google Authentication pages opens in the mobile browser, it states that my app wants to gain access to the user's Google Photos and I grant this access. But when calling CloudRail service to get children, no photos are visible
`googledriveChildren = mGoogledriveService.getChildren("/"); // returns goole drive top level files/folders
`googledriveChildren = mGoogledriveService.getChildren("/photos"); // generates a NotFoundException
I have already been down this path and achieved the integration - with the help/guidance from the folks at Cloudrail. You should note that my integration is limited to reading/downloading from Google Photos. I have not found any way to write/upload. Nor have I found any way of reading the album structure that can be set up in Google Photos.
First, you need to include the scope for Google Photos. I did this as follows:
public static final String GOOGLE_PHOTOS_SCOPE = "https://www.googleapis.com/auth/drive.photos.readonly";
private final AtomicReference<CloudStorage> googlephotos = new AtomicReference<>();
List<String> scope = new ArrayList<>();
scope.add(My_Constants.GOOGLE_PHOTOS_SCOPE);
googlephotos.set(new GoogleDrive(context, google_client_id, "", Get.GetString(R.string.google_redirect_uri),
Get.GetString(R.string.google_authentication_state), scope));
((GoogleDrive) googlephotos.get()).useAdvancedAuthentication();
You then need to build a Cloudrail advancedRequest to download whatever data you want. I download the metadata I require as follows:
CloudStrorage service = googlephotos.get();
private void searchForGooglePhotos(final CloudStorage service) throws Throwable {
GoogleDrive google_drive = (GoogleDrive) service;
boolean more = true;
String pageToken = null;
while (more) {
StringBuilder builder = new StringBuilder();
String query = URLEncoder.encode("mimeType='image/jpeg' and trashed = false", "utf-8");
builder.append("/files?spaces=photos");
if (pageToken != null) {
builder.append("&pageToken=");
builder.append(pageToken);
}
builder.append("&q=");
builder.append(query);
builder.append("&fields=nextPageToken,files(id,name,modifiedTime,description,size," +
"imageMediaMetadata(height,rotation,width,time))");
AdvancedRequestSpecification specification = new AdvancedRequestSpecification(builder.toString());
AdvancedRequestResponse response = google_drive.advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> resultObjectMap = (Map<String, Object>) response.getBodyJsonParsed();
pageToken = (String) resultObjectMap.get("nextPageToken");
#SuppressWarnings("unchecked")
ArrayList<Map<String, Object>> filesObjectMap = ((ArrayList<Map<String, Object>>) resultObjectMap.get("files"));
for (Map<String, Object> fileObjectMap : filesObjectMap) {
// process downloaded files
}
more = (pageToken != null);
}
}
Subsequently in my app I use Glide to download the photos themselves when required. In the Glide DataFetcher I obtain the inputStream using:
if (model.getSourceRecord().isTypeGooglePhotos()) {
AdvancedRequestSpecification specification;
AdvancedRequestResponse response;
if (model.getIsThumbnail()) {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() +
"?spaces=photos&fields=thumbnailLink");
response = ((GoogleDrive) service).advancedRequest(specification);
#SuppressWarnings("unchecked")
Map<String, Object> parsed = (Map<String, Object>) response.getBodyJsonParsed();
String link = (String) parsed.get("thumbnailLink");
specification = new AdvancedRequestSpecification(link);
specification.disableBaseUrl();
} else {
specification = new AdvancedRequestSpecification("/files" + model.getSourceId() + "?spaces=photos&alt=media");
}
response = ((GoogleDrive) service).advancedRequest(specification);
input_stream = response.getBodyAsStream();
} else {
if (model.getIsThumbnail()) {
input_stream = service.getThumbnail(model.getSourceId());
} else {
input_stream = service.download(model.getSourceId());
}
}
Here, "model" contains various info associated with each photo. The sourceId comes from the "id" downloaded:
String source_id = java.io.File.separator + fileObjectMap.get("id");
I hope this helps.
Would anyone arriving at this question / response please note that, as of mid Jan 2018, Google have "sunset" (sic) the photos space (spaces=photos above). This means that the above solution no longer works.
On the Google REST API documentation: "The photos space will sunset in early January 2018. Your users can continue to access Google Photos via the drive space by enabling the Google Photos folder in My Drive in the Drive client settings"
Ugh!
I am using Universal Image Loader to display images downloaded from a URI or already available in a disk cache implementation.
I want to display music album covers, but more than one track might have the same URI for a cover (i.e. tracks from the same album). I want that even if the image is the same it's stored each time for each different track with the track name, because I want users to be able to replace the default covers with a custom one, even for each single track.
For instance
01 - Track 01.mp3
02 - Track 02.mp3
Belong to the same album and the cover URI is http://something/img.jpg, on disk cache I want to have
01 - Track 01.jpg
02 - Track 02.jpg
even if it's the same image.
So I've coded a FileNameGenerator that stores a SetĀ of hashes for each Uri, where the hash is the SHA-1 of the absolute path of the file.
Here is my implementation:
public MyFileNameGenerator(String ext) {
super();
this.ext = ext;
}
HashMap<String,HashSet<String>> names = new HashMap<String, HashSet<String>>();
#Override
public String generate(String imageUri) {
if(imageUri==null) return null;
if (imageUri.startsWith("file:///")) {
return FilenameUtils.removeExtension(Uri.parse(imageUri)
.getLastPathSegment()) + "."+ext;
}
//How to recognize the correct hash?
//return FilenameUtils.removeExtension(Data.currentFiles
// .get(names.get(imageUri)).getName()) + "." + ext;
}
public void setTrackData(String uri, String hash) {
if(!names.containsKey(uri))
names.put(uri, new HashSet<String>());
names.get(uri).add(hash);
}
But I'm at a dead end, because it's impossible to understand for which file I'm displaying the image, as generate only takes imageURI as parameter and more hashes can belong to the same uri.
How could I circumvent this issue?
I think I've found a solution for this.
Whenever I call my ImageLoader instance to show an image, I use
if (uri != null && !uri.isEmpty()) {
uri = Uri.parse(uri).buildUpon()
.appendQueryParameter("myhashkeyparameter", "myHashValue").toString();
}
mImageLoader.displayImage(uri,mImageView);
This code will append to a vaild uri a query parameter, the uri will become:
http://someuri/image.jpg?myhashkeyparameter=myHashValue
Then in the FileNameGenerator's generate method I can use
String hash = Uri.parse(imageUri).getQueryParameter("myhashkeyparameter");
to retrieve the wanted file without relying on using the imageUri as key.
Full code:
#Override
public String generate(String imageUri) {
if(imageUri==null||imageUri.isEmpty()) return "";
if (imageUri.startsWith("file:///")) {
return FilenameUtils.removeExtension(Uri.parse(imageUri)
.getLastPathSegment()) + "."+ext;
}
String hash = Uri.parse(imageUri).getQueryParameter("myhashkeyparameter");
if(null==hash||hash.isEmpty()) return "";
if(Data.currentFiles.containsKey(hash)){
return FilenameUtils.removeExtension(Data.currentFiles.get(hash).getName())+".png";
}
else return "";
}
You just have to be careful that the string used as queryparameter is not already used in the http URL, so avoid traditional names like name,hash,h,title and so on.
I have an image url I parse form json that I want to load into an android widget onto the homescreen. Right now I am trying to do it this way but its wrong:
ImageDownloadTask imageD = new ImageDownloadTask(image);
views.setImageViewBitmap(R.id.image, imageD.execute(image));
image is a string holding a url to an image that needs to be downloaded and I am trying to set it to R.id.image
I found another stack question and tried this as a result:
views.setBitmap(R.id.image, "setImageBitmap",BitmapFactory.decodeStream(new URL(image).openStream()));
And when I use that nothing in the app loads at all, none of the text views get set.
My third try was this:
//get beer data
JSONObject o = new JSONObject(result);
String name = getName(o);
String image = getImage(o);
String abv = getABV(o);
String ibu = getIBU(o);
String glass = getGlass(o);
String beerBreweryName = getBreweryName(o);
String beerBreweryStyle = getBreweryStyle(o);
String beerDescription = getDescription(o);
InputStream in = new java.net.URL(image).openStream();
Bitmap bitmap = BitmapFactory.decodeStream(in);
views.setTextViewText(R.id.beerTitle, name);
views.setTextViewText(R.id.beerBreweryName, beerBreweryName);
views.setTextViewText(R.id.beerStyleName, beerBreweryStyle);
views.setImageViewBitmap(R.id.image, bitmap);
This gave the same result as the last attempt, it would not even set any text views....
Just tried another attempt after one of the answers posted below:
RemoteViews views = new RemoteViews(c.getPackageName(), R.layout.widget_test);
//get beer data
JSONObject o = new JSONObject(result);
String name = getName(o);
String imageURL = getImage(o);
String abv = getABV(o);
String ibu = getIBU(o);
String glass = getGlass(o);
String beerBreweryName = getBreweryName(o);
String beerBreweryStyle = getBreweryStyle(o);
String beerDescription = getDescription(o);
Log.d("widgetImage" , imageURL);
views.setImageViewUri(R.id.image, Uri.parse(imageURL));
views.setTextViewText(R.id.beerTitle, name);
views.setTextViewText(R.id.beerBreweryName, beerBreweryName);
views.setTextViewText(R.id.beerStyleName, beerBreweryStyle);
mgr.updateAppWidget(appWidgetIds, views);
This attempt lets all the text views load, but no image ever shows up.
The way to do this reliably is to use setImageViewURI on the remote ImageView. The trick is that the URI you give it is a content:// URI which then points back to a content provider that you export from your application. In your content provider you can do anything you need to do to supply the image bytes.
For example, in your manifest:
<provider android:name=".ImageContentProvider" android:authorities="com.example.test" android:exported="true" />
And your provider:
public class ImageContentProvider extends ContentProvider {
// (Removed overrides that do nothing)
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
List<String> segs = uri.getPathSegments();
// Download the image content here, get the info you need from segs
return ParcelFileDescriptor.open(new File(path), ParcelFileDescriptor.MODE_READ_ONLY);
}
}
And then your URL is something like:
content://com.example.test/something-you-can-define/here
This is necessary because your remote image view is not running in your process. You are much more limited in what you can do because everything must be serialized across the process boundary. The URI can serialize just fine but if you try to send a megabyte of image data with setImageViewBitmap, it's probably going to fail (depending on available device memory).
Got a lot of help from multiple sources for this question. The big problem for me why a bunch of the attempts I tried listed above seemed to lock the widget app and not load anything is because I can not download the image and set it in a UI thread.
To accomplish this I had to move everything to the do in background of my async task and not in the onPostExecute.