/* * Copyright 2014 Google Inc. All rights reserved. * * 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.google.samples.apps.iosched.util; import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.MarkerOptions; import com.google.maps.android.ui.IconGenerator; import com.google.samples.apps.iosched.R; import com.google.samples.apps.iosched.map.util.MarkerModel; import com.jakewharton.disklrucache.DiskLruCache; import android.content.Context; import android.support.annotation.DrawableRes; import android.text.TextUtils; import java.io.File; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Locale; import static com.google.samples.apps.iosched.util.LogUtils.LOGD; import static com.google.samples.apps.iosched.util.LogUtils.LOGE; public class MapUtils { public static final String ICON_RESOURCE_PREFIX = "map_marker_"; private static final String TILE_PATH = "maptiles"; private static final String TAG = LogUtils.makeLogTag(MapUtils.class); public static final String TYPE_ICON_PREFIX = "ICON_"; /** * Returns the room type for a {@link com.google.samples.apps.iosched.map.util.MarkerModel} * for a given String. */ public static int detectMarkerType(String markerType) { if (TextUtils.isEmpty(markerType)) { return MarkerModel.TYPE_INACTIVE; } String tags = markerType.toUpperCase(Locale.US); if (tags.contains("SESSION")) { return MarkerModel.TYPE_SESSION; } else if (tags.contains("PLAIN")) { return MarkerModel.TYPE_PLAIN; } else if (tags.contains("LABEL")) { return MarkerModel.TYPE_LABEL; } else if (tags.contains("CODELAB")) { return MarkerModel.TYPE_CODELAB; } else if (tags.contains("SANDBOX")) { return MarkerModel.TYPE_SANDBOX; } else if (tags.startsWith(TYPE_ICON_PREFIX)) { return MarkerModel.TYPE_ICON; } else if (tags.contains("OFFICEHOURS")) { return MarkerModel.TYPE_OFFICEHOURS; } else if (tags.contains("MISC")) { return MarkerModel.TYPE_MISC; } else if (tags.contains("MOSCONE")) { return MarkerModel.TYPE_VENUE; } else if (tags.contains("INACTIVE")) { return MarkerModel.TYPE_INACTIVE; } return MarkerModel.TYPE_INACTIVE; // default } /** * Returns the drawable Id of icon to use for a room type. */ public static @DrawableRes int getRoomIcon(int markerType) { switch (markerType) { case MarkerModel.TYPE_SESSION: return R.drawable.ic_map_session; case MarkerModel.TYPE_PLAIN: return R.drawable.ic_map_pin; case MarkerModel.TYPE_CODELAB: return R.drawable.ic_map_codelab; case MarkerModel.TYPE_SANDBOX: return R.drawable.ic_map_sandbox; case MarkerModel.TYPE_OFFICEHOURS: return R.drawable.ic_map_officehours; case MarkerModel.TYPE_MISC: return R.drawable.ic_map_misc; case MarkerModel.TYPE_VENUE: return R.drawable.ic_map_venue; default: return R.drawable.ic_map_pin; } } /** * True if the info details for this room type should only contain a title. */ public static boolean hasInfoTitleOnly(int markerType) { return markerType == MarkerModel.TYPE_PLAIN; } /** * True if the info details for this room type contain a title and a list of sessions. */ public static boolean hasInfoSessionList(int markerType) { return markerType != MarkerModel.TYPE_INACTIVE && markerType != MarkerModel.TYPE_LABEL && markerType != MarkerModel.TYPE_CODELAB && markerType != MarkerModel.TYPE_ICON; } /** * True if the info details for this room type contain a title and a list of sessions. */ public static boolean hasInfoFirstDescriptionOnly(int markerType) { return markerType == MarkerModel.TYPE_CODELAB; } public static boolean hasInfoSessionListIcons(int markerType) { return markerType == MarkerModel.TYPE_SANDBOX; } /** * Creates a marker for a session. * * @param id Id to be embedded as the title */ public static MarkerOptions createPinMarker(String id, LatLng position) { final BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.map_marker_unselected); return new MarkerOptions().position(position).title(id).icon(icon).anchor(0.5f, 0.85526f) .visible( false); } /** * Creates a new IconGenerator for labels on the map. */ public static IconGenerator getLabelIconGenerator(Context c) { IconGenerator iconFactory = new IconGenerator(c); iconFactory.setTextAppearance(R.style.MapLabel); iconFactory.setBackground(null); return iconFactory; } /** * Creates a marker for a label. * * @param iconFactory Reusable IconFactory * @param id Id to be embedded as the title * @param label Text to be shown on the label */ public static MarkerOptions createLabelMarker(IconGenerator iconFactory, String id, LatLng position, String label) { final BitmapDescriptor icon = BitmapDescriptorFactory.fromBitmap(iconFactory.makeIcon(label)); return new MarkerOptions().position(position).title(id).icon(icon) .anchor(0.5f, 0.5f) .visible(false); } /** * Creates a marker for an icon. The icon is selected in {@link #getDrawableForIconType(Context, * String)} and anchored at the bottom center for the location. */ public static MarkerOptions createIconMarker(final String iconType, final String id, LatLng position, Context context) { final int iconResource = getDrawableForIconType(context, iconType); if (iconResource < 1) { // Not a valid icon type. return null; } final BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(iconResource); return new MarkerOptions().position(position).title(id).icon(icon) .anchor(0.5f, 1f) .visible(false); } /** * Returns the drawable resource id for an icon marker. The resource name is generated by * prefixing #ICON_RESOURCE_PREFIX to the icon type in lower case. Returns 0 if no resource with * this name exists. */ private static int getDrawableForIconType(final Context context, final String iconType) { if (iconType == null || !iconType.startsWith(TYPE_ICON_PREFIX)) { return 0; } // Return the ID of the resource that matches the iconType name. // If no resources matches this name, returns 0. return context.getResources() .getIdentifier(ICON_RESOURCE_PREFIX + iconType.toLowerCase(), "drawable", context.getPackageName()); } /** * Creates a marker for the venue. */ public static MarkerOptions createVenueMarker(LatLng position) { final String title = "VENUE"; final BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.map_marker_venue); return new MarkerOptions().position(position).title(title).icon(icon) .visible(false); } private static String[] mapTileAssets; /** * Returns true if the given tile file exists as a local asset. */ public static boolean hasTileAsset(Context context, String filename) { //cache the list of available files if (mapTileAssets == null) { try { mapTileAssets = context.getAssets().list("maptiles"); } catch (IOException e) { // no assets mapTileAssets = new String[0]; } } // search for given filename for (String s : mapTileAssets) { if (s.equals(filename)) { return true; } } return false; } /** * Copy the file from the assets to the map tiles directory if it was * shipped with the APK. */ public static boolean copyTileAsset(Context context, String filename) { if (!hasTileAsset(context, filename)) { // file does not exist as asset return false; } // copy file from asset to internal storage try { InputStream is = context.getAssets().open(TILE_PATH + File.separator + filename); File f = getTileFile(context, filename); FileOutputStream os = new FileOutputStream(f); byte[] buffer = new byte[1024]; int dataSize; while ((dataSize = is.read(buffer)) > 0) { os.write(buffer, 0, dataSize); } os.close(); } catch (IOException e) { return false; } return true; } /** * Return a {@link File} pointing to the storage location for map tiles. */ public static File getTileFile(Context context, String filename) { File folder = new File(context.getFilesDir(), TILE_PATH); if (!folder.exists()) { folder.mkdirs(); } return new File(folder, filename); } public static void removeUnusedTiles(Context mContext, final ArrayList<String> usedTiles) { // remove all files are stored in the tile path but are not used File folder = new File(mContext.getFilesDir(), TILE_PATH); File[] unused = folder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String filename) { return !usedTiles.contains(filename); } }); if (unused != null) { for (File f : unused) { f.delete(); } } } public static boolean hasTile(Context mContext, String filename) { return getTileFile(mContext, filename).exists(); } private static final int MAX_DISK_CACHE_BYTES = 1024 * 1024 * 2; // 2MB public static DiskLruCache openDiskCache(Context c) { File cacheDir = new File(c.getCacheDir(), "tiles"); try { return DiskLruCache.open(cacheDir, 1, 3, MAX_DISK_CACHE_BYTES); } catch (IOException e) { LOGE(TAG, "Couldn't open disk cache."); } return null; } public static void clearDiskCache(Context c) { DiskLruCache cache = openDiskCache(c); if (cache != null) { try { LOGD(TAG, "Clearing map tile disk cache"); cache.delete(); cache.close(); } catch (IOException e) { // ignore } } } /** * Checks whether two LatLngBounds intersect. * * @return true if the given bounds intersect. */ public static boolean boundsIntersect(LatLngBounds first, LatLngBounds second) { // First check if the latitudes are not intersecting. if (first.northeast.latitude < second.southwest.latitude || first.southwest.latitude > second.northeast.latitude) { return false; } // Next, check if the longitudes are not intersecting. if (first.northeast.longitude < second.southwest.longitude || first.southwest.longitude > second.northeast.longitude) { return false; } // Both latitude and longitude are intersecting. return true; } }