Java tutorial
/* * Copyright (C) 2014 Tobias Preuss, Peter Vasil * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.avpptr.umweltzone.utils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.CollectionType; import com.fasterxml.jackson.databind.type.TypeFactory; import org.ligi.tracedroid.logging.Log; import android.content.Context; import android.content.res.Resources; import android.support.annotation.NonNull; import android.support.annotation.RawRes; import android.support.v4.util.LruCache; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.List; import java.util.Locale; import de.avpptr.umweltzone.R; import de.avpptr.umweltzone.Umweltzone; import de.avpptr.umweltzone.analytics.TrackingPoint; import de.avpptr.umweltzone.models.Circuit; import de.avpptr.umweltzone.models.CircuitDeserializer; import de.avpptr.umweltzone.models.Faq; import de.avpptr.umweltzone.models.LowEmissionZone; public abstract class ContentProvider { private static final LruCache<String, List<Circuit>> CIRCUITS_CACHE = new LruCache<String, List<Circuit>>(6); private static final LruCache<String, Integer> RESOURCE_ID_CACHE = new LruCache<String, Integer>(6); public static void enforceContentUpdate() { // Clear caches CIRCUITS_CACHE.evictAll(); RESOURCE_ID_CACHE.evictAll(); } @NonNull public static List<Faq> getFaqs(final Context context) { // Do not accidentally compare with Locale.GERMAN if (Locale.getDefault().equals(Locale.GERMANY)) { return getContent(context, "faqs_de", Faq.class); } return getContent(context, "faqs_en", Faq.class); } @NonNull public static List<LowEmissionZone> getLowEmissionZones(final Context context) { return getContent(context, "zones_de", LowEmissionZone.class); } @NonNull public static List<Circuit> getCircuits(final Context context, final String zoneName) { String keyForZone = generateKeyForZoneWith(zoneName); List<Circuit> circuits = CIRCUITS_CACHE.get(keyForZone); if (circuits == null) { circuits = getContent(context, keyForZone, Circuit.class); CIRCUITS_CACHE.put(keyForZone, circuits); } return circuits; } private static String generateKeyForZoneWith(String zoneName) { return "zone_" + zoneName; } @NonNull private static <T> List<T> getContent(final Context context, final String fileName, Class<T> contentType) { return getContent(context, fileName, "raw", contentType); } @SuppressWarnings("unchecked") // for Collections.EMPTY_LIST @NonNull private static <T> List<T> getContent(final Context context, final String fileName, final String folderName, Class<T> contentType) { int rawResourceId = getResourceId(context, fileName, folderName); InputStream inputStream = context.getResources().openRawResource(rawResourceId); SimpleModule module = new SimpleModule(); module.addDeserializer(Circuit.class, new CircuitDeserializer()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(module); String datePattern = context.getString(R.string.config_zone_number_since_date_format); objectMapper.setDateFormat(new SimpleDateFormat(datePattern, Locale.getDefault())); try { TypeFactory typeFactory = objectMapper.getTypeFactory(); CollectionType collectionType = typeFactory.constructCollectionType(List.class, contentType); return objectMapper.readValue(inputStream, collectionType); } catch (IOException e) { // TODO Aware that app will crash when JSON is mis-structured. e.printStackTrace(); } Log.e(ContentProvider.class.getName(), "Failure parsing zone data for: " + fileName); return Collections.EMPTY_LIST; } @RawRes static Integer getResourceId(Context context, String fileName, String folderName) { String resourceKey = getFilePath(folderName, fileName); Integer rawResourceId = RESOURCE_ID_CACHE.get(resourceKey); if (rawResourceId == null) { // Invoke cache rawResourceId = getRawResourceId(context, fileName, folderName); RESOURCE_ID_CACHE.put(resourceKey, rawResourceId); } return rawResourceId; } @RawRes private static int getRawResourceId(Context context, String fileName, String folderName) { final Resources resources = context.getResources(); // Look-up identifier using reflection (expensive) int rawResourceId = resources.getIdentifier(fileName, folderName, context.getPackageName()); if (rawResourceId == de.avpptr.umweltzone.contract.Resources.INVALID_RESOURCE_ID) { String filePath = getFilePath(folderName, fileName); Umweltzone.getTracker().trackError(TrackingPoint.ResourceNotFoundError, filePath); throw new IllegalStateException("Resource for file path '" + filePath + "' not found."); } return rawResourceId; } private static String getFilePath(final String folderName, final String fileName) { return folderName + "/" + fileName; } }