/** * Returns the currently selected stop sort order as the index in R.array.sort_stops * * @return the currently selected stop sort order as the index in R.array.sort_stops */ public static int getStopSortOrderFromPreferences() { Resources r = Application.get().getResources(); SharedPreferences settings = Application.getPrefs(); String[] sortOptions = r.getStringArray(R.array.sort_stops); String sortPref = settings.getString(r.getString(R.string.preference_key_default_stop_sort), sortOptions[0]); if (sortPref.equalsIgnoreCase(sortOptions[0])) { return 0; } else if (sortPref.equalsIgnoreCase(sortOptions[1])) { return 1; } return 0; // Default to the first option }
/** * Returns an icon for the vehicle that should be shown on the map * * @param isRealtime true if the marker shown indicate real-time info, false if it should * indicate schedule * @param status the vehicles status to add to the map * @param response the response which contained the provided status * @return an icon for the vehicle that should be shown on the map */ private BitmapDescriptor getVehicleIcon( boolean isRealtime, ObaTripStatus status, ObaTripsForRouteResponse response) { int colorResource; Resources r = Application.get().getResources(); if (isRealtime) { long deviationMin = TimeUnit.SECONDS.toMinutes(status.getScheduleDeviation()); colorResource = ArrivalInfo.computeColorFromDeviation(deviationMin); } else { colorResource = R.color.stop_info_scheduled_time; } int color = r.getColor(colorResource); double direction = MathUtils.toDirection(status.getOrientation()); int halfWind = MathUtils.getHalfWindIndex((float) direction, NUM_DIRECTIONS - 1); // Log.d(TAG, "VehicleId=" + status.getVehicleId() + ", orientation= " + // status.getOrientation() + ", direction=" + direction + ", halfWind= " + halfWind + ", // deviation=" + status.getScheduleDeviation()); String key = createBitmapCacheKey(halfWind, colorResource); Bitmap b = getBitmapFromCache(key); if (b == null) { // Cache miss - create Bitmap and add to cache b = UIUtils.colorBitmap(vehicle_icons[halfWind], color); addBitmapToCache(key, b); } return BitmapDescriptorFactory.fromBitmap(b); }
/** Cache the core black template Bitmaps used for vehicle icons */ private static final void loadIcons() { // Initialize variables used for all marker icons Resources r = Application.get().getResources(); // Black vehicle icons if (vehicle_icons[0] == null) { vehicle_icons[0] = createVehicleIcon(NORTH); vehicle_icons[1] = createVehicleIcon(NORTH_EAST); vehicle_icons[2] = createVehicleIcon(EAST); vehicle_icons[3] = createVehicleIcon(SOUTH_EAST); vehicle_icons[4] = createVehicleIcon(SOUTH); vehicle_icons[5] = createVehicleIcon(SOUTH_WEST); vehicle_icons[6] = createVehicleIcon(WEST); vehicle_icons[7] = createVehicleIcon(NORTH_WEST); vehicle_icons[8] = createVehicleIcon(NO_DIRECTION); } /** * Cache for colored versions of the vehicle icons. Total possible number of entries is 9 * directions * 4 color types (early, ontime, delayed, scheduled) = 36. In a test, the * RouteMapController used around 15 bitmaps over a 30 min period for 4 vehicles on the map at * 10 sec refresh rate. This can be more depending on the route configuration (if the route has * lots of curves) and number of vehicles. To conserve memory, we'll set the max cache size at * 15. */ final int MAX_CACHE_SIZE = 15; if (mVehicleColoredIconCache == null) { mVehicleColoredIconCache = new LruCache<>(MAX_CACHE_SIZE); } }
/** Cache the BitmapDescriptors that hold the images used for icons */ private static final void loadIcons() { // Initialize variables used for all marker icons Resources r = Application.get().getResources(); mPx = r.getDimensionPixelSize(R.dimen.map_stop_shadow_size_6); mArrowWidthPx = mPx / 2f; // half the stop icon size mArrowHeightPx = mPx / 3f; // 1/3 the stop icon size float arrowSpacingReductionPx = mPx / 10f; mBuffer = mArrowHeightPx - arrowSpacingReductionPx; // Set offset used to position the image for markers (see getX/YPercentOffsetForDirection()) // This allows the current selection marker to land on the middle of the stop marker circle mPercentOffset = (mBuffer / (mPx + mBuffer)) * 0.5f; mArrowPaintStroke = new Paint(); mArrowPaintStroke.setColor(Color.WHITE); mArrowPaintStroke.setStyle(Paint.Style.STROKE); mArrowPaintStroke.setStrokeWidth(1.0f); mArrowPaintStroke.setAntiAlias(true); bus_stop_icons[0] = createBusStopIcon(NORTH); bus_stop_icons[1] = createBusStopIcon(NORTH_WEST); bus_stop_icons[2] = createBusStopIcon(WEST); bus_stop_icons[3] = createBusStopIcon(SOUTH_WEST); bus_stop_icons[4] = createBusStopIcon(SOUTH); bus_stop_icons[5] = createBusStopIcon(SOUTH_EAST); bus_stop_icons[6] = createBusStopIcon(EAST); bus_stop_icons[7] = createBusStopIcon(NORTH_EAST); bus_stop_icons[8] = createBusStopIcon(NO_DIRECTION); }
protected void initCheckedState() { setChecked( Application.getPrefs() .getBoolean( getContext().getString(R.string.preference_key_experimental_regions), DEFAULT_VALUE)); }
/** * Creates a vehicle icon with the given direction arrow, or without a direction arrow if the * direction is NO_DIRECTION. Color is black so they can be tinted later. * * @param direction vehicle direction, obtained from status.orientation, translated to direction, * and defined in constants in this class, or NO_DIRECTION if the vehicle icon shouldn't have * a direction arrow * @return a vehicle icon bitmap with the arrow pointing the given direction, or with no arrow if * direction is NO_DIRECTION */ private static Bitmap createVehicleIcon(int direction) throws NullPointerException { if (direction < 0 || direction >= NUM_DIRECTIONS) { throw new IllegalArgumentException("Invalid direction - " + direction); } Resources r = Application.get().getResources(); Bitmap b; switch (direction) { case NORTH: b = BitmapFactory.decodeResource(r, R.drawable.ic_marker_with_bus_smaller_north_inside); break; case NORTH_EAST: b = BitmapFactory.decodeResource( r, R.drawable.ic_marker_with_bus_smaller_north_east_inside); break; case EAST: b = BitmapFactory.decodeResource(r, R.drawable.ic_marker_with_bus_smaller_east_inside); break; case SOUTH_EAST: b = BitmapFactory.decodeResource( r, R.drawable.ic_marker_with_bus_smaller_south_east_inside); break; case SOUTH: b = BitmapFactory.decodeResource(r, R.drawable.ic_marker_with_bus_smaller_south_inside); break; case SOUTH_WEST: b = BitmapFactory.decodeResource( r, R.drawable.ic_marker_with_bus_smaller_south_west_inside); break; case WEST: b = BitmapFactory.decodeResource(r, R.drawable.ic_marker_with_bus_smaller_west_inside); break; case NORTH_WEST: b = BitmapFactory.decodeResource( r, R.drawable.ic_marker_with_bus_smaller_north_west_inside); break; case NO_DIRECTION: default: b = BitmapFactory.decodeResource(r, R.drawable.ic_marker_with_bus_smaller_none_inside); } return b; }
@Override public boolean onMarkerClick(Marker marker) { long startTime = Long.MAX_VALUE, endTime = Long.MAX_VALUE; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { startTime = SystemClock.elapsedRealtimeNanos(); } ObaStop stop = mMarkerData.getStopFromMarker(marker); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { endTime = SystemClock.elapsedRealtimeNanos(); Log.d( TAG, "Stop HashMap read time: " + TimeUnit.MILLISECONDS.convert(endTime - startTime, TimeUnit.NANOSECONDS) + "ms"); } if (stop == null) { // The marker isn't a stop that is contained in this StopOverlay - return unhandled return false; } if (BuildConfig.DEBUG) { // Show the stop_id in a toast for debug purposes Toast.makeText(mActivity, stop.getId(), Toast.LENGTH_SHORT).show(); } doFocusChange(stop); // Report Stop distance metric Location stopLocation = stop.getLocation(); Location myLocation = Application.getLastKnownLocation(mActivity, null); // Track the users distance to bus stop ObaAnalytics.trackBusStopDistance(stop.getId(), myLocation, stopLocation); return true; }
public static void saveBoolean(String key, boolean value) { saveBoolean(Application.getPrefs(), key, value); }
public static void saveLong(String key, long value) { saveLong(Application.getPrefs(), key, value); }
public static void saveInt(String key, int value) { saveInt(Application.getPrefs(), key, value); }
/** * Creates a bus stop icon with the given direction arrow, or without a direction arrow if the * direction is NO_DIRECTION * * @param direction Bus stop direction, obtained from ObaStop.getDirection() and defined in * constants in this class, or NO_DIRECTION if the stop icon shouldn't have a direction arrow * @return a bus stop icon bitmap with the arrow pointing the given direction, or with no arrow if * direction is NO_DIRECTION */ private static Bitmap createBusStopIcon(String direction) throws NullPointerException { if (direction == null) { throw new IllegalArgumentException(direction); } Resources r = Application.get().getResources(); Context context = Application.get(); Float directionAngle = null; // 0-360 degrees Bitmap bm; Canvas c; Drawable shape; Float rotationX = null, rotationY = null; // Point around which to rotate the arrow Paint arrowPaintFill = new Paint(); arrowPaintFill.setStyle(Paint.Style.FILL); arrowPaintFill.setAntiAlias(true); if (direction.equals(NO_DIRECTION)) { // Don't draw the arrow bm = Bitmap.createBitmap(mPx, mPx, Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, 0, bm.getWidth(), bm.getHeight()); } else if (direction.equals(NORTH)) { directionAngle = 0f; bm = Bitmap.createBitmap(mPx, (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, (int) mBuffer, mPx, bm.getHeight()); // Shade with darkest color at tip of arrow arrowPaintFill.setShader( new LinearGradient( bm.getWidth() / 2, 0, bm.getWidth() / 2, mArrowHeightPx, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // For NORTH, no rotation occurs - use center of image anyway so we have some value rotationX = bm.getWidth() / 2f; rotationY = bm.getHeight() / 2f; } else if (direction.equals(NORTH_WEST)) { directionAngle = 315f; // Arrow is drawn N, rotate 315 degrees bm = Bitmap.createBitmap( (int) (mPx + mBuffer), (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds((int) mBuffer, (int) mBuffer, bm.getWidth(), bm.getHeight()); // Shade with darkest color at tip of arrow arrowPaintFill.setShader( new LinearGradient( 0, 0, mBuffer, mBuffer, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // Rotate around below coordinates (trial and error) rotationX = mPx / 2f + mBuffer / 2f; rotationY = bm.getHeight() / 2f - mBuffer / 2f; } else if (direction.equals(WEST)) { directionAngle = 0f; // Arrow is drawn pointing West, so no rotation bm = Bitmap.createBitmap((int) (mPx + mBuffer), mPx, Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds((int) mBuffer, 0, bm.getWidth(), bm.getHeight()); arrowPaintFill.setShader( new LinearGradient( 0, bm.getHeight() / 2, mArrowHeightPx, bm.getHeight() / 2, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // For WEST rotationX = bm.getHeight() / 2f; rotationY = bm.getHeight() / 2f; } else if (direction.equals(SOUTH_WEST)) { directionAngle = 225f; // Arrow is drawn N, rotate 225 degrees bm = Bitmap.createBitmap( (int) (mPx + mBuffer), (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds((int) mBuffer, 0, bm.getWidth(), mPx); arrowPaintFill.setShader( new LinearGradient( 0, bm.getHeight(), mBuffer, bm.getHeight() - mBuffer, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // Rotate around below coordinates (trial and error) rotationX = bm.getWidth() / 2f - mBuffer / 4f; rotationY = mPx / 2f + mBuffer / 4f; } else if (direction.equals(SOUTH)) { directionAngle = 180f; // Arrow is drawn N, rotate 180 degrees bm = Bitmap.createBitmap(mPx, (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, 0, bm.getWidth(), (int) (bm.getHeight() - mBuffer)); arrowPaintFill.setShader( new LinearGradient( bm.getWidth() / 2, bm.getHeight(), bm.getWidth() / 2, bm.getHeight() - mArrowHeightPx, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); rotationX = bm.getWidth() / 2f; rotationY = bm.getHeight() / 2f; } else if (direction.equals(SOUTH_EAST)) { directionAngle = 135f; // Arrow is drawn N, rotate 135 degrees bm = Bitmap.createBitmap( (int) (mPx + mBuffer), (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, 0, mPx, mPx); arrowPaintFill.setShader( new LinearGradient( bm.getWidth(), bm.getHeight(), bm.getWidth() - mBuffer, bm.getHeight() - mBuffer, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // Rotate around below coordinates (trial and error) rotationX = (mPx + mBuffer / 2) / 2f; rotationY = bm.getHeight() / 2f; } else if (direction.equals(EAST)) { directionAngle = 180f; // Arrow is drawn pointing West, so rotate 180 bm = Bitmap.createBitmap((int) (mPx + mBuffer), mPx, Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, 0, mPx, bm.getHeight()); arrowPaintFill.setShader( new LinearGradient( bm.getWidth(), bm.getHeight() / 2, bm.getWidth() - mArrowHeightPx, bm.getHeight() / 2, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); rotationX = bm.getWidth() / 2f; rotationY = bm.getHeight() / 2f; } else if (direction.equals(NORTH_EAST)) { directionAngle = 45f; // Arrow is drawn pointing N, so rotate 45 degrees bm = Bitmap.createBitmap( (int) (mPx + mBuffer), (int) (mPx + mBuffer), Bitmap.Config.ARGB_8888); c = new Canvas(bm); shape = ContextCompat.getDrawable(context, R.drawable.map_stop_icon); shape.setBounds(0, (int) mBuffer, mPx, bm.getHeight()); // Shade with darkest color at tip of arrow arrowPaintFill.setShader( new LinearGradient( bm.getWidth(), 0, bm.getWidth() - mBuffer, mBuffer, r.getColor(R.color.theme_primary), r.getColor(R.color.theme_accent), Shader.TileMode.MIRROR)); // Rotate around middle of circle rotationX = (float) mPx / 2; rotationY = bm.getHeight() - (float) mPx / 2; } else { throw new IllegalArgumentException(direction); } shape.draw(c); if (direction.equals(NO_DIRECTION)) { // Everything after this point is for drawing the arrow image, so return the bitmap as-is for // no arrow return bm; } /** * Draw the arrow - all dimensions should be relative to px so the arrow is drawn the same size * for all orientations */ // Height of the cutout in the bottom of the triangle that makes it an arrow (0=triangle) final float CUTOUT_HEIGHT = mPx / 12; Path path = new Path(); float x1 = 0, y1 = 0; // Tip of arrow float x2 = 0, y2 = 0; // lower left float x3 = 0, y3 = 0; // cutout in arrow bottom float x4 = 0, y4 = 0; // lower right if (direction.equals(NORTH) || direction.equals(SOUTH) || direction.equals(NORTH_EAST) || direction.equals(SOUTH_EAST) || direction.equals(NORTH_WEST) || direction.equals(SOUTH_WEST)) { // Arrow is drawn pointing NORTH // Tip of arrow x1 = mPx / 2; y1 = 0; // lower left x2 = (mPx / 2) - (mArrowWidthPx / 2); y2 = mArrowHeightPx; // cutout in arrow bottom x3 = mPx / 2; y3 = mArrowHeightPx - CUTOUT_HEIGHT; // lower right x4 = (mPx / 2) + (mArrowWidthPx / 2); y4 = mArrowHeightPx; } else if (direction.equals(EAST) || direction.equals(WEST)) { // Arrow is drawn pointing WEST // Tip of arrow x1 = 0; y1 = mPx / 2; // lower left x2 = mArrowHeightPx; y2 = (mPx / 2) - (mArrowWidthPx / 2); // cutout in arrow bottom x3 = mArrowHeightPx - CUTOUT_HEIGHT; y3 = mPx / 2; // lower right x4 = mArrowHeightPx; y4 = (mPx / 2) + (mArrowWidthPx / 2); } path.setFillType(Path.FillType.EVEN_ODD); path.moveTo(x1, y1); path.lineTo(x2, y2); path.lineTo(x3, y3); path.lineTo(x4, y4); path.lineTo(x1, y1); path.close(); // Rotate arrow around (rotationX, rotationY) point Matrix matrix = new Matrix(); matrix.postRotate(directionAngle, rotationX, rotationY); path.transform(matrix); c.drawPath(path, arrowPaintFill); c.drawPath(path, mArrowPaintStroke); return bm; }
@Override protected void onClick() { if (!isChecked()) { /* Warn the user before enabling, since experimental regions may not have real-time info or may be unavailable. */ AlertDialog dialog = new AlertDialog.Builder(getContext()) .setMessage(R.string.preferences_experimental_regions_enable_warning) .setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); setValue(true); } }) .setNegativeButton( android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .create(); dialog.show(); } else { if (Application.get().getCurrentRegion() != null && Application.get().getCurrentRegion().getExperimental()) { // If the user is currently using an experimental region, warn that it won't be available AlertDialog dialog = new AlertDialog.Builder(getContext()) .setMessage(R.string.preferences_experimental_regions_disable_warning) .setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); // Set the region info to null, so we no longer use the current experimental // region Application.get().setCurrentRegion(null); setValue(false); } }) .setNegativeButton( android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .create(); dialog.show(); } else { setValue(false); } } }