/*
* Copyright 2015. Appsi Mobile
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.appsimobile.appsii.module.weather;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.RetryPolicy;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.RequestFuture;
import com.android.volley.toolbox.Volley;
import com.appsimobile.appsii.R;
import com.appsimobile.appsii.ResponseParserException;
import com.appsimobile.appsii.SimpleJson;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Created by nick on 12/04/15.
*/
public class ImageDownloadHelper {
public static final int MAX_CACHE_SIZE = 10 * 1024 * 1024;
public static final String FLICKR_API_KEY = "11ed8a19c24662dc6b18979902dc6494";
static final String extras = "tags, url_o, url_h";
private static ImageDownloadHelper sImageDownloadHelper;
final RequestQueue sRequestQueue;
final ImageLoader sImageLoader;
private ImageDownloadHelper(Context context) {
sRequestQueue = Volley.newRequestQueue(context, null, MAX_CACHE_SIZE);
sImageLoader = new ImageLoader(sRequestQueue, createImageCache()) {
final RetryPolicy mRetryPolicy = new DefaultRetryPolicy(
30000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
@Override
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth,
int maxHeight,
ImageView.ScaleType scaleType, String cacheKey) {
Request<Bitmap> result = super.
makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, cacheKey);
result.setRetryPolicy(mRetryPolicy);
return result;
}
};
}
/**
* Creates a new ImageCache object, used to simplify the constructor
*/
private static ImageLoader.ImageCache createImageCache() {
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
return new FlickrImageCache(cacheSize);
}
public synchronized static ImageDownloadHelper getInstance(Context context) {
if (sImageDownloadHelper == null) {
sImageDownloadHelper = new ImageDownloadHelper(context.getApplicationContext());
}
return sImageDownloadHelper;
}
public static int getRotationFromJson(JSONObject result) {
SimpleJson simpleJson = new SimpleJson(result);
try {
return simpleJson.getInt("rotation", 0);
} catch (ResponseParserException e) {
Log.e("ImageDownloadHelper", "No rotation attr", e);
return 0;
}
}
public static int getFallbackDrawableForConditionCode(boolean isDay, int conditionCode) {
// http://developer.yahoo.com/weather/
switch (conditionCode) {
case 19: // dust or sand
case 21: // haze
case 20: // foggy
case 22: // smoky
case 24: // windy
case 25: // cold
case 26: // cloudy
case 27: // mostly cloudy (night)
case 28: // mostly cloudy (day)
case 29: // partly cloudy (night)
case 30: // partly cloudy (day)
case 44: // partly cloudy
return isDay ? R.drawable.weather_cloudy_day : R.drawable.weather_cloudy_night;
case 31: // clear (night)
case 33: // fair (night)
return R.drawable.weather_clear_night;
default:
case 34: // fair (day)
case 32: // sunny
case 36: // hot
return isDay ? R.drawable.weather_clear_day : R.drawable.weather_clear_night;
case 0: // tornado
case 2: // hurricane
case 1: // tropical storm
case 4: // thunderstorms
case 23: // blustery
// TODO: add wind
case 3: // severe thunderstorms
// TODO: add thunder
case 5: // mixed rain and snow
case 6: // mixed rain and sleet
case 7: // mixed snow and sleet
case 8: // freezing drizzle
case 10: // freezing rain
case 13: // snow flurries
case 14: // light snow showers
case 42: // scattered snow showers
case 15: // blowing snow
case 16: // snow
case 41: // heavy snow
case 43: // heavy snow
case 46: // snow showers
return isDay ? R.drawable.weather_snowy_day : R.drawable.weather_snowy_night;
case 18: // sleet
case 9: // drizzle
case 11: // showers
case 12: // showers
case 17: // hail
case 35: // mixed rain and hail
case 37: // isolated thunderstorms
case 38: // scattered thunderstorms
case 39: // scattered thunderstorms
case 40: // scattered showers
case 45: // thundershowers
case 47: // isolated thundershowers
return isDay ? R.drawable.weather_cloudy_day : R.drawable.weather_cloudy_night;
}
}
public static void getEligiblePhotosFromResponse(@Nullable JSONObject jsonObject,
List<PhotoInfo> result,
int minDimension) {
result.clear();
if (jsonObject == null) return;
JSONObject photos = jsonObject.optJSONObject("photos");
if (photos == null) return;
JSONArray photoArr = photos.optJSONArray("photo");
if (photoArr == null) return;
int N = photoArr.length();
for (int i = 0; i < N; i++) {
JSONObject object = photoArr.optJSONObject(i);
if (object == null) continue;
String id = object.optString("id");
if (TextUtils.isEmpty(id)) continue;
String urlH = urlFromImageObject(
object, "url_h", "width_h", "height_h", minDimension - 100);
String urlO = urlFromImageObject(
object, "url_o", "width_o", "height_o", minDimension - 100);
if (urlH != null) {
result.add(new PhotoInfo(id, urlH));
} else if (urlO != null) {
result.add(new PhotoInfo(id, urlO));
}
}
}
static String urlFromImageObject(JSONObject object, String u, String w, String h,
int minDimension) {
String url = object.optString(u);
if (TextUtils.isEmpty(url)) return null;
int heightO = object.optInt(h);
int widthO = object.optInt(w);
if (heightO < minDimension || widthO < minDimension) return null;
return url;
}
private static String getTagForConditionCode(int conditionCode) {
// http://developer.yahoo.com/weather/
switch (conditionCode) {
case 19: // dust or sand
return "dust";
case 21: // haze
return "haze";
case 20: // foggy
return "fog";
case 22: // smoky
return "smoky";
case 24: // windy
return "windy";
case 25: // cold
return "cold";
case 26: // cloudy
case 27: // mostly cloudy (night)
case 28: // mostly cloudy (day)
case 29: // partly cloudy (night)
case 30: // partly cloudy (day)
case 44: // partly cloudy
return "cloudy";
case 31: // clear (night)
case 33: // fair (night)
case 34: // fair (day)
return "clear";
case 32: // sunny
return "sunny";
case 36: // hot
return "hot";
case 0: // tornado
return "tornado";
case 2: // hurricane
return "hurricane";
case 1: // tropical storm
case 3: // severe thunderstorms
case 4: // thunderstorms
case 23: // blustery
return "thunder";
case 5: // mixed rain and snow
case 6: // mixed rain and sleet
case 7: // mixed snow and sleet
case 8: // freezing drizzle
case 10: // freezing rain
case 18: // sleet
return "snow";
case 9: // drizzle
return "drizzle";
case 11: // showers
case 12: // showers
return "showers";
case 17: // hail
case 35: // mixed rain and hail
return "hail";
case 37: // isolated thunderstorms
case 38: // scattered thunderstorms
case 39: // scattered thunderstorms
return "thunder";
case 40: // scattered showers
return "rainy";
case 45: // thundershowers
case 47: // isolated thundershowers
return "thunder";
case 13: // snow flurries
case 14: // light snow showers
case 42: // scattered snow showers
case 15: // blowing snow
case 16: // snow
case 41: // heavy snow
case 43: // heavy snow
case 46: // snow showers
return "snow";
}
return "clear";
}
public ImageLoader getImageLoader() {
return sImageLoader;
}
@Nullable
public JSONObject searchCityWeatherPhotos(String woeid, int conditionCode, boolean day)
throws VolleyError {
String tags = getTagForConditionCode(conditionCode) + "," + (day ? "day" : "night");
Uri uri = Uri.parse("https://api.flickr.com/services/rest/").buildUpon().
appendQueryParameter("method", "flickr.photos.search").
appendQueryParameter("api_key", FLICKR_API_KEY).
appendQueryParameter("sort", "relevance").
appendQueryParameter("tags", tags).
appendQueryParameter("tag_mode", "all").
appendQueryParameter("privacy_filter", "1").
appendQueryParameter("content_type", "1").
appendQueryParameter("group_id", "1463451@N25").
// appendQueryParameter("group_id", "1553326@N24").
appendQueryParameter("woe_id", woeid).
appendQueryParameter("media", "photos").
appendQueryParameter("extras", extras).
appendQueryParameter("format", "json").
appendQueryParameter("nojsoncallback", "1").
build();
String url = uri.toString();
RequestFuture<JSONObject> requestFuture = RequestFuture.newFuture();
JsonObjectRequest request =
new JsonObjectRequest(Request.Method.GET, url, requestFuture, requestFuture);
sRequestQueue.add(request);
return getResult(requestFuture);
}
private <T> T getResult(RequestFuture<T> requestFuture) throws VolleyError {
try {
return requestFuture.get(10, TimeUnit.SECONDS);
} catch (InterruptedException | TimeoutException e) {
return null;
} catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof VolleyError) throw (VolleyError) cause;
return null;
}
}
//
// public static void getImageUrlsFromResult(JSONObject jsonObject, List<String> result,
// int minDimension) {
// result.clear();
//
// JSONObject photos = jsonObject.optJSONObject("photos");
// if (photos == null) return;
//
// JSONArray photoArr = photos.optJSONArray("photo");
// if (photoArr == null) return;
//
// int N = photoArr.length();
// for (int i = 0; i < N; i++) {
// JSONObject object = photoArr.optJSONObject(i);
// if (object == null) continue;
//
// String urlH = urlFromImageObject(
// object, "url_h", "width_h", "height_h", minDimension - 100);
//
// if (urlH != null) {
// result.add(urlH);
// } else {
// String urlO = urlFromImageObject(
// object, "url_o", "width_o", "height_o", minDimension - 100);
// if (urlO != null) {
// result.add(urlO);
// }
// }
// }
// }
public JSONObject searchCityImage(String woeid) throws VolleyError {
Uri uri = Uri.parse("https://api.flickr.com/services/rest/").buildUpon().
appendQueryParameter("method", "flickr.photos.search").
appendQueryParameter("api_key", FLICKR_API_KEY).
appendQueryParameter("sort", "relevance").
appendQueryParameter("tag_mode", "all").
appendQueryParameter("privacy_filter", "1").
appendQueryParameter("content_type", "1").
appendQueryParameter("group_id", "1463451@N25").
appendQueryParameter("woe_id", woeid).
appendQueryParameter("media", "photos").
appendQueryParameter("extras", extras).
appendQueryParameter("format", "json").
appendQueryParameter("nojsoncallback", "1").
build();
String url = uri.toString();
RequestFuture<JSONObject> requestFuture = RequestFuture.newFuture();
JsonObjectRequest request =
new JsonObjectRequest(Request.Method.GET, url, requestFuture, requestFuture);
sRequestQueue.add(request);
return getResult(requestFuture);
}
public JSONObject loadPhotoInfo(Context context, String photoId) throws VolleyError {
Uri uri = Uri.parse("https://api.flickr.com/services/rest/").buildUpon().
appendQueryParameter("method", "flickr.photos.getInfo").
appendQueryParameter("api_key", FLICKR_API_KEY).
appendQueryParameter("photo_id", photoId).
appendQueryParameter("format", "json").
appendQueryParameter("nojsoncallback", "1").
build();
String url = uri.toString();
RequestFuture<JSONObject> requestFuture = RequestFuture.newFuture();
JsonObjectRequest request =
new JsonObjectRequest(Request.Method.GET, url, requestFuture, requestFuture);
sRequestQueue.add(request);
return getResult(requestFuture);
}
public static class PhotoInfo {
public final String id;
public final String url;
public PhotoInfo(String id, String url) {
this.id = id;
this.url = url;
}
}
/**
* Implements the ImageCache. This is needed for the ImageLoader.
* This class uses a simply implementation of LruCache to provide
* memory sensitive caching.
*/
private static class FlickrImageCache implements ImageLoader.ImageCache {
private final LruCache<String, Bitmap> mCache;
private final int mCacheSize;
FlickrImageCache(int cacheSize) {
mCacheSize = cacheSize;
mCache = new ImageLruCache(mCacheSize);
}
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
/**
* The memory cache for the images. This is used by the ImageLoader
*/
private static class ImageLruCache extends LruCache<String, Bitmap> {
ImageLruCache(int cacheSize) {
super(cacheSize);
}
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
}
}