package de.blau.android.util; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.support.design.widget.FloatingActionButton; import android.support.v4.view.ViewCompat; import android.support.v4.widget.NestedScrollView; import android.util.Log; import android.view.Display; import android.view.View; import android.widget.ScrollView; import de.blau.android.Logic; import de.blau.android.osm.BoundingBox; import de.blau.android.osm.Node; import de.blau.android.osm.OsmElement; import de.blau.android.osm.Relation; import de.blau.android.osm.StorageDelegator; import de.blau.android.osm.Tags; import de.blau.android.osm.Way; public class Util { private static final String DEBUG_TAG = "Util"; static public ArrayList<String> getArrayList(String s) { ArrayList<String> v = new ArrayList<String>(); v.add(s); return v; } static public LinkedHashMap<String,ArrayList<String>> getArrayListMap(Map<String,String>map) { LinkedHashMap<String,ArrayList<String>> result = new LinkedHashMap<String,ArrayList<String>>(); for (Entry<String, String> e:map.entrySet()) { result.put(e.getKey(), getArrayList(e.getValue())); } return result; } /** * Sort a list of ways in the order they are connected, there is likely a far better algorithm than this * Note: assumes that ways could be reversed * @param list * @return null if not connected or not all ways */ static public List<OsmElement> sortWays(List<OsmElement>list) { List<OsmElement> result = new ArrayList<OsmElement>(); List<OsmElement> unconnected = new ArrayList<OsmElement>(list); OsmElement e = unconnected.get(0); unconnected.remove(0); if (!e.getName().equals(Way.NAME)) { return null; // not all are ways } result.add(e); while (true) { boolean found = false; for (OsmElement w:unconnected) { if (!w.getName().equals(Way.NAME)) { return null; // not all are ways } // this is a bit complicated because we don't want to reverse ways just yet Node firstNode1 = ((Way) result.get(0)).getFirstNode(); Node firstNode2 = ((Way) result.get(0)).getLastNode(); Node lastNode1 = ((Way) result.get(result.size()-1)).getFirstNode(); Node lastNode2 = ((Way) result.get(result.size()-1)).getLastNode(); Node wFirstNode = ((Way)w).getFirstNode(); Node wLastNode = ((Way)w).getLastNode(); if (wFirstNode.equals(firstNode1) || wFirstNode.equals(firstNode2) || wLastNode.equals(firstNode1) || wLastNode.equals(firstNode2)) { result.add(0,w); unconnected.remove(w); found = true; break; } else if (wFirstNode.equals(lastNode1) || wFirstNode.equals(lastNode2) || wLastNode.equals(lastNode1) || wLastNode.equals(lastNode2)) { result.add(w); unconnected.remove(w); found = true; break; } } if (!found && unconnected.size() > 0) { return null; } else if (unconnected.size() == 0) { return result; } } } /** * Safely return a short cut (aka one character) from the string resources * @param ctx * @param id * @return character or 0 if no short cut can be found */ static public char getShortCut(Context ctx,int id) { String s = ctx.getString(id); if (s != null && s.length() >= 1) { return s.charAt(0); } else { return 0; } } /** * Get the location of the center of the given osm-element * @param delegator * @param osmElementType * @param osmId * @return {lat, lon} or null */ public static int[] getCenter(final StorageDelegator delegator, final String osmElementType, long osmId) { OsmElement osmElement = delegator.getOsmElement(osmElementType, osmId); if (osmElement instanceof Node) { Node n = (Node) osmElement; return new int[] {n.getLat(), n.getLon()}; } if (osmElement instanceof Way) { double[] coords = Logic.centroidLonLat((Way)osmElement); if (coords != null) { return new int[] {(int) (coords[1]*1E7), (int) (coords[0]*1E7)}; } } if (osmElement instanceof Relation) { // the center of the bounding box is naturally just a rough estimate BoundingBox bbox = osmElement.getBounds(); if (bbox != null) { return new int[] {(int)(bbox.getCenterLat()*1E7), bbox.getLeft()+(bbox.getRight()-bbox.getLeft())/2}; } } return null; } /** * Convert a list to a semicolon separated string * @param list * @return string containing the individual list values separated by ; or the empty string if list is null or empty */ public static String listToOsmList(List<String> list) { String osmList = ""; if (list != null) { for (String i:list) { if (!"".equals(osmList)) { osmList = osmList + ";"; } osmList = osmList + i; } } return osmList; } @SuppressLint("NewApi") public static boolean isLandscape(Activity activity) { // reliable determine if we are in landscape mode Display display = activity.getWindowManager().getDefaultDisplay(); Point size = new Point(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { display.getSize(size); } else { //noinspection deprecation size.x = display.getWidth(); //noinspection deprecation size.y = display.getHeight(); } return isLarge(activity) && size.x > size.y; } @SuppressLint("NewApi") public static boolean isLarge(Activity activity) { int screenSize = activity.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { return screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE; } return (screenSize == Configuration.SCREENLAYOUT_SIZE_LARGE || screenSize == Configuration.SCREENLAYOUT_SIZE_XLARGE); } /** * Scroll to the supplied view * @param sv the ScrollView or NestedScrollView to scroll * @param row the row to display, if null scrool to top or bottom of sv * @param up * @param force */ public static void scrollToRow(final View sv, final View row,final boolean up, boolean force) { Rect scrollBounds = new Rect(); sv.getHitRect(scrollBounds); Log.d(DEBUG_TAG, "scrollToRow bounds " + scrollBounds); if (row != null && row.getLocalVisibleRect(scrollBounds)&& !force) { return; // already on screen } if (row==null) { Log.d(DEBUG_TAG, "scrollToRow scrolling to top or bottom"); sv.post(new Runnable() { @Override public void run() { if (sv != null && sv instanceof ScrollView) { ((ScrollView)sv).fullScroll(up ? ScrollView.FOCUS_UP : ScrollView.FOCUS_DOWN); } else if (sv != null && sv instanceof NestedScrollView) { ((NestedScrollView)sv).fullScroll(up ? ScrollView.FOCUS_UP : ScrollView.FOCUS_DOWN); } else { Log.e(DEBUG_TAG, "scrollToRow unexpected view " + sv); } } }); } else { Log.d(DEBUG_TAG, "scrollToRow scrolling to row"); sv.post(new Runnable() { @SuppressLint("NewApi") @Override public void run() { final int target = up ? row.getTop(): row.getBottom(); if (sv != null && sv instanceof ScrollView) { ((ScrollView)sv).smoothScrollTo(0, target); } else if (sv != null && sv instanceof NestedScrollView) { ((NestedScrollView)sv).smoothScrollTo(0, target); } else { Log.e(DEBUG_TAG, "scrollToRow unexpected view " + sv); } } }); } } public static void setBackgroundTintList(FloatingActionButton fab, ColorStateList tint) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { fab.setBackgroundTintList(tint); } else { ViewCompat.setBackgroundTintList(fab, tint); } } /** * Group keys so that i18n follow the base key (which should always come first in map) * @param <V> * @param map */ public static <V> void groupI18nKeys(Map<String,V> map) { LinkedHashMap<String,V> temp = new LinkedHashMap<String,V>(); ArrayList<String> keys = new ArrayList<String>(map.keySet()); while (keys.size()>0) { String key = keys.get(0); keys.remove(0); if (Tags.I18N_NAME_KEYS.contains(key)) { temp.put(key, map.get(key)); int i = 0; while (keys.size()>0 && i < keys.size()) { String i18nKey = keys.get(i); if (i18nKey.startsWith(key+":")) { temp.put(i18nKey, map.get(i18nKey)); keys.remove(i); } else { i++; } } } else { temp.put(key, map.get(key)); } } map.clear(); map.putAll(temp); } /** * Convert first letter of v to upper case using English * @param v * @return */ public static String capitalize(String v) { if (v != null && v.length() > 0) { char[] a = v.toCharArray(); a[0] = Character.toUpperCase(a[0]); return String.valueOf(a); } return v; } /** * Set the alpha value for a FAB, just a workaround of support lib brokenness * @param fab the floating action button * @param fabalpha the alpha value */ public static void setAlpha(FloatingActionButton fab, float fabalpha) { ViewCompat.setAlpha(fab, fabalpha); } /** * Share the supplied position with other apps * * @param activity this activity * @param lonLat coordinates to sahre */ public static void sharePosition(Activity activity, double[] lonLat) { if (lonLat != null) { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); Uri geo = Uri.parse("geo:"+lonLat[1]+","+lonLat[0]); Log.d(DEBUG_TAG,"sharing " + geo); Intent geoIntent = new Intent(Intent.ACTION_VIEW, geo); activity.startActivity(geoIntent); } } /** * Check that a double is not zero * @param a the double to test * @return true if not zero */ public static boolean notZero(double a) { return a < -Double.MIN_VALUE || a > Double.MIN_VALUE; } }