This commit is contained in:
crimean 2019-05-26 20:58:32 +03:00
commit 7765807d66
76 changed files with 3629 additions and 350 deletions

View file

@ -69,4 +69,5 @@ public class IndexConstants {
public static final String ROUTING_XML_FILE= "routing.xml";
public static final String SETTINGS_DIR = "settings/"; //$NON-NLS-1$
public static final String TEMP_DIR = "temp/";
public static final String ROUTING_PROFILES_DIR = "routing/";
}

View file

@ -44,6 +44,8 @@ public class GeneralRouter implements VehicleRouter {
private boolean shortestRoute;
private boolean heightObstacles;
private boolean allowPrivate;
private String filename = null;
private String profileName = "";
private Map<RouteRegion, Map<Integer, Integer>> regionConvert = new LinkedHashMap<RouteRegion, Map<Integer,Integer>>();
@ -120,6 +122,22 @@ public class GeneralRouter implements VehicleRouter {
parameters = new LinkedHashMap<String, GeneralRouter.RoutingParameter>();
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public String getProfileName() {
return profileName;
}
public void setProfileName(String profileName) {
this.profileName = profileName;
}
public GeneralRouter(GeneralRouter parent, Map<String, String> params) {
this.profile = parent.profile;
this.attributes = new LinkedHashMap<String, String>();

View file

@ -47,13 +47,12 @@ public class RoutingConfiguration {
// 1.5 Recalculate distance help
public float recalculateDistance = 20000f;
public static class Builder {
// Design time storage
private String defaultRouter = "";
private Map<String, GeneralRouter> routers = new LinkedHashMap<String, GeneralRouter>();
private Map<String, String> attributes = new LinkedHashMap<String, String>();
private HashMap<Long, Location> impassableRoadLocations = new HashMap<Long, Location>();
private Map<String, GeneralRouter> routers = new LinkedHashMap<>();
private Map<String, String> attributes = new LinkedHashMap<>();
private HashMap<Long, Location> impassableRoadLocations = new HashMap<>();
// Example
// {
@ -126,11 +125,15 @@ public class RoutingConfiguration {
return defaultRouter;
}
public GeneralRouter getRouter(String applicationMode) {
return routers.get(applicationMode);
public GeneralRouter getRouter(String routingProfileName) {
return routers.get(routingProfileName);
}
public Map<String, GeneralRouter> getAllRouters() {
return routers;
}
public void removeImpassableRoad(RouteDataObject obj) {
impassableRoadLocations.remove(obj.id);
}
@ -166,8 +169,11 @@ public class RoutingConfiguration {
}
public static RoutingConfiguration.Builder parseFromInputStream(InputStream is) throws IOException, XmlPullParserException {
return parseFromInputStream(is, null, new RoutingConfiguration.Builder());
}
public static RoutingConfiguration.Builder parseFromInputStream(InputStream is, String filename, RoutingConfiguration.Builder config) throws IOException, XmlPullParserException {
XmlPullParser parser = PlatformUtil.newXMLPullParser();
final RoutingConfiguration.Builder config = new RoutingConfiguration.Builder();
GeneralRouter currentRouter = null;
RouteDataObjectAttribute currentAttribute = null;
String preType = null;
@ -180,7 +186,7 @@ public class RoutingConfiguration {
if ("osmand_routing_config".equals(name)) {
config.defaultRouter = parser.getAttributeValue("", "defaultProfile");
} else if ("routingProfile".equals(name)) {
currentRouter = parseRoutingProfile(parser, config);
currentRouter = parseRoutingProfile(parser, config, filename);
} else if ("attribute".equals(name)) {
parseAttribute(parser, config, currentRouter);
} else if ("parameter".equals(name)) {
@ -295,10 +301,8 @@ public class RoutingConfiguration {
}
}
private static GeneralRouter parseRoutingProfile(XmlPullParser parser, final RoutingConfiguration.Builder config) {
String currentSelectedRouter = parser.getAttributeValue("", "name");
private static GeneralRouter parseRoutingProfile(XmlPullParser parser, final RoutingConfiguration.Builder config, String filename) {
String currentSelectedRouterName = parser.getAttributeValue("", "name");
Map<String, String> attrs = new LinkedHashMap<String, String>();
for(int i=0; i< parser.getAttributeCount(); i++) {
attrs.put(parser.getAttributeName(i), parser.getAttributeValue(i));
@ -306,7 +310,13 @@ public class RoutingConfiguration {
GeneralRouterProfile c = Algorithms.parseEnumValue(GeneralRouterProfile.values(),
parser.getAttributeValue("", "baseProfile"), GeneralRouterProfile.CAR);
GeneralRouter currentRouter = new GeneralRouter(c, attrs);
config.routers.put(currentSelectedRouter, currentRouter);
currentRouter.setProfileName(currentSelectedRouterName);
if (filename != null) {
currentRouter.setFilename(filename);
currentSelectedRouterName = filename + "/" + currentSelectedRouterName;
}
config.routers.put(currentSelectedRouterName, currentRouter);
return currentRouter;
}

View file

@ -131,6 +131,35 @@
</LinearLayout>
<include layout="@layout/list_item_divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/card_bg_color"
android:orientation="vertical">
<net.osmand.telegram.ui.views.TextViewEx
android:layout_width="match_parent"
android:layout_height="@dimen/list_header_height"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingLeft="@dimen/content_padding_standard"
android:paddingRight="@dimen/content_padding_standard"
android:text="@string/gpx_settings"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/list_item_title_text_size"
app:typeface="@string/font_roboto_medium" />
<LinearLayout
android:id="@+id/gpx_settings_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
<include layout="@layout/list_item_divider"/>
<LinearLayout

View file

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="shared_string_select">Select</string>
<string name="min_logging_distance">Minimum logging distance</string>
<string name="min_logging_distance_descr">Filter: minimum distance to log a new point</string>
<string name="min_logging_accuracy">Minimum logging accuracy</string>
<string name="min_logging_accuracy_descr">Filter: no logging unless the accuracy is reached</string>
<string name="min_logging_speed">Minimum logging speed</string>
<string name="min_logging_speed_descr">Filter: no logging below selected speed</string>
<string name="gpx_settings">GPX settings</string>
<string name="proxy_key">Key</string>
<string name="proxy_password">Password</string>
<string name="proxy_username">Username</string>

View file

@ -15,6 +15,7 @@ public class AMapPoint implements Parcelable {
public static final String POINT_SPEED_PARAM = "point_speed_param";
public static final String POINT_TYPE_ICON_NAME_PARAM = "point_type_icon_name_param";
public static final String POINT_STALE_LOC_PARAM = "point_stale_loc_param";
public static final String POINT_BEARING_PARAM = "point_bearing_param";
private String id;
private String shortName;

View file

@ -43,6 +43,9 @@ private val LOC_HISTORY_VALUES_SEC = listOf(
12 * 60 * 60L,
24 * 60 * 60L
)
private val MIN_LOCATION_DISTANCE = listOf(0f, 2.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f)
private val MIN_LOCATION_ACCURACY = listOf(0f, 1.0f, 2.0f, 5.0f, 10.0f, 15.0f, 20.0f, 50.0f, 100.0f)
private val MIN_LOCATION_SPEED = listOf(0f, 0.000001f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f)
const val SHARE_TYPE_MAP = "Map"
const val SHARE_TYPE_TEXT = "Text"
@ -53,6 +56,9 @@ private const val SEND_MY_LOC_DEFAULT_INDEX = 6
private const val STALE_LOC_DEFAULT_INDEX = 0
private const val LOC_HISTORY_DEFAULT_INDEX = 7
private const val SHARE_TYPE_DEFAULT_INDEX = 2
private const val MIN_LOCATION_DISTANCE_INDEX = 0
private const val MIN_LOCATION_ACCURACY_INDEX = 0
private const val MIN_LOCATION_SPEED_INDEX = 0
private const val SETTINGS_NAME = "osmand_telegram_settings"
@ -69,6 +75,10 @@ private const val STALE_LOC_TIME_KEY = "stale_loc_time"
private const val LOC_HISTORY_TIME_KEY = "loc_history_time"
private const val SHARE_TYPE_KEY = "share_type"
private const val MIN_LOCATION_DISTANCE_KEY = "min_location_distance"
private const val MIN_LOCATION_ACCURACY_KEY = "min_location_accuracy"
private const val MIN_LOCATION_SPEED_KEY = "min_location_speed"
private const val APP_TO_CONNECT_PACKAGE_KEY = "app_to_connect_package"
private const val DEFAULT_VISIBLE_TIME_SECONDS = 60 * 60L // 1 hour
@ -117,12 +127,17 @@ class TelegramSettings(private val app: TelegramApplication) {
var locHistoryTime = LOC_HISTORY_VALUES_SEC[LOC_HISTORY_DEFAULT_INDEX]
var shareTypeValue = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX]
var minLocationDistance = MIN_LOCATION_DISTANCE[MIN_LOCATION_DISTANCE_INDEX]
var minLocationAccuracy = MIN_LOCATION_ACCURACY[MIN_LOCATION_ACCURACY_INDEX]
var minLocationSpeed = MIN_LOCATION_SPEED[MIN_LOCATION_SPEED_INDEX]
var appToConnectPackage = ""
private set
var liveNowSortType = LiveNowSortType.SORT_BY_DISTANCE
val gpsAndLocPrefs = listOf(SendMyLocPref(), StaleLocPref(), LocHistoryPref(), ShareTypePref())
val gpxLoggingPrefs = listOf(MinLocationDistance(), MinLocationAccuracy(), MinLocationSpeed())
var batteryOptimisationAsked = false
@ -460,15 +475,20 @@ class TelegramSettings(private val app: TelegramApplication) {
if (shareInfo.lastTextSuccessfulSendTime == -1L && shareInfo.lastMapSuccessfulSendTime == -1L
&& ((statusChangeTime / 1000 - shareInfo.start) < SHARING_INITIALIZATION_TIME)) {
initializing = true
} else if (shareInfo.hasSharingError
|| (shareInfo.lastSendTextMessageTime - shareInfo.lastTextSuccessfulSendTime > WAITING_TDLIB_TIME)
|| (shareInfo.lastSendMapMessageTime - shareInfo.lastMapSuccessfulSendTime > WAITING_TDLIB_TIME)
} else {
val textSharingError = shareInfo.lastSendTextMessageTime - shareInfo.lastTextSuccessfulSendTime > WAITING_TDLIB_TIME
val mapSharingError = shareInfo.lastSendMapMessageTime - shareInfo.lastMapSuccessfulSendTime > WAITING_TDLIB_TIME
if (shareInfo.hasSharingError
|| (shareTypeValue == SHARE_TYPE_MAP_AND_TEXT && (textSharingError || mapSharingError))
|| textSharingError && (shareTypeValue == SHARE_TYPE_TEXT)
|| mapSharingError && (shareTypeValue == SHARE_TYPE_MAP)
) {
sendChatsErrors = true
locationTime = Math.max(shareInfo.lastTextSuccessfulSendTime, shareInfo.lastMapSuccessfulSendTime)
chatsIds.add(shareInfo.chatId)
}
}
}
if (sendChatsErrors) {
title = app.getString(R.string.not_possible_to_send_to_telegram_chats)
@ -477,7 +497,7 @@ class TelegramSettings(private val app: TelegramApplication) {
} else if (!initializing) {
when {
!gpsEnabled -> {
locationTime = app.shareLocationHelper.lastLocationUpdateTime
locationTime = app.shareLocationHelper.lastLocationUpdateTime / 1000
if (locationTime <= 0) {
locationTime = getLastSuccessfulSendTime()
}
@ -557,6 +577,10 @@ class TelegramSettings(private val app: TelegramApplication) {
edit.putLong(STALE_LOC_TIME_KEY, staleLocTime)
edit.putLong(LOC_HISTORY_TIME_KEY, locHistoryTime)
edit.putFloat(MIN_LOCATION_DISTANCE_KEY, minLocationDistance)
edit.putFloat(MIN_LOCATION_ACCURACY_KEY, minLocationAccuracy)
edit.putFloat(MIN_LOCATION_SPEED_KEY, minLocationSpeed)
edit.putString(SHARE_TYPE_KEY, shareTypeValue)
edit.putString(APP_TO_CONNECT_PACKAGE_KEY, appToConnectPackage)
@ -623,6 +647,13 @@ class TelegramSettings(private val app: TelegramApplication) {
val shareTypeDef = SHARE_TYPE_VALUES[SHARE_TYPE_DEFAULT_INDEX]
shareTypeValue = prefs.getString(SHARE_TYPE_KEY, shareTypeDef)
val minLocationDistanceDef = MIN_LOCATION_DISTANCE[MIN_LOCATION_DISTANCE_INDEX]
minLocationDistance = prefs.getFloat(MIN_LOCATION_DISTANCE_KEY, minLocationDistanceDef)
val minLocationPrecisionDef = MIN_LOCATION_ACCURACY[MIN_LOCATION_ACCURACY_INDEX]
minLocationAccuracy = prefs.getFloat(MIN_LOCATION_ACCURACY_KEY, minLocationPrecisionDef)
val minLocationSpeedDef = MIN_LOCATION_SPEED[MIN_LOCATION_SPEED_INDEX]
minLocationSpeed = prefs.getFloat(MIN_LOCATION_SPEED_KEY, minLocationSpeedDef)
val currentUserId = app.telegramHelper.getCurrentUserId()
currentSharingMode = prefs.getString(SHARING_MODE_KEY, if (currentUserId != -1) currentUserId.toString() else "")
@ -787,7 +818,7 @@ class TelegramSettings(private val app: TelegramApplication) {
}
}
inner class SendMyLocPref : DurationPref(
inner class SendMyLocPref : NumericPref(
R.drawable.ic_action_share_location,
R.string.send_my_location,
R.string.send_my_location_desc,
@ -798,12 +829,15 @@ class TelegramSettings(private val app: TelegramApplication) {
OsmandFormatter.getFormattedDuration(app, sendMyLocInterval)
override fun setCurrentValue(index: Int) {
sendMyLocInterval = values[index]
sendMyLocInterval = values[index].toLong()
app.updateSendLocationInterval()
}
override fun getMenuItems() =
values.map { OsmandFormatter.getFormattedDuration(app, it.toLong()) }
}
inner class StaleLocPref : DurationPref(
inner class StaleLocPref : NumericPref(
R.drawable.ic_action_time_span,
R.string.stale_location,
R.string.stale_location_desc,
@ -814,11 +848,14 @@ class TelegramSettings(private val app: TelegramApplication) {
OsmandFormatter.getFormattedDuration(app, staleLocTime)
override fun setCurrentValue(index: Int) {
staleLocTime = values[index]
}
staleLocTime = values[index].toLong()
}
inner class LocHistoryPref : DurationPref(
override fun getMenuItems() =
values.map { OsmandFormatter.getFormattedDuration(app, it.toLong()) }
}
inner class LocHistoryPref : NumericPref(
R.drawable.ic_action_location_history,
R.string.location_history,
R.string.location_history_desc,
@ -830,12 +867,15 @@ class TelegramSettings(private val app: TelegramApplication) {
override fun setCurrentValue(index: Int) {
val value = values[index]
locHistoryTime = value
app.telegramHelper.messageActiveTimeSec = value
}
locHistoryTime = value.toLong()
app.telegramHelper.messageActiveTimeSec = value.toLong()
}
inner class ShareTypePref : DurationPref(
override fun getMenuItems() =
values.map { OsmandFormatter.getFormattedDuration(app, it.toLong()) }
}
inner class ShareTypePref : NumericPref(
R.drawable.ic_action_location_history,
R.string.send_location_as,
R.string.send_location_as_descr,
@ -871,18 +911,87 @@ class TelegramSettings(private val app: TelegramApplication) {
}
}
abstract inner class DurationPref(
inner class MinLocationDistance : NumericPref(
R.drawable.ic_action_location_history,
R.string.min_logging_distance,
R.string.min_logging_distance_descr,
MIN_LOCATION_DISTANCE
) {
override fun getCurrentValue() = getFormattedValue(minLocationDistance)
override fun setCurrentValue(index: Int) {
val value = values[index]
minLocationDistance = value.toFloat()
}
override fun getMenuItems() = values.map { getFormattedValue(it.toFloat()) }
private fun getFormattedValue(value: Float): String {
return if (value == 0f) app.getString(R.string.shared_string_select)
else OsmandFormatter.getFormattedDistance(value, app)
}
}
inner class MinLocationAccuracy : NumericPref(
R.drawable.ic_action_location_history,
R.string.min_logging_accuracy,
R.string.min_logging_accuracy_descr,
MIN_LOCATION_ACCURACY
) {
override fun getCurrentValue() = getFormattedValue(minLocationAccuracy)
override fun setCurrentValue(index: Int) {
val value = values[index]
minLocationAccuracy = value.toFloat()
}
override fun getMenuItems() = values.map { getFormattedValue(it.toFloat()) }
private fun getFormattedValue(value: Float): String {
return if (value == 0f) app.getString(R.string.shared_string_select)
else OsmandFormatter.getFormattedDistance(value, app)
}
}
inner class MinLocationSpeed : NumericPref(
R.drawable.ic_action_location_history,
R.string.min_logging_speed,
R.string.min_logging_speed_descr,
MIN_LOCATION_SPEED
) {
override fun getCurrentValue() = getFormattedValue(minLocationSpeed)
override fun setCurrentValue(index: Int) {
val value = values[index]
minLocationSpeed = value.toFloat()
}
override fun getMenuItems() = values.map { getFormattedValue(it.toFloat()) }
private fun getFormattedValue(value: Float): String {
return when (value) {
MIN_LOCATION_SPEED[0] -> app.getString(R.string.shared_string_select)
MIN_LOCATION_SPEED[1] -> "> 0"
else -> OsmandFormatter.getFormattedSpeed(value, app)
}
}
}
abstract inner class NumericPref(
@DrawableRes val iconId: Int,
@StringRes val titleId: Int,
@StringRes val descriptionId: Int,
val values: List<Long>
val values: List<Number>
) {
abstract fun getCurrentValue(): String
abstract fun setCurrentValue(index: Int)
open fun getMenuItems() = values.map { OsmandFormatter.getFormattedDuration(app, it) }
abstract fun getMenuItems(): List<String>
}
enum class AppConnect(

View file

@ -7,6 +7,7 @@ import net.osmand.telegram.helpers.LocationMessages.BufferMessage
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import net.osmand.telegram.utils.AndroidNetworkUtils
import net.osmand.telegram.utils.BASE_URL
import net.osmand.util.MapUtils
import org.drinkless.td.libcore.telegram.TdApi
import org.json.JSONException
import org.json.JSONObject
@ -15,6 +16,8 @@ private const val USER_SET_LIVE_PERIOD_DELAY_MS = 5000 // 5 sec
private const val UPDATE_LOCATION_ACCURACY = 150 // 150 meters
private const val SENT_LOCATIONS_INTERVAL_TIME_MS = 4000 // 4 sec
class ShareLocationHelper(private val app: TelegramApplication) {
private val log = PlatformUtil.getLog(ShareLocationHelper::class.java)
@ -30,6 +33,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
var lastLocationUpdateTime: Long = 0
var lastLocationSentTime: Long = 0
var lastLocation: Location? = null
set(value) {
if (lastTimeInMillis == 0L) {
@ -48,14 +53,33 @@ class ShareLocationHelper(private val app: TelegramApplication) {
private var lastTimeInMillis: Long = 0L
fun updateLocation(location: Location?) {
lastLocation = location
val lastPoint = lastLocation
var record = true
if (location != null) {
val minDistance = app.settings.minLocationDistance
if (minDistance > 0 && lastPoint != null) {
val calculatedDistance = MapUtils.getDistance(lastPoint.latitude, lastPoint.longitude, location.latitude, location.longitude)
if (calculatedDistance < minDistance) {
record = false
}
}
val accuracy = app.settings.minLocationAccuracy
if (accuracy > 0 && (!location.hasAccuracy() || location.accuracy > accuracy)) {
record = false
}
val minSpeed = app.settings.minLocationSpeed
if (minSpeed > 0 && (!location.hasSpeed() || location.speed < minSpeed)) {
record = false
}
if (location != null && location.accuracy < UPDATE_LOCATION_ACCURACY) {
if (record) {
lastLocationUpdateTime = System.currentTimeMillis()
lastLocation = location
if (app.settings.getChatsShareInfo().isNotEmpty()) {
shareLocationMessages(location, app.telegramHelper.getCurrentUserId())
}
}
}
app.settings.updateSharingStatusHistory()
refreshNotification()
}
@ -108,12 +132,16 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
fun checkAndSendBufferMessagesToChat(chatId: Long) {
val shareInfo = app.settings.getChatsShareInfo()[chatId]
if (shareInfo != null) {
val shareChatsInfo = app.settings.getChatsShareInfo()
val shareInfo = shareChatsInfo[chatId]
val chatsCounts = shareChatsInfo.size
val currentTime = System.currentTimeMillis()
if (shareInfo != null && currentTime - lastLocationSentTime > chatsCounts * SENT_LOCATIONS_INTERVAL_TIME_MS) {
app.locationMessages.getBufferedTextMessagesForChat(chatId).take(MAX_MESSAGES_IN_TDLIB_PER_CHAT).forEach {
if (shareInfo.pendingTdLibText < MAX_MESSAGES_IN_TDLIB_PER_CHAT) {
if (it.deviceName.isEmpty()) {
if (!shareInfo.pendingTextMessage && shareInfo.currentTextMessageId != -1L) {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.editTextLocation(shareInfo, it)
app.locationMessages.removeBufferedMessage(it)
}
@ -126,6 +154,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
if (shareInfo.pendingTdLibMap < MAX_MESSAGES_IN_TDLIB_PER_CHAT) {
if (it.deviceName.isEmpty()) {
if (!shareInfo.pendingMapMessage && shareInfo.currentMapMessageId != -1L) {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.editMapLocation(shareInfo, it)
app.locationMessages.removeBufferedMessage(it)
}
@ -188,6 +217,14 @@ class ShareLocationHelper(private val app: TelegramApplication) {
val isBot = app.settings.currentSharingMode != userId.toString()
val deviceName = if (isBot) app.settings.currentSharingMode else ""
var bufferedMessagesFull = false
val chatsCounts = chatsShareInfo.size
val currentTime = System.currentTimeMillis()
app.locationMessages.addMyLocationMessage(location)
if (currentTime - lastLocationSentTime <= chatsCounts * SENT_LOCATIONS_INTERVAL_TIME_MS) {
return
}
chatsShareInfo.values.forEach { shareInfo ->
if (shareInfo.pendingTdLibText >= MAX_MESSAGES_IN_TDLIB_PER_CHAT || shareInfo.pendingTdLibMap >= MAX_MESSAGES_IN_TDLIB_PER_CHAT) {
@ -209,8 +246,8 @@ class ShareLocationHelper(private val app: TelegramApplication) {
prepareTextMessage(shareInfo, messageText, isBot)
}
}
checkAndSendBufferMessagesToChat(shareInfo.chatId)
}
app.locationMessages.addMyLocationMessage(location)
if (bufferedMessagesFull) {
checkNetworkType()
}
@ -225,6 +262,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
if (isBot) {
sendLocationToBot(message, shareInfo, SHARE_TYPE_TEXT)
} else {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.sendNewTextLocation(shareInfo, message)
}
}
@ -237,6 +275,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
} else {
if (shareInfo.pendingTdLibText < MAX_MESSAGES_IN_TDLIB_PER_CHAT) {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.editTextLocation(shareInfo, message)
} else {
app.locationMessages.addBufferedMessage(message)
@ -258,6 +297,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
app.locationMessages.addBufferedMessage(message)
}
} else {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.sendNewMapLocation(shareInfo, message)
}
}
@ -270,6 +310,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
}
} else {
if (shareInfo.pendingTdLibMap < MAX_MESSAGES_IN_TDLIB_PER_CHAT) {
lastLocationSentTime = System.currentTimeMillis()
app.telegramHelper.editMapLocation(shareInfo, message)
} else {
app.locationMessages.addBufferedMessage(message)
@ -298,6 +339,7 @@ class ShareLocationHelper(private val app: TelegramApplication) {
} else if (shareType == SHARE_TYPE_MAP) {
shareInfo.lastSendMapMessageTime = (System.currentTimeMillis() / 1000).toInt()
}
lastLocationSentTime = System.currentTimeMillis()
AndroidNetworkUtils.sendRequestAsync(app, url, null, "Send Location", false, false,
object : AndroidNetworkUtils.OnRequestResultListener {
override fun onResult(result: String?) {

View file

@ -90,6 +90,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
if (item.latLon == null) {
return
}
setupMapLayer()
osmandAidlHelper.execOsmandApi {
osmandAidlHelper.showMapPoint(
MAP_LAYER_ID,
@ -100,7 +101,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
Color.WHITE,
ALatLon(item.latLon!!.latitude, item.latLon!!.longitude),
generatePointDetails(item.bearing?.toFloat(), item.altitude?.toFloat(), item.precision?.toFloat()),
generatePointParams(if (stale) item.grayscalePhotoPath else item.photoPath, stale, item.speed?.toFloat())
generatePointParams(if (stale) item.grayscalePhotoPath else item.photoPath, stale, item.speed?.toFloat(), item.bearing?.toFloat())
)
}
}
@ -147,8 +148,13 @@ class ShowLocationHelper(private val app: TelegramApplication) {
}
}
setupMapLayer()
val params = generatePointParams(photoPath, stale, if (content is MessageUserLocation) content.speed.toFloat() else null)
var speed = 0f
var bearing = 0f
if (content is MessageUserLocation) {
speed = content.speed.toFloat()
bearing = content.bearing.toFloat()
}
val params = generatePointParams(photoPath, stale, speed, bearing)
val typeName = if (isGroup) chatTitle else OsmandFormatter.getListItemLiveTimeDescr(app, date, app.getString(R.string.last_response) + ": ")
if (update) {
osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, pointId, name, name, typeName, Color.WHITE, aLatLon, details, params)
@ -158,7 +164,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
points[pointId] = message
} else if (content is MessageOsmAndBotLocation && content.isValid()) {
setupMapLayer()
val params = generatePointParams(null, stale, content.speed.toFloat())
val params = generatePointParams(null, stale, content.speed.toFloat(), content.bearing.toFloat())
if (update) {
osmandAidlHelper.updateMapPoint(MAP_LAYER_ID, pointId, name, name, chatTitle, Color.WHITE, aLatLon, details, params)
} else {
@ -340,7 +346,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
private fun generatePointDetails(bearing: Float?, altitude: Float?, precision: Float?): List<String> {
val details = mutableListOf<String>()
if (bearing != null && bearing != 0.0f) {
details.add(String.format(Locale.US, "${OsmandLocationUtils.BEARING_PREFIX}%.1f \n", bearing))
details.add(String.format(Locale.US, "${OsmandLocationUtils.BEARING_PREFIX}%.1f${OsmandLocationUtils.BEARING_SUFFIX} \n", bearing))
}
if (altitude != null && altitude != 0.0f) {
details.add(String.format(Locale.US, "${OsmandLocationUtils.ALTITUDE_PREFIX}%.1f m\n", altitude))
@ -352,7 +358,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
return details
}
private fun generatePointParams(photoPath: String?, stale: Boolean, speed: Float?): Map<String, String> {
private fun generatePointParams(photoPath: String?, stale: Boolean, speed: Float?, bearing: Float?): Map<String, String> {
val photoUri = generatePhotoUri(photoPath, stale)
app.grantUriPermission(
app.settings.appToConnectPackage,
@ -366,6 +372,9 @@ class ShowLocationHelper(private val app: TelegramApplication) {
if (speed != 0.0f) {
params[AMapPoint.POINT_SPEED_PARAM] = speed.toString()
}
if (bearing != 0.0f) {
params[AMapPoint.POINT_BEARING_PARAM] = bearing.toString()
}
return params
}

View file

@ -17,7 +17,7 @@ import android.view.ViewGroup
import android.widget.*
import net.osmand.telegram.R
import net.osmand.telegram.TelegramSettings
import net.osmand.telegram.TelegramSettings.DurationPref
import net.osmand.telegram.TelegramSettings.NumericPref
import net.osmand.telegram.helpers.TelegramHelper.Companion.OSMAND_BOT_USERNAME
import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.telegram.utils.AndroidUtils
@ -49,18 +49,8 @@ class SettingsDialogFragment : BaseDialogFragment() {
window.statusBarColor = ContextCompat.getColor(app, R.color.card_bg_light)
}
var container = mainView.findViewById<ViewGroup>(R.id.gps_and_loc_container)
for (pref in settings.gpsAndLocPrefs) {
inflater.inflate(R.layout.item_with_desc_and_right_value, container, false).apply {
findViewById<ImageView>(R.id.icon).setImageDrawable(uiUtils.getThemedIcon(pref.iconId))
findViewById<TextView>(R.id.title).setText(pref.titleId)
findViewById<TextView>(R.id.description).setText(pref.descriptionId)
val valueView = findViewById<TextView>(R.id.value)
valueView.text = pref.getCurrentValue()
setOnClickListener {
showPopupMenu(pref, valueView)
}
container.addView(this)
}
settings.gpsAndLocPrefs.forEach {
createNumericPref(inflater, container, it)
}
if (Build.VERSION.SDK_INT >= 26) {
@ -148,6 +138,11 @@ class SettingsDialogFragment : BaseDialogFragment() {
}
}
container = mainView.findViewById<ViewGroup>(R.id.gpx_settings_container)
settings.gpxLoggingPrefs.forEach {
createNumericPref(inflater, container, it)
}
container = mainView.findViewById(R.id.osmand_connect_container)
for (appConn in TelegramSettings.AppConnect.values()) {
val pack = appConn.appPackage
@ -249,6 +244,20 @@ class SettingsDialogFragment : BaseDialogFragment() {
}
}
private fun createNumericPref(inflater: LayoutInflater, container: ViewGroup, pref: NumericPref) {
inflater.inflate(R.layout.item_with_desc_and_right_value, container, false).apply {
findViewById<ImageView>(R.id.icon).setImageDrawable(uiUtils.getThemedIcon(pref.iconId))
findViewById<TextView>(R.id.title).setText(pref.titleId)
findViewById<TextView>(R.id.description).setText(pref.descriptionId)
val valueView = findViewById<TextView>(R.id.value)
valueView.text = pref.getCurrentValue()
setOnClickListener {
showPopupMenu(pref, valueView)
}
container.addView(this)
}
}
private fun addItemToContainer(inflater: LayoutInflater, container: ViewGroup, tag: String, title: String) {
inflater.inflate(R.layout.item_with_rb_and_btn, container, false).apply {
val checked = tag == settings.currentSharingMode
@ -270,7 +279,7 @@ class SettingsDialogFragment : BaseDialogFragment() {
}
}
private fun showPopupMenu(pref: DurationPref, valueView: TextView) {
private fun showPopupMenu(pref: NumericPref, valueView: TextView) {
val menuList = pref.getMenuItems()
val ctx = valueView.context
ListPopupWindow(ctx).apply {

View file

@ -32,6 +32,7 @@ object OsmandLocationUtils {
const val BEARING_PREFIX = "Bearing: "
const val SPEED_PREFIX = "Speed: "
const val HDOP_PREFIX = "Horizontal precision: "
const val BEARING_SUFFIX = "°"
const val NOW = "now"
const val FEW_SECONDS_AGO = "few seconds ago"
@ -241,19 +242,28 @@ object OsmandLocationUtils {
}
}
s.startsWith(SPEED_PREFIX) -> {
val altStr = s.removePrefix(SPEED_PREFIX)
val speedStr = s.removePrefix(SPEED_PREFIX)
try {
val alt = altStr.split(" ").first()
res.speed = alt.toDouble()
val speed = speedStr.split(" ").first()
res.speed = speed.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(BEARING_PREFIX) -> {
val bearingStr = s.removePrefix(BEARING_PREFIX)
try {
val bearing = bearingStr.removeSuffix(BEARING_SUFFIX)
res.bearing = bearing.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
}
s.startsWith(HDOP_PREFIX) -> {
val altStr = s.removePrefix(HDOP_PREFIX)
val hdopStr = s.removePrefix(HDOP_PREFIX)
try {
val alt = altStr.split(" ").first()
res.hdop = alt.toDouble()
val hdop = hdopStr.split(" ").first()
res.hdop = hdop.toDouble()
} catch (e: Exception) {
e.printStackTrace()
}
@ -336,6 +346,10 @@ object OsmandLocationUtils {
entities.add(TdApi.TextEntity(builder.lastIndex, SPEED_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$SPEED_PREFIX%.1f m/s\n", location.speed))
}
if (location.bearing > 0) {
entities.add(TdApi.TextEntity(builder.lastIndex, BEARING_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$BEARING_PREFIX%.1f$BEARING_SUFFIX\n", location.bearing))
}
if (location.hdop != 0.0 && location.speed == 0.0) {
entities.add(TdApi.TextEntity(builder.lastIndex, HDOP_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$HDOP_PREFIX%d m\n", location.hdop.toInt()))
@ -375,6 +389,10 @@ object OsmandLocationUtils {
entities.add(TdApi.TextEntity(builder.lastIndex, SPEED_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$SPEED_PREFIX%.1f m/s\n", location.speed))
}
if (location.bearing > 0) {
entities.add(TdApi.TextEntity(builder.lastIndex, BEARING_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$BEARING_PREFIX%.1f$BEARING_SUFFIX\n", location.bearing))
}
if (location.hdop != 0.0 && location.speed == 0.0) {
entities.add(TdApi.TextEntity(builder.lastIndex, HDOP_PREFIX.length, TdApi.TextEntityTypeBold()))
builder.append(String.format(Locale.US, "$HDOP_PREFIX%d m\n", location.hdop.toInt()))

View file

@ -891,5 +891,15 @@
</activity>
<receiver android:name="net.osmand.plus.liveupdates.LiveUpdatesAlarmReceiver"/>
<activity
android:configChanges="keyboardHidden|orientation"
android:label="Application profiles"
android:name=".profiles.SettingsProfileActivity"/>
<activity
android:name=".profiles.EditProfileActivity"
android:label="Application profiles"/>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"><shape android:shape="rectangle">
<solid android:color="@color/map_widget_blue_pressed" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
<item android:state_enabled="false"><shape android:shape="rectangle">
<solid android:color="@color/searchbar_tab_inactive_light" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
<item><shape android:shape="rectangle">
<solid android:color="@color/active_buttons_and_links_light" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
</selector>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"><shape android:shape="rectangle">
<solid android:color="@color/profile_button_gray_pressed" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
<item android:state_enabled="false"><shape android:shape="rectangle">
<solid android:color="@color/searchbar_tab_inactive_light" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
<item><shape android:shape="rectangle">
<solid android:color="@color/profile_button_gray" />
<corners android:radius="@dimen/map_button_rect_rad" />
</shape></item>
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_shadow_fab_normal" android:state_pressed="true"/>
<item android:drawable="@drawable/bg_shadow_fab_normal"/>
</selector>

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/bottom_sheet_large_list_item_height"
android:background="?attr/selectableItemBackground"
android:orientation="vertical"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="63dp"
android:paddingStart="@dimen/list_content_padding"
android:paddingEnd="@dimen/list_content_padding"
android:paddingLeft="@dimen/list_content_padding"
android:paddingRight="@dimen/list_content_padding"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin"
tools:tint="?attr/primary_icon_color"
tools:src="@drawable/ic_action_coordinates_latitude"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ListItemTitle"
android:textColor="?attr/main_font_color_basic"
tools:text="Item Title"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
tools:text="Item additional desription"/>
</LinearLayout>
<RadioButton
android:id="@+id/compound_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:saveEnabled="false"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="64dp"
android:layout_marginLeft="64dp"
android:background="@color/divider_light"/>
</LinearLayout>

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:background="?attr/bg_color"
android:orientation="vertical">
<TextView
android:id="@+id/dialog_title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
tools:text="Select navigation type"
android:textColor="?attr/main_font_color_basic"
android:textSize="@dimen/default_list_text_size"/>
<TextView
android:id="@+id/dialog_description_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/list_content_padding"
android:paddingStart="@dimen/list_content_padding"
android:paddingEnd="@dimen/list_content_padding"
android:paddingLeft="@dimen/list_content_padding"
android:paddingRight="@dimen/list_content_padding"
android:textSize="@dimen/default_list_text_size"
android:visibility="gone"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/menu_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
<View
android:id="@+id/bottom_row_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/dashboard_divider"/>
<LinearLayout
android:id="@+id/buttons_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal"
>
<Button
android:id="@+id/cancel_selection"
android:layout_width="0dp"
android:layout_height="@dimen/bottom_sheet_cancel_button_height"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:background="@drawable/btn_right_round"
android:text="@string/shared_string_cancel"
android:textAllCaps="false"
android:textColor="@color/map_widget_blue"/>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,550 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="?attr/ctx_menu_info_view_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView
android:id="@+id/scroll_view_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_marginBottom="@dimen/setting_profile_item_height">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<LinearLayout
android:id="@+id/type_selection_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?attr/bg_color"
android:foreground="?attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding_small"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding_small"
android:orientation="horizontal">
<ImageView
android:id="@+id/mode_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin"
android:layout_gravity="center_vertical"
android:tint="?attr/primary_icon_color"
tools:src="@drawable/ic_action_coordinates_latitude"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="Base Profile"/>
<TextView
android:id="@+id/mode_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textSize="18sp"
android:textColor="?attr/main_font_color_basic"
tools:text="Item additional description"/>
</LinearLayout>
<ImageView
android:id="@+id/type_down_arrow2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center_vertical"
android:tint="@color/settings_divider"
android:src="@drawable/ic_action_arrow_drop_down"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/ctx_menu_info_view_bg"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/bg_shadow_list_bottom"/>
<View
android:layout_width="match_parent"
android:layout_height="10dp"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/bg_shadow_list_top"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<net.osmand.plus.widgets.OsmandTextFieldBoxes
android:id="@+id/profile_name_otfb"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
app:labelText="Profile Name">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/profile_name_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
tools:text="@string/lorem_ipsum"/>
</net.osmand.plus.widgets.OsmandTextFieldBoxes>
<LinearLayout
android:id="@+id/profile_icon_layout"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:orientation="vertical"
>
<TextView
android:id="@+id/select_icon_btn_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="12sp"
android:paddingBottom="2dp"
android:text="Icon"/>
<ImageView
android:id="@+id/select_icon_btn_img"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
tools:src="@drawable/ic_action_car_dark"/>
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/click_block_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"/>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding">
<ImageView
android:id="@+id/type_down_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:paddingRight="@dimen/content_padding"
android:tint="@color/settings_divider"
android:src="@drawable/ic_action_arrow_drop_down"/>
<net.osmand.plus.widgets.OsmandTextFieldBoxes
android:id="@+id/navigation_type_otfb"
android:layout_width="match_parent"
android:layout_height="60dp"
app:labelText="Navigation Type">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/navigation_type_et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/ic_action_arrow_drop_down"
android:drawableRight="@drawable/ic_action_arrow_drop_down"
android:focusable="false"
android:maxLines="1"
tools:text="Car"/>
</net.osmand.plus.widgets.OsmandTextFieldBoxes>
<FrameLayout
android:id="@+id/select_nav_type_btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"/>
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:text="You can add own modified version of routing.xml to ..osmand/files/routing/"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/ctx_menu_info_view_bg"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/bg_shadow_list_bottom"/>
<View
android:layout_width="match_parent"
android:layout_height="10dp"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/bg_shadow_list_top"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:orientation="vertical">
<TextView
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:text="Setup Profile"
android:textColor="?attr/main_font_color_basic"
android:textSize="@dimen/default_list_text_size"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="@dimen/list_content_padding"
android:text="Profile keeps its own settings"
android:textColor="@color/description_font_and_bottom_sheet_icons"/>
<LinearLayout
android:id="@+id/map_config_btn"
android:layout_width="match_parent"
android:layout_height="65dp"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/map_config_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:src="@drawable/ic_action_layers_dark"
android:tint="?attr/primary_icon_color"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="24dp"
android:layout_marginRight="@dimen/list_content_padding"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Configure map"
android:textColor="?attr/main_font_color_basic"
android:textSize="@dimen/default_list_text_size"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Select default map options for profile"
android:textColor="@color/description_font_and_bottom_sheet_icons"/>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="64dp"
android:layout_marginLeft="64dp"
android:background="@color/divider_light"/>
<LinearLayout
android:id="@+id/screen_config_btn"
android:layout_width="match_parent"
android:layout_height="64dp"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/screen_config_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:src="@drawable/ic_configure_screen_dark"
android:tint="?attr/primary_icon_color"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="24dp"
android:layout_marginRight="@dimen/list_content_padding"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Configure screen"
android:textColor="?attr/main_font_color_basic"
android:textSize="@dimen/default_list_text_size"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Select default screen options for profile"
android:textColor="@color/description_font_and_bottom_sheet_icons"/>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="64dp"
android:layout_marginLeft="64dp"
android:background="@color/divider_light"
/>
<LinearLayout
android:id="@+id/nav_settings_btn"
android:layout_width="match_parent"
android:layout_height="64dp"
android:gravity="center_vertical"
android:background="?android:attr/selectableItemBackground"
android:orientation="horizontal">
<ImageView
android:id="@+id/nav_settings_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginLeft="@dimen/list_content_padding"
android:src="@drawable/ic_action_gdirections_dark"
android:tint="?attr/primary_icon_color"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="24dp"
android:layout_marginRight="@dimen/list_content_padding"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Navigation settings"
android:textColor="?attr/main_font_color_basic"
android:textSize="@dimen/default_list_text_size"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Select default navigation settings for profile"
android:textColor="@color/description_font_and_bottom_sheet_icons"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<include layout="@layout/list_shadow_footer"/>
<LinearLayout
android:id="@+id/buttons_layout_sv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/bg_color"
android:orientation="horizontal"
android:visibility="gone">
<Button
android:id="@+id/cancel_button_sv"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="8dp"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="8dp"
android:background="@drawable/btn_round_profile_gray"
android:text="@string/shared_string_cancel"
android:textAllCaps="false"
android:textColor="@color/map_widget_blue"/>
<Button
android:id="@+id/save_profile_btn_sv"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="8dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="8dp"
android:layout_marginRight="@dimen/list_content_padding"
android:background="@drawable/btn_round_profile_blue"
android:text="@string/shared_string_save"
android:textAllCaps="false"
android:textColor="@color/color_white"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/buttons_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/bg_color"
android:orientation="horizontal">
<Button
android:id="@+id/cancel_button"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="@dimen/list_content_padding"
android:layout_marginEnd="8dp"
android:layout_marginLeft="@dimen/list_content_padding"
android:layout_marginRight="8dp"
android:background="@drawable/btn_round_profile_gray"
android:text="@string/shared_string_cancel"
android:textAllCaps="false"
android:textColor="@color/map_widget_blue"/>
<Button
android:id="@+id/save_profile_btn"
android:layout_width="0dp"
android:layout_height="42dp"
android:layout_weight="1"
android:layout_marginTop="@dimen/list_content_padding"
android:layout_marginBottom="@dimen/list_content_padding"
android:layout_marginStart="8dp"
android:layout_marginEnd="@dimen/list_content_padding"
android:layout_marginLeft="8dp"
android:layout_marginRight="@dimen/list_content_padding"
android:background="@drawable/btn_round_profile_blue"
android:text="@string/shared_string_save"
android:textAllCaps="false"
android:textColor="@color/color_white"/>
</LinearLayout>
</FrameLayout>

View file

@ -35,6 +35,80 @@
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:id="@+id/type_selection_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding_small"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding_small"
android:orientation="horizontal">
<ImageView
android:id="@+id/mode_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin"
android:layout_gravity="center_vertical"
android:tint="?attr/primary_icon_color"
tools:src="@drawable/ic_action_coordinates_latitude"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/mode_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.ListItemTitle"
android:textColor="?attr/main_font_color_basic"
tools:text="Item Title"/>
<TextView
android:id="@+id/mode_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
tools:text="Item additional desription"/>
</LinearLayout>
<ImageView
android:id="@+id/type_down_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_action_arrow_drop_down"/>
</LinearLayout>
<ImageView
android:id="@+id/bottom_shadow2"
android:layout_width="match_parent"
android:layout_height="5dp"
android:scaleType="fitXY"
android:src="@drawable/bg_shadow_list_bottom"/>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profile_settings"
android:layout_width="match_parent"
android:layout_height="@dimen/setting_profile_item_height"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/profile_icon"
android:layout_width="wrap_content"
android:layout_height="@dimen/standard_icon_size"
android:layout_marginLeft="@dimen/setting_profile_image_margin"
android:layout_marginRight="@dimen/setting_profile_image_margin"
tools:src="@drawable/ic_action_bicycle_dark"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/settings_divider"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:layout_weight="5">
<TextView
android:id="@+id/profile_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/main_font_dark"
android:textSize="@dimen/default_list_text_size"
tools:text="Bicycle"/>
<TextView
android:id="@+id/profile_descr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/default_desc_text_size"
tools:text="Type: Bicycle"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_action_additional_option"
android:tint="?attr/primary_icon_color"
android:layout_gravity="center_vertical"
android:paddingLeft="@dimen/setting_profile_item_switch_margin"
android:paddingRight="10dp"
android:paddingBottom="@dimen/setting_profile_item_switch_margin"
android:paddingTop="@dimen/setting_profile_item_switch_margin"
android:paddingStart="@dimen/setting_profile_item_switch_margin"
android:paddingEnd="10dp"/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/profile_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:paddingTop="@dimen/setting_profile_item_switch_margin"
android:paddingBottom="@dimen/setting_profile_item_switch_margin"
android:paddingLeft="16dp"
android:paddingStart="16dp"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/ctx_menu_info_view_bg"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/profiles_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingBottom="72dp"/>
<LinearLayout
android:id="@+id/add_profile_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:clickable="true"
android:focusable="true"
android:layout_gravity="bottom|center_horizontal"
android:background="@drawable/fab_extended_drawable"
android:orientation="horizontal"
android:gravity="center_vertical">
<android.support.v7.widget.AppCompatImageView
android:layout_gravity="center_vertical"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginLeft="14dp"
android:layout_marginStart="14dp"
android:src="@drawable/ic_action_plus"/>
<android.support.v7.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/shared_string_add"
android:textColor="@color/color_white"
android:layout_marginRight="20dp"
android:layout_marginEnd="20dp"
/>
</LinearLayout>
</RelativeLayout>

View file

@ -258,6 +258,8 @@
<color name="shadow_color">#33888888</color>
<color name="compass_control_active">#EE5622</color>
<color name="sherpafy_selection">#ff33b5e5</color>
<color name="sherpafy_add_text">#b9b9b9</color>
@ -455,6 +457,9 @@
<color name="wiki_webview_background_light">#ffffff</color>
<color name="wiki_webview_background_dark">#17191a</color>
<color name="text_field_box_dark">#3F474B</color>
<color name="text_field_box_light">#eeeeee</color>
<!-- Basic colors -->
<color name="app_bar_main_dark">#101112</color>
<color name="app_bar_main_light">#ff8800</color>
@ -477,5 +482,8 @@
<color name="inactive_buttons_and_links_dark">#505050</color>
<color name="active_buttons_and_links_trans_light">#66237bff</color>
<color name="active_buttons_and_links_trans_dark">#66d28521</color>
<color name="settings_divider">#dcdcdc</color>
<color name="profile_button_gray">#EFEFEF</color>
<color name="profile_button_gray_pressed">#ababab</color>
</resources>

View file

@ -8,5 +8,8 @@
<!-- Time control widget ids-->
<item name="time_control_widget_state_arrival_time" type="id"/>
<item name="time_control_widget_state_time_to_go" type="id"/>
<!-- Compass control widget ids-->
<item name="compass_ruler_control_widget_state_show" type="id"/>
<item name="compass_ruler_control_widget_state_hide" type="id"/>
</resources>

View file

@ -244,6 +244,7 @@
<dimen name="bottom_sheet_title_height">52dp</dimen>
<dimen name="bottom_sheet_descr_height">44dp</dimen>
<dimen name="bottom_sheet_list_item_height">48dp</dimen>
<dimen name="bottom_sheet_large_list_item_height">64dp</dimen>
<dimen name="bottom_sheet_icon_margin">24dp</dimen>
<dimen name="bottom_sheet_divider_margin_top">7dp</dimen>
<dimen name="bottom_sheet_divider_margin_bottom">8dp</dimen>
@ -340,4 +341,9 @@
<dimen name="pages_item_padding">3dp</dimen>
<dimen name="pages_item_margin">4dp</dimen>
<dimen name="setting_profile_item_height">64dp</dimen>
<dimen name="setting_profile_image_margin">24dp</dimen>
<dimen name="setting_profile_item_switch_margin">18dp</dimen>
</resources>

View file

@ -11,6 +11,44 @@
Thx - Hardy
-->
<string name="show_compass_ruler">Show compass ruler</string>
<string name="hide_compass_ruler">Hide compass ruler</string>
<string name="select_icon_profile_dialog_title">Select icon</string>
<string name="settings_routing_mode_string">Mode: %s</string>
<string name="settings_derived_routing_mode_string">User Mode, derived from: %s</string>
<string name="routing_profile_ski">Ski</string>
<string name="profile_type_descr_string">Type: %s</string>
<string name="profile_type_base_string">Base Profile</string>
<string name="profile_alert_need_routing_type_title">Select navigation type</string>
<string name="profile_alert_need_routing_type_msg">You need to select Navigation type to create New Application Profile</string>
<string name="profile_alert_need_profile_name_title">Enter Profile Name</string>
<string name="profile_alert_need_profile_name_msg">Profile name shouldn\'t be empty!</string>
<string name="profile_alert_duplicate_name_title">Duplicate Name</string>
<string name="profile_alert_duplicate_name_msg">There is already profile with such name</string>
<string name="profile_alert_cant_delete_base">You cannot delete OsmAnd base profiles</string>
<string name="profile_alert_need_save_title">Save Changes</string>
<string name="profile_alert_need_save_msg">You need to save changes to Profile before proceed</string>
<string name="profile_alert_delete_title">Delete Profile</string>
<string name="profile_alert_delete_msg">Are you sure you want to delete %s profile</string>
<string name="select_base_profile_dialog_title">Select base profile</string>
<string name="select_base_profile_dialog_message">Custom Application Profile should be based on one of the default App Profiles. Selected Profile defines basic settings: setup of Widgets, units of speed and distance. In string below Profile\'s name, you could learn which Navigation Profiles are suitable for each Application Profile.</string>
<string name="select_nav_profile_dialog_title">Select navigation type</string>
<string name="base_profile_descr_car">Car, Truck, Motorcycle</string>
<string name="base_profile_descr_bicycle">MBT, Moped, Skiing, Horse</string>
<string name="base_profile_descr_pedestrian">Walking, Hiking, Running</string>
<string name="base_profile_descr_public_transport">All PT types</string>
<string name="base_profile_descr_boat">Ship, Rowing, Sailing</string>
<string name="base_profile_descr_aircraft">Airplane, Gliding</string>
<string name="routing_profile_geocoding">Geocoding</string>
<string name="routing_profile_straightline">Straight Line</string>
<string name="routing_profile_broutrer">BRouter (Offline)</string>
<string name="osmand_default_routing">OsmAnd routing</string>
<string name="custom_routing">Custom routing profile</string>
<string name="special_routing_type">Special routing</string>
<string name="third_party_routing_type">3rd-party routing</string>
<string name="application_profiles_descr">Select the profiles to be visible in the app.</string>
<string name="application_profiles">Application profiles</string>
<string name="searching_gps">Searching GPS</string>
<string name="coordinates_widget">Coordinates widget</string>
<string name="files_moved">Moved %1$d files (%2$s).</string>
@ -26,6 +64,7 @@
<string name="public_transport_calc_pedestrian">Calculate pedestrian route</string>
<string name="public_transport_type">Transport type</string>
<string name="send_log">Send log</string>
<string name="routing_attr_avoid_tram_name">Avoid trams</string>
<string name="routing_attr_avoid_tram_description">Avoid trams</string>
<string name="routing_attr_avoid_bus_name">Avoid buses</string>
@ -3119,4 +3158,5 @@
<string name="routing_attr_avoid_sett_name">Avoid cobblestone and sett</string>
<string name="routing_attr_avoid_sett_description">Avoid cobblestone and sett</string>
</resources>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference android:key="general_settings" android:title="@string/general_settings_2" android:summary="@string/general_settings_descr"/>
<Preference android:key="application_profiles" android:title="@string/application_profiles" android:summary="@string/application_profiles_descr"/>
<Preference android:key="routing_settings" android:title="@string/routing_settings_2" android:summary="@string/routing_settings_descr"/>
<Preference android:key="subscription_settings" android:title="@string/osm_live_subscription" android:summary="@string/osm_live_subscription_desc"/>
<PreferenceCategory android:key="plugin_settings" android:title="@string/plugin_settings" />

View file

@ -15,6 +15,7 @@ public class AMapPoint implements Parcelable {
public static final String POINT_SPEED_PARAM = "point_speed_param";
public static final String POINT_TYPE_ICON_NAME_PARAM = "point_type_icon_name_param";
public static final String POINT_STALE_LOC_PARAM = "point_stale_loc_param";
public static final String POINT_BEARING_PARAM = "point_bearing_param";
private String id;
private String shortName;

View file

@ -11,6 +11,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.os.Build;
import android.support.v7.app.AlertDialog;
@ -56,6 +57,7 @@ import net.osmand.plus.voice.TTSCommandPlayerImpl;
import net.osmand.plus.wikivoyage.data.TravelDbHelper;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.RoutingConfiguration;
import net.osmand.router.RoutingConfiguration.Builder;
import net.osmand.util.Algorithms;
import net.osmand.util.OpeningHoursParser;
@ -190,7 +192,9 @@ public class AppInitializer implements IProgress {
}
app.getSettings().SHOW_TRAVEL_UPDATE_CARD.set(true);
app.getSettings().SHOW_TRAVEL_NEEDED_MAPS_CARD.set(true);
ApplicationMode.initCustomModes(app.getSettings());
initSettings = true;
}
public int getNumberOfStarts() {
@ -456,6 +460,7 @@ public class AppInitializer implements IProgress {
} catch (Exception e) {
e.printStackTrace();
}
getLazyRoutingConfig();
app.applyTheme(app);
app.inAppPurchaseHelper = startupInit(new InAppPurchaseHelper(app), InAppPurchaseHelper.class);
app.poiTypes = startupInit(MapPoiTypes.getDefaultNoInit(), MapPoiTypes.class);
@ -547,29 +552,35 @@ public class AppInitializer implements IProgress {
return object;
}
public net.osmand.router.RoutingConfiguration.Builder getLazyDefaultRoutingConfig() {
long tm = System.currentTimeMillis();
@SuppressLint("StaticFieldLeak")
private void getLazyRoutingConfig() {
new AsyncTask<Void, Void, RoutingConfiguration.Builder>() {
@Override
protected Builder doInBackground(Void... voids) {
File routingFolder = app.getAppPath(IndexConstants.ROUTING_PROFILES_DIR);
RoutingConfiguration.Builder builder = RoutingConfiguration.getDefault();
if (routingFolder.isDirectory() && routingFolder.listFiles().length > 0) {
File[] fl = routingFolder.listFiles();
for (File f : fl) {
if (f.isFile() && f.getName().endsWith(".xml") && f.canRead()) {
try {
File routingXml = app.getAppPath(IndexConstants.ROUTING_XML_FILE);
if (routingXml.exists() && routingXml.canRead()) {
try {
return RoutingConfiguration.parseFromInputStream(new FileInputStream(routingXml));
RoutingConfiguration.parseFromInputStream(new FileInputStream(f), f.getName(), builder);
} catch (XmlPullParserException | IOException e) {
throw new IllegalStateException(e);
}
} else {
return RoutingConfiguration.getDefault();
}
} finally {
long te = System.currentTimeMillis();
if(te - tm > 30) {
System.err.println("Defalt routing config init took " + (te - tm) + " ms");
}
}
}
return builder;
}
@Override
protected void onPostExecute(Builder builder) {
super.onPostExecute(builder);
app.updateRoutingConfig(builder);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public synchronized void initVoiceDataInDifferentThread(final Activity uiContext,

View file

@ -2,6 +2,14 @@ package net.osmand.plus;
import android.content.Context;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.LinkedHashSet;
import net.osmand.PlatformUtil;
import net.osmand.StateChangedListener;
import java.util.ArrayList;
@ -12,53 +20,46 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import net.osmand.plus.routing.RouteProvider.RouteService;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
public class ApplicationMode {
private static final Log LOG = PlatformUtil.getLog(ApplicationMode.class);
private static Map<String, Set<ApplicationMode>> widgetsVisibilityMap = new LinkedHashMap<>();
private static Map<String, Set<ApplicationMode>> widgetsAvailabilityMap = new LinkedHashMap<>();
private static List<ApplicationMode> defaultValues = new ArrayList<>();
private static List<ApplicationMode> values = new ArrayList<>();
private static List<ApplicationMode> cachedFilteredValues = new ArrayList<>();
/*
* DEFAULT("Browse map"), CAR("Car"), BICYCLE("Bicycle"), PEDESTRIAN("Pedestrian"); NAUTICAL("boat")
* DEFAULT("Browse map"), CAR("Car"), BICYCLE("Bicycle"), PEDESTRIAN("Pedestrian"); NAUTICAL("boat"); PUBLIC_TRANSPORT("Public transport"); AIRCRAFT("Aircraft")
*/
public static final ApplicationMode DEFAULT = create(R.string.app_mode_default, "default").speed(1.5f, 5).arrivalDistance(90).defLocation().
icon(R.drawable.map_world_globe_dark, R.drawable.ic_world_globe_dark).reg();
public static final ApplicationMode CAR = create(R.string.app_mode_car, "car").speed(15.3f, 35).carLocation().
icon(R.drawable.map_action_car_dark, R.drawable.ic_action_car_dark).reg();
icon(R.drawable.map_action_car_dark, R.drawable.ic_action_car_dark).setRoutingProfile("car").reg();
public static final ApplicationMode BICYCLE = create(R.string.app_mode_bicycle, "bicycle").speed(5.5f, 15).arrivalDistance(60).offRouteDistance(50).bicycleLocation().
icon(R.drawable.map_action_bicycle_dark, R.drawable.ic_action_bicycle_dark).reg();
icon(R.drawable.map_action_bicycle_dark, R.drawable.ic_action_bicycle_dark).setRoutingProfile("bicycle").reg();
public static final ApplicationMode PEDESTRIAN = create(R.string.app_mode_pedestrian, "pedestrian").speed(1.5f, 5).arrivalDistance(45).offRouteDistance(20).
icon(R.drawable.map_action_pedestrian_dark, R.drawable.ic_action_pedestrian_dark).reg();
icon(R.drawable.map_action_pedestrian_dark, R.drawable.ic_action_pedestrian_dark).setRoutingProfile("pedestrian").reg();
public static final ApplicationMode PUBLIC_TRANSPORT = create(R.string.app_mode_public_transport, "public_transport").
icon(R.drawable.map_action_bus_dark, R.drawable.ic_action_bus_dark).reg();
icon(R.drawable.map_action_bus_dark, R.drawable.ic_action_bus_dark).setRoutingProfile("public_transport").reg();
public static final ApplicationMode BOAT = create(R.string.app_mode_boat, "boat").speed(5.5f, 20).carLocation().nauticalLocation().
icon(R.drawable.map_action_sail_boat_dark, R.drawable.ic_action_sail_boat_dark).reg();
public static final ApplicationMode BOAT = create(R.string.app_mode_boat, "boat").speed(5.5f, 20).nauticalLocation().
icon(R.drawable.map_action_sail_boat_dark, R.drawable.ic_action_sail_boat_dark).setRoutingProfile("boat").reg();
public static final ApplicationMode AIRCRAFT = create(R.string.app_mode_aircraft, "aircraft").speed(40f, 100).carLocation().
icon(R.drawable.map_action_aircraft, R.drawable.ic_action_aircraft).reg();
public static final ApplicationMode HIKING = create(R.string.app_mode_hiking, "hiking").speed(1.5f, 5).parent(PEDESTRIAN).
icon(R.drawable.map_action_trekking_dark, R.drawable.ic_action_trekking_dark).reg();
public static final ApplicationMode MOTORCYCLE = create(R.string.app_mode_motorcycle, "motorcycle").speed(15.3f, 40).
carLocation().parent(CAR).
icon(R.drawable.map_action_motorcycle_dark, R.drawable.ic_action_motorcycle_dark).reg();
public static final ApplicationMode TRUCK = create(R.string.app_mode_truck, "truck").speed(15.3f, 40).
carLocation().parent(CAR).
icon(R.drawable.map_action_truck_dark, R.drawable.ic_action_truck_dark).reg();
public static final ApplicationMode TRAIN = create(R.string.app_mode_train, "train").speed(25f, 40).
carLocation().icon(R.drawable.map_action_train, R.drawable.ic_action_train).reg();
icon(R.drawable.map_action_aircraft, R.drawable.ic_action_aircraft).setRouteService(RouteService.STRAIGHT).setRoutingProfile("STRAIGHT_LINE_MODE").reg();
static {
ApplicationMode[] exceptDefault = new ApplicationMode[]{CAR, PEDESTRIAN, BICYCLE, BOAT, AIRCRAFT, PUBLIC_TRANSPORT, TRAIN};
ApplicationMode[] exceptPedestrianAndDefault = new ApplicationMode[]{CAR, BICYCLE, BOAT, AIRCRAFT, PUBLIC_TRANSPORT, TRAIN};
ApplicationMode[] exceptDefault = new ApplicationMode[]{CAR, PEDESTRIAN, BICYCLE, BOAT, AIRCRAFT, PUBLIC_TRANSPORT};
ApplicationMode[] exceptPedestrianAndDefault = new ApplicationMode[]{CAR, BICYCLE, BOAT, AIRCRAFT, PUBLIC_TRANSPORT};
ApplicationMode[] exceptAirBoatDefault = new ApplicationMode[]{CAR, BICYCLE, PEDESTRIAN};
ApplicationMode[] pedestrian = new ApplicationMode[]{PEDESTRIAN};
ApplicationMode[] pedestrianBicycle = new ApplicationMode[]{PEDESTRIAN, BICYCLE};
@ -100,13 +101,16 @@ public class ApplicationMode {
regWidgetVisibility("bgService", none);
}
private static class ApplicationModeBuilder {
public static class ApplicationModeBuilder {
private ApplicationMode applicationMode;
public ApplicationMode reg() {
values.add(applicationMode);
defaultValues.add(applicationMode);
return applicationMode;
}
public ApplicationMode customReg() {
values.add(applicationMode);
return applicationMode;
}
@ -117,6 +121,22 @@ public class ApplicationMode {
return this;
}
public ApplicationModeBuilder parent(ApplicationMode parent) {
applicationMode.parent = parent;
String parentTypeName = parent.getStringKey();
if (parentTypeName.equals("car") || parentTypeName.equals("aircraft")) {
this.carLocation();
} else if (parentTypeName.equals("bicycle")) {
this.bicycleLocation();
} else if (parentTypeName.equals("boat")) {
this.nauticalLocation();
} else {
this.defLocation();
}
return this;
}
public ApplicationModeBuilder carLocation() {
applicationMode.bearingIconDay = R.drawable.map_car_bearing;
applicationMode.bearingIconNight = R.drawable.map_car_bearing_night;
@ -129,11 +149,6 @@ public class ApplicationMode {
return this;
}
public ApplicationModeBuilder parent(ApplicationMode parent) {
applicationMode.parent = parent;
return this;
}
public ApplicationModeBuilder bicycleLocation() {
applicationMode.bearingIconDay = R.drawable.map_bicycle_bearing;
applicationMode.bearingIconNight = R.drawable.map_bicycle_bearing_night;
@ -183,6 +198,21 @@ public class ApplicationMode {
applicationMode.offRouteDistance = offRouteDistance;
return this;
}
public ApplicationModeBuilder userProfileTitle(String userProfileTitle) {
applicationMode.userProfileName = userProfileTitle;
return this;
}
public ApplicationModeBuilder setRoutingProfile(String routingProfileName) {
applicationMode.routingProfile = routingProfileName;
return this;
}
public ApplicationModeBuilder setRouteService(RouteService service) {
applicationMode.routeService = service;
return this;
}
}
private static ApplicationModeBuilder create(int key, String stringKey) {
@ -191,6 +221,10 @@ public class ApplicationMode {
return builder;
}
public static ApplicationModeBuilder createCustomMode(String userProfileTitle, String stringKey) {
return create(-1, stringKey).userProfileTitle(userProfileTitle);
}
private ApplicationMode(int key, String stringKey) {
this.key = key;
this.stringKey = stringKey;
@ -229,9 +263,12 @@ public class ApplicationMode {
}
public static List<ApplicationMode> allPossibleValues() {
return new ArrayList<ApplicationMode>(values);
return new ArrayList<>(values);
}
public static List<ApplicationMode> getDefaultValues() {
return new ArrayList<>(defaultValues);
}
// returns modifiable ! Set<ApplicationMode> to exclude non-wanted derived
public static Set<ApplicationMode> regWidgetVisibility(String widgetId, ApplicationMode... am) {
@ -363,11 +400,24 @@ public class ApplicationMode {
}
public String toHumanString(Context ctx) {
if (Algorithms.isEmpty(userProfileName) && key != -1) {
return ctx.getString(key);
} else {
return userProfileName;
}
}
public String toHumanStringCtx(Context ctx) {
if (Algorithms.isEmpty(userProfileName)) {
return ctx.getString(key);
} else {
return userProfileName;
}
}
public RouteService getRouteService() {
return routeService;
}
public static ApplicationMode valueOfStringKey(String key, ApplicationMode def) {
@ -399,24 +449,92 @@ public class ApplicationMode {
return this == mode || getParent() == mode;
}
private final int key;
private final String stringKey;
public String getRoutingProfile() {
return routingProfile;
}
private ApplicationMode parent;
private int mapIconId = R.drawable.map_world_globe_dark;
private int smallIconDark = R.drawable.ic_world_globe_dark;
private float defaultSpeed = 10f;
private int minDistanceForTurn = 50;
private int arrivalDistance = 90;
private int offRouteDistance = 350;
private int bearingIconDay = R.drawable.map_pedestrian_bearing;
private int bearingIconNight = R.drawable.map_pedestrian_bearing_night;
private int headingIconDay = R.drawable.map_pedestrian_location_view_angle;
private int headingIconNight = R.drawable.map_pedestrian_location_view_angle_night;
private int locationIconDay = R.drawable.map_pedestrian_location;
private int locationIconNight = R.drawable.map_pedestrian_location_night;
private int locationIconDayLost = R.drawable.map_pedestrian_location_lost;
private int locationIconNightLost = R.drawable.map_pedestrian_location_lost_night;
public String getUserProfileName() {
return userProfileName;
}
@Expose private final int key;
@Expose private final String stringKey;
@Expose private String userProfileName;
@Expose private ApplicationMode parent;
@Expose private int mapIconId = R.drawable.map_world_globe_dark;
@Expose private int smallIconDark = R.drawable.ic_world_globe_dark;
@Expose private float defaultSpeed = 10f;
@Expose private int minDistanceForTurn = 50;
@Expose private int arrivalDistance = 90;
@Expose private int offRouteDistance = 350;
@Expose private int bearingIconDay = R.drawable.map_pedestrian_bearing;
@Expose private int bearingIconNight = R.drawable.map_pedestrian_bearing_night;
@Expose private int headingIconDay = R.drawable.map_pedestrian_location_view_angle;
@Expose private int headingIconNight = R.drawable.map_pedestrian_location_view_angle_night;
@Expose private int locationIconDay = R.drawable.map_pedestrian_location;
@Expose private int locationIconNight = R.drawable.map_pedestrian_location_night;
@Expose private int locationIconDayLost = R.drawable.map_pedestrian_location_lost;
@Expose private int locationIconNightLost = R.drawable.map_pedestrian_location_lost_night;
@Expose private String routingProfile = null;
@Expose private RouteService routeService = RouteService.OSMAND;
private static StateChangedListener<String> listener;
private static OsmAndAppCustomization.OsmAndAppCustomizationListener customizationListener;
public static void saveCustomModeToSettings(OsmandSettings settings){
List<ApplicationMode> customModes = new ArrayList<>();
for (ApplicationMode mode : values) {
if (mode.parent != null) {
customModes.add(mode);
}
}
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
String profiles = gson.toJson(customModes);
settings.CUSTOM_APP_PROFILES.set(profiles);
}
public static void initCustomModes(OsmandSettings settings){
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Type t = new TypeToken<ArrayList<ApplicationMode>>() {}.getType();
List<ApplicationMode> customProfiles = gson.fromJson(settings.CUSTOM_APP_PROFILES.get(), t);
if (!Algorithms.isEmpty(customProfiles)) {
for (ApplicationMode m : customProfiles) {
if (!values.contains(m)) {
values.add(m);
}
}
}
}
public static void deleteCustomMode(String userModeTitle, OsmandApplication app) {
Iterator<ApplicationMode> it = values.iterator();
while (it.hasNext()) {
ApplicationMode m = it.next();
if (m.userProfileName != null && m.userProfileName.equals(userModeTitle)) {
it.remove();
}
}
ApplicationMode.saveCustomModeToSettings(app.getSettings());
}
public static boolean changeProfileStatus(ApplicationMode mode, boolean isSelected, OsmandApplication app) {
Set<ApplicationMode> selectedModes = new LinkedHashSet<>(ApplicationMode.values(app));
StringBuilder vls = new StringBuilder(ApplicationMode.DEFAULT.getStringKey() + ",");
if (allPossibleValues().contains(mode)) {
if (isSelected) {
selectedModes.add(mode);
} else {
selectedModes.remove(mode);
if (app.getSettings().APPLICATION_MODE.get() == mode) {
app.getSettings().APPLICATION_MODE.set(ApplicationMode.DEFAULT);
}
}
for (ApplicationMode m : selectedModes) {
vls.append(m.getStringKey()).append(",");
}
app.getSettings().AVAILABLE_APP_MODES.set(vls.toString());
return true;
}
return false;
}
}

View file

@ -137,10 +137,10 @@ public class CurrentPositionHelper {
for (BinaryMapReaderResource rep : checkReaders) {
rs[i++] = rep.getReader(BinaryMapReaderResourceType.STREET_LOOKUP);
}
RoutingConfiguration cfg = app.getDefaultRoutingConfig().build(p, 10,
RoutingConfiguration cfg = app.getRoutingConfig().build(p, 10,
new HashMap<String, String>());
ctx = new RoutePlannerFrontEnd().buildRoutingContext(cfg, null, rs);
RoutingConfiguration defCfg = app.getDefaultRoutingConfig().build("geocoding", 10,
RoutingConfiguration defCfg = app.getRoutingConfig().build("geocoding", 10,
new HashMap<String, String>());
defCtx = new RoutePlannerFrontEnd().buildRoutingContext(defCfg, null, rs);
} else {

View file

@ -64,6 +64,7 @@ import net.osmand.plus.search.QuickSearchHelper;
import net.osmand.plus.voice.CommandPlayer;
import net.osmand.plus.wikivoyage.data.TravelDbHelper;
import net.osmand.router.RoutingConfiguration;
import net.osmand.router.RoutingConfiguration.Builder;
import net.osmand.search.SearchUICore;
import net.osmand.util.Algorithms;
@ -127,7 +128,7 @@ public class OsmandApplication extends MultiDexApplication {
InAppPurchaseHelper inAppPurchaseHelper;
MapViewTrackingUtilities mapViewTrackingUtilities;
RoutingConfiguration.Builder defaultRoutingConfig;
private RoutingConfiguration.Builder routingConfig;
private Locale preferredLocale = null;
private Locale defaultLocale;
private File externalStorageDirectory;
@ -797,11 +798,18 @@ public class OsmandApplication extends MultiDexApplication {
return lang;
}
public synchronized RoutingConfiguration.Builder getDefaultRoutingConfig() {
if(defaultRoutingConfig == null) {
defaultRoutingConfig = appInitializer.getLazyDefaultRoutingConfig();
public synchronized RoutingConfiguration.Builder getRoutingConfig() {
RoutingConfiguration.Builder rc;
if(routingConfig == null) {
rc = new RoutingConfiguration.Builder();
} else {
rc = routingConfig;
}
return defaultRoutingConfig;
return rc;
}
public void updateRoutingConfig(Builder update) {
routingConfig = update;
}
public OsmandRegions getRegions() {

View file

@ -803,6 +803,8 @@ public class OsmandSettings {
public final CommonPreference<RulerMode> RULER_MODE = new EnumIntPreference<>("ruler_mode", RulerMode.FIRST, RulerMode.values()).makeGlobal();
public final OsmandPreference<Boolean> SHOW_COMPASS_CONTROL_RULER = new BooleanPreference("show_compass_ruler", true).makeGlobal();
public final CommonPreference<Boolean> SHOW_LINES_TO_FIRST_MARKERS = new BooleanPreference("show_lines_to_first_markers", false).makeProfile();
public final CommonPreference<Boolean> SHOW_ARROWS_TO_FIRST_MARKERS = new BooleanPreference("show_arrows_to_first_markers", false).makeProfile();
@ -2745,6 +2747,9 @@ public class OsmandSettings {
RateUsBottomSheetDialog.RateUsState.INITIAL_STATE, RateUsBottomSheetDialog.RateUsState.values())
.makeGlobal();
public final CommonPreference<String> CUSTOM_APP_PROFILES =
new StringPreference("custom_profiles", "").makeGlobal().cache();
public enum DayNightMode {
AUTO(R.string.daynight_mode_auto, R.drawable.ic_action_map_sunst),

View file

@ -9,8 +9,11 @@ import android.support.v7.widget.Toolbar;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
@ -56,6 +59,7 @@ public abstract class ActionBarPreferenceActivity extends AppCompatPreferenceAct
});
getSpinner().setVisibility(View.GONE);
getTypeButton().setVisibility(View.GONE);
setProgressVisibility(false);
}
@ -81,6 +85,25 @@ public abstract class ActionBarPreferenceActivity extends AppCompatPreferenceAct
return (Spinner) findViewById(R.id.spinner_nav);
}
protected LinearLayout getTypeButton() {
return (LinearLayout) findViewById(R.id.type_selection_button);
}
protected TextView getModeTitleTV() {
return (TextView) findViewById(R.id.mode_title);
}
protected TextView getModeSubTitleTV() {
return (TextView) findViewById(R.id.mode_subtitle);
}
protected ImageView getModeIconIV() {
return (ImageView) findViewById(R.id.mode_icon);
}
protected ImageView getDropDownArrow() {
return (ImageView) findViewById(R.id.type_down_arrow);
}
protected void setProgressVisibility(boolean visibility) {
if (visibility) {
findViewById(R.id.ProgressBar).setVisibility(View.VISIBLE);

View file

@ -14,6 +14,7 @@
* limitations under the License.
*/
package net.osmand.plus.activities;
import android.app.FragmentManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;

View file

@ -82,6 +82,7 @@ import net.osmand.plus.base.FailSafeFuntions;
import net.osmand.plus.base.MapViewTrackingUtilities;
import net.osmand.plus.chooseplan.OsmLiveCancelledDialog;
import net.osmand.plus.dashboard.DashboardOnMap;
import net.osmand.plus.dashboard.DashboardOnMap.DashboardType;
import net.osmand.plus.dialogs.ErrorBottomSheetDialog;
import net.osmand.plus.dialogs.RateUsBottomSheetDialog;
import net.osmand.plus.dialogs.WhatsNewDialogFragment;
@ -107,6 +108,7 @@ import net.osmand.plus.mapmarkers.PlanRouteFragment;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.measurementtool.NewGpxData;
import net.osmand.plus.profiles.EditProfileFragment;
import net.osmand.plus.render.RendererRegistry;
import net.osmand.plus.resources.ResourceManager;
import net.osmand.plus.routepreparationmenu.ChooseRouteFragment;
@ -782,6 +784,19 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
}
setIntent(null);
}
if (intent.hasExtra(EditProfileFragment.OPEN_CONFIG_ON_MAP)) {
switch (intent.getStringExtra(EditProfileFragment.OPEN_CONFIG_ON_MAP)) {
case EditProfileFragment.MAP_CONFIG:
this.getDashboard().setDashboardVisibility(true, DashboardType.CONFIGURE_MAP, null);
break;
case EditProfileFragment.SCREEN_CONFIG:
this.getDashboard().setDashboardVisibility(true, DashboardType.CONFIGURE_SCREEN, null);
break;
}
setIntent(null);
}
}
mapView.refreshMap(true);
if (atlasMapRendererView != null) {

View file

@ -14,6 +14,7 @@ import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R;
import net.osmand.plus.Version;
import net.osmand.plus.liveupdates.OsmLiveActivity;
import net.osmand.plus.profiles.SettingsProfileActivity;
public class SettingsActivity extends SettingsBaseActivity {
@ -28,6 +29,7 @@ public class SettingsActivity extends SettingsBaseActivity {
private Preference general;
private Preference routing;
private Preference subscription;
private Preference profiles;
@Override
@ -40,6 +42,8 @@ public class SettingsActivity extends SettingsBaseActivity {
general.setOnPreferenceClickListener(this);
routing = (Preference) screen.findPreference("routing_settings");
routing.setOnPreferenceClickListener(this);
profiles = (Preference) screen.findPreference("application_profiles");
profiles.setOnPreferenceClickListener(this);
subscription = (Preference) screen.findPreference("subscription_settings");
subscription.setOnPreferenceClickListener(this);
@ -88,6 +92,9 @@ public class SettingsActivity extends SettingsBaseActivity {
if (preference == general) {
startActivity(new Intent(this, SettingsGeneralActivity.class));
return true;
} else if (preference == profiles){
startActivity(new Intent(this, SettingsProfileActivity.class));
return true;
} else if (preference == routing) {
startActivity(new Intent(this, SettingsNavigationActivity.class));
return true;

View file

@ -2,6 +2,8 @@ package net.osmand.plus.activities;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
@ -13,12 +15,12 @@ import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import net.osmand.plus.ApplicationMode;
@ -38,6 +40,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.osmand.util.Algorithms;
public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity
@ -328,7 +331,6 @@ public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity
settings = app.getSettings();
getToolbar().setTitle(R.string.shared_string_settings);
if (profileSettings) {
modes.clear();
for (ApplicationMode a : ApplicationMode.values(app)) {
@ -336,53 +338,64 @@ public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity
modes.add(a);
}
}
List<String> s = new ArrayList<String>();
for (ApplicationMode a : modes) {
s.add(a.toHumanString(app));
}
SpinnerAdapter spinnerAdapter = new SpinnerAdapter(this,
R.layout.spinner_item, s);
// android.R.layout.simple_spinner_dropdown_item
spinnerAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
getSpinner().setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
settings.APPLICATION_MODE.set(modes.get(position));
updateAllSettings();
}
getTypeButton().setVisibility(View.VISIBLE);
getTypeButton().setOnClickListener(new View.OnClickListener() {
@Override
public void onNothingSelected(AdapterView<?> parent) {
public void onClick(View v) {
AlertDialog.Builder singleSelectDialogBuilder = new Builder(SettingsBaseActivity.this);
singleSelectDialogBuilder.setTitle("Select App Profile");
final ArrayAdapter<String> modeNames = new ArrayAdapter<>(
SettingsBaseActivity.this, android.R.layout.select_dialog_singlechoice);
for (ApplicationMode am : modes) {
modeNames.add(am.toHumanString(SettingsBaseActivity.this));
}
singleSelectDialogBuilder.setNegativeButton(R.string.shared_string_cancel,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
getSpinner().setAdapter(spinnerAdapter);
getSpinner().setVisibility(View.VISIBLE);
singleSelectDialogBuilder.setAdapter(modeNames, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
settings.APPLICATION_MODE.set(modes.get(which));
updateModeButton(modes.get(which));
updateAllSettings();
}
});
singleSelectDialogBuilder.show();
}
});
}
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(this));
}
class SpinnerAdapter extends ArrayAdapter<String>{
public SpinnerAdapter(Context context, int resource, List<String> objects) {
super(context, resource, objects);
void updateModeButton(ApplicationMode mode) {
String title = Algorithms.isEmpty(mode.getUserProfileName())
? mode.toHumanString(SettingsBaseActivity.this)
: mode.getUserProfileName();
String subtitle = mode.getParent() == null
? String.format(getString(R.string.settings_routing_mode_string), Algorithms
.capitalizeFirstLetterAndLowercase(mode.getStringKey().replace("_", "")))
: String.format(getString(R.string.settings_derived_routing_mode_string), Algorithms
.capitalizeFirstLetterAndLowercase(mode.getParent().getStringKey()));
getModeTitleTV().setText(title);
getModeSubTitleTV().setText(subtitle);
getModeIconIV().setImageDrawable(getMyApplication().getUIUtilities().getIcon(mode.getSmallIconDark(),
getMyApplication().getSettings().isLightContent()
? R.color.active_buttons_and_links_light
: R.color.active_buttons_and_links_dark));
getDropDownArrow().setImageDrawable(getMyApplication().getUIUtilities().getIcon(R.drawable.ic_action_arrow_drop_down,
getMyApplication().getSettings().isLightContent()
? R.color.active_buttons_and_links_light
: R.color.active_buttons_and_links_dark));
settings.APPLICATION_MODE.set(mode);
updateAllSettings();
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View view = super.getDropDownView(position, convertView, parent);
if (!settings.isLightActionBar()){
TextView textView = (TextView) view.findViewById(android.R.id.text1);
textView.setBackgroundColor(getResources().getColor(R.color.actionbar_dark_color));
}
return view;
}
}
@Override
protected void onResume() {
super.onResume();
@ -421,6 +434,14 @@ public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity
profileDialog = null;
}
});
b.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
if (selected.size() == 0) {
setSelectedAppMode(ApplicationMode.CAR);
}
}
});
b.setTitle(R.string.profile_settings);
b.setView(v);
profileDialog = b.show();
@ -431,7 +452,7 @@ public abstract class SettingsBaseActivity extends ActionBarPreferenceActivity
boolean found = false;
for (ApplicationMode a : modes) {
if (am == a) {
getSpinner().setSelection(ind);
updateModeButton(a);
found = true;
break;
}

View file

@ -503,6 +503,7 @@ public class SettingsGeneralActivity extends SettingsBaseActivity implements OnR
misc.addPreference(createCheckBoxPreference(settings.DO_NOT_USE_ANIMATIONS, R.string.do_not_use_animations, R.string.do_not_use_animations_descr));
misc.addPreference(createCheckBoxPreference(settings.MAP_EMPTY_STATE_ALLOWED, R.string.tap_on_map_to_hide_interface, R.string.tap_on_map_to_hide_interface_descr));
misc.addPreference(createCheckBoxPreference(settings.DO_NOT_SHOW_STARTUP_MESSAGES, R.string.do_not_show_startup_messages, R.string.do_not_show_startup_messages_desc));
misc.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION,R.string.animate_my_location, R.string.animate_my_location_desc));
OsmandApplication app = getMyApplication();
if (Version.isGooglePlayEnabled(getMyApplication()) && !Version.isPaidVersion(app)) {
misc.addPreference(createCheckBoxPreference(settings.DO_NOT_SEND_ANONYMOUS_APP_USAGE, R.string.do_not_send_anonymous_app_usage, R.string.do_not_send_anonymous_app_usage_desc));

View file

@ -21,6 +21,7 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import net.osmand.PlatformUtil;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.ContextMenuAdapter;
import net.osmand.plus.ContextMenuItem;
@ -46,10 +47,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
public class SettingsNavigationActivity extends SettingsBaseActivity {
public static final String MORE_VALUE = "MORE_VALUE";
private Preference avoidRouting;
@ -75,7 +76,6 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
((OsmandApplication) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
getToolbar().setTitle(R.string.routing_settings);
createUI();
}
@ -84,7 +84,6 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
PreferenceScreen screen = getPreferenceScreen();
settings = getMyApplication().getSettings();
routerServicePreference = (ListPreference) screen.findPreference(settings.ROUTER_SERVICE.getId());
RouteService[] vls = RouteService.getAvailableRouters(getMyApplication());
String[] entries = new String[vls.length];
for(int i=0; i<entries.length; i++){
@ -245,7 +244,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
cat.addPreference(fastRoute);
} else {
ApplicationMode am = settings.getApplicationMode();
GeneralRouter router = getRouter(getMyApplication().getDefaultRoutingConfig(), am);
GeneralRouter router = getRouter(getMyApplication().getRoutingConfig(), am);
clearParameters();
if (router != null) {
Map<String, RoutingParameter> parameters = router.getParameters();
@ -355,7 +354,7 @@ public class SettingsNavigationActivity extends SettingsBaseActivity {
public static GeneralRouter getRouter(net.osmand.router.RoutingConfiguration.Builder builder, ApplicationMode am) {
GeneralRouter router = builder.getRouter(am.getStringKey());
GeneralRouter router = builder.getRouter(am.getRoutingProfile());
if(router == null && am.getParent() != null) {
router = builder.getRouter(am.getParent().getStringKey());
}

View file

@ -14,6 +14,7 @@ import android.preference.PreferenceScreen;
import android.support.v7.app.AlertDialog;
import android.view.View;
import net.osmand.PlatformUtil;
import net.osmand.StateChangedListener;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.OsmAndLocationSimulation;
@ -22,6 +23,7 @@ import net.osmand.plus.OsmandSettings;
import net.osmand.plus.R;
import net.osmand.plus.activities.SettingsBaseActivity;
import net.osmand.plus.activities.actions.AppModeDialog;
import net.osmand.plus.profiles.SettingsProfileFragment;
import net.osmand.util.SunriseSunset;
import java.text.SimpleDateFormat;
@ -42,26 +44,9 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
PreferenceScreen cat = getPreferenceScreen();
Preference pref;
// 2 should be in main settings
cat.addPreference(createCheckBoxPreference(settings.USE_OPENGL_RENDER,
R.string.use_opengl_render,R.string.use_opengl_render_descr));
cat.addPreference(createCheckBoxPreference(settings.ANIMATE_MY_LOCATION,
R.string.animate_my_location, R.string.animate_my_location_desc));
pref = new Preference(this);
pref.setTitle(R.string.app_modes_choose);
pref.setSummary(R.string.app_modes_choose_descr);
pref.setKey("available_application_modes");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
availableProfileDialog();
return true;
}
});
cat.addPreference(pref);
PreferenceCategory navigation = new PreferenceCategory(this);
navigation.setTitle(R.string.routing_settings);
cat.addPreference(navigation);
@ -225,30 +210,4 @@ public class SettingsDevelopmentActivity extends SettingsBaseActivity {
//pref.setEnabled(false);
info.addPreference(pref);
}
protected void availableProfileDialog() {
AlertDialog.Builder b = new AlertDialog.Builder(this);
final List<ApplicationMode> modes = ApplicationMode.allPossibleValues();
modes.remove(ApplicationMode.DEFAULT);
final Set<ApplicationMode> selected = new LinkedHashSet<ApplicationMode>(ApplicationMode.values(getMyApplication()));
selected.remove(ApplicationMode.DEFAULT);
View v = AppModeDialog.prepareAppModeView(this, modes, selected, null, false, true, false,
new View.OnClickListener() {
@Override
public void onClick(View v) {
StringBuilder vls = new StringBuilder(ApplicationMode.DEFAULT.getStringKey()+",");
for(ApplicationMode mode : modes) {
if(selected.contains(mode)) {
vls.append(mode.getStringKey()).append(",");
}
}
settings.AVAILABLE_APP_MODES.set(vls.toString());
}
});
b.setTitle(R.string.profile_settings);
b.setPositiveButton(R.string.shared_string_ok, null);
b.setView(v);
b.show();
}
}

View file

@ -149,7 +149,7 @@ public class AvoidSpecificRoads {
app.getSettings().removeImpassableRoad(latLon);
RouteDataObject obj = impassableRoads.remove(latLon);
if (obj != null) {
app.getDefaultRoutingConfig().removeImpassableRoad(obj);
app.getRoutingConfig().removeImpassableRoad(obj);
}
}
@ -254,7 +254,7 @@ public class AvoidSpecificRoads {
final LatLon oldLoc = getLocation(currentObject);
app.getSettings().moveImpassableRoad(oldLoc, newLoc);
impassableRoads.remove(oldLoc);
app.getDefaultRoutingConfig().removeImpassableRoad(currentObject);
app.getRoutingConfig().removeImpassableRoad(currentObject);
addImpassableRoadInternal(object, ll, showDialog, activity, newLoc);
if (callback != null) {
@ -276,7 +276,7 @@ public class AvoidSpecificRoads {
boolean showDialog,
@Nullable MapActivity activity,
@NonNull LatLon loc) {
if (app.getDefaultRoutingConfig().addImpassableRoad(object, ll)) {
if (app.getRoutingConfig().addImpassableRoad(object, ll)) {
impassableRoads.put(loc, object);
} else {
LatLon location = getLocation(object);
@ -305,7 +305,7 @@ public class AvoidSpecificRoads {
}
public LatLon getLocation(RouteDataObject object) {
Location location = app.getDefaultRoutingConfig().getImpassableRoadLocations().get(object.getId());
Location location = app.getRoutingConfig().getImpassableRoadLocations().get(object.getId());
return location == null ? null : new LatLon(location.getLatitude(), location.getLongitude());
}

View file

@ -32,7 +32,7 @@ import java.util.Map;
public class AMapPointMenuController extends MenuController {
private static final float NO_SPEED = -1;
private static final float NO_VALUE = -1;
private static final int NO_ICON = 0;
private AMapPoint point;
@ -136,19 +136,40 @@ public class AMapPointMenuController extends MenuController {
public CharSequence getAdditionalInfoStr() {
MapActivity activity = getMapActivity();
if (activity != null) {
float speed = getPointSpeed();
if (speed != NO_SPEED) {
String formatted = OsmAndFormatter.getFormattedSpeed(speed, activity.getMyApplication());
return activity.getString(R.string.map_widget_speed) + ": " + formatted;
float bearing = getPointBearing();
if (bearing != NO_VALUE) {
return OsmAndFormatter.getFormattedAzimuth(bearing, activity.getMyApplication());
}
}
return super.getAdditionalInfoStr();
}
@NonNull
@Override
public String getSubtypeStr() {
MapActivity activity = getMapActivity();
if (activity != null) {
float speed = getPointSpeed();
if (speed != NO_VALUE) {
return OsmAndFormatter.getFormattedSpeed(speed, activity.getMyApplication());
}
}
return super.getSubtypeStr();
}
@Nullable
@Override
public Drawable getSubtypeIcon() {
if (getPointSpeed() != NO_VALUE) {
return getIcon(R.drawable.ic_action_speed_16);
}
return super.getSubtypeIcon();
}
@Override
public int getAdditionalInfoIconRes() {
if (getPointSpeed() != NO_SPEED) {
return R.drawable.ic_action_speed_16;
if (getPointBearing() != NO_VALUE) {
return R.drawable.ic_action_bearing;
}
return super.getAdditionalInfoIconRes();
}
@ -214,10 +235,22 @@ public class AMapPointMenuController extends MenuController {
try {
return Float.parseFloat(speed);
} catch (NumberFormatException e) {
return NO_SPEED;
return NO_VALUE;
}
}
return NO_SPEED;
return NO_VALUE;
}
private float getPointBearing() {
String bearing = point.getParams().get(AMapPoint.POINT_BEARING_PARAM);
if (!TextUtils.isEmpty(bearing)) {
try {
return Float.parseFloat(bearing);
} catch (NumberFormatException e) {
return NO_VALUE;
}
}
return NO_VALUE;
}
@Nullable

View file

@ -0,0 +1,70 @@
package net.osmand.plus.profiles;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.view.MenuItemCompat;
import android.view.Menu;
import android.view.MenuItem;
import net.osmand.PlatformUtil;
import net.osmand.plus.R;
import net.osmand.plus.activities.OsmandActionBarActivity;
public class EditProfileActivity extends OsmandActionBarActivity {
public static final int DELETE_ID = 1010;
public static final String EDIT_PROFILE_FRAGMENT_TAG = "editProfileFragment";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getMyApplication().applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.single_fragment_layout);
if (savedInstanceState == null) {
EditProfileFragment editProfileFragment = new EditProfileFragment();
editProfileFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(android.R.id.content,
editProfileFragment, EDIT_PROFILE_FRAGMENT_TAG).commit();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem m = menu.add(0, DELETE_ID, 0, R.string.action_delete)
.setIcon(R.drawable.ic_action_delete_dark);
MenuItemCompat.setShowAsAction(m, MenuItem.SHOW_AS_ACTION_ALWAYS);
super.onCreateOptionsMenu(menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
case android.R.id.home:
finish();
return true;
case DELETE_ID:
((EditProfileFragment) getSupportFragmentManager().findFragmentByTag(
EDIT_PROFILE_FRAGMENT_TAG)).onDeleteProfileClick();
return true;
}
return false;
}
@Override
public void onBackPressed() {
final EditProfileFragment epf = (EditProfileFragment) getSupportFragmentManager()
.findFragmentByTag(EDIT_PROFILE_FRAGMENT_TAG);
if (epf.onBackPressedAllowed()) {
super.onBackPressed();
} else {
epf.confirmCancelDialog(this);
}
}
}

View file

@ -0,0 +1,790 @@
package net.osmand.plus.profiles;
import static net.osmand.plus.activities.SettingsNavigationActivity.INTENT_SKIP_DIALOG;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.DIALOG_TYPE;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SELECTED_KEY;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_APP_PROFILE;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_ICON;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_NAV_PROFILE;
import static net.osmand.plus.profiles.SettingsProfileFragment.IS_NEW_PROFILE;
import static net.osmand.plus.profiles.SettingsProfileFragment.IS_USER_PROFILE;
import static net.osmand.plus.profiles.SettingsProfileFragment.PROFILE_STRING_KEY;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AlertDialog.Builder;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import net.osmand.PlatformUtil;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.OsmandActionBarActivity;
import net.osmand.plus.activities.SettingsNavigationActivity;
import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SelectProfileListener;
import net.osmand.plus.routing.RouteProvider.RouteService;
import net.osmand.plus.widgets.OsmandTextFieldBoxes;
import net.osmand.router.GeneralRouter;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import studio.carbonylgroup.textfieldboxes.ExtendedEditText;
public class EditProfileFragment extends BaseOsmAndFragment {
private static final Log LOG = PlatformUtil.getLog(EditProfileFragment.class);
public static final String OPEN_CONFIG_ON_MAP = "openConfigOnMap";
public static final String MAP_CONFIG = "openMapConfigMenu";
public static final String NAV_CONFIG = "openNavConfigMenu";
public static final String SCREEN_CONFIG = "openScreenConfigMenu";
public static final String SELECTED_ITEM = "editedProfile";
public static final String SELECTED_ICON = "selectedIcon";
OsmandApplication app;
ApplicationMode mode = null;
ApplicationProfileObject profile = null;
List<RoutingProfileDataObject> routingProfileDataObjects;
RoutingProfileDataObject selectedRoutingProfileDataObject = null;
private boolean isNew = false;
private boolean isUserProfile = false;
private boolean isDataChanged = false;
private boolean isCancelAllowed = true;
private boolean isNightMode;
private SelectProfileListener navTypeListener = null;
private SelectProfileListener iconIdListener = null;
private SelectProfileListener baseTypeListener = null;
private ImageView profileIcon;
private LinearLayout profileIconBtn;
private ExtendedEditText profileNameEt;
private OsmandTextFieldBoxes profileNameTextBox;
private ExtendedEditText navTypeEt;
private OsmandTextFieldBoxes navTypeTextBox;
private FrameLayout selectNavTypeBtn;
private Button cancelBtn;
private Button saveButton;
private View mapConfigBtn;
private View screenConfigBtn;
private View navConfigBtn;
private LinearLayout buttonsLayout;
private FrameLayout clickBlockLayout;
private LinearLayout typeSelectionBtn;
private ImageView baseModeIcon;
private TextView baseModeTitle;
private ScrollView scrollContainer;
private LinearLayout buttonsLayoutSV;
private Button cancelBtnSV;
private Button saveButtonSV;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = getMyApplication();
if (getArguments() != null) {
String modeName = getArguments().getString(PROFILE_STRING_KEY, "car");
isNew = getArguments().getBoolean(IS_NEW_PROFILE, false);
isUserProfile = getArguments().getBoolean(IS_USER_PROFILE, false);
mode = ApplicationMode.valueOfStringKey(modeName, ApplicationMode.DEFAULT);
profile = new ApplicationProfileObject(mode, isNew, isUserProfile);
}
isNightMode = !app.getSettings().isLightContent();
routingProfileDataObjects = getRoutingProfiles(app);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_selected_profile, container, false);
profileIcon = view.findViewById(R.id.select_icon_btn_img);
profileIconBtn = view.findViewById(R.id.profile_icon_layout);
profileNameEt = view.findViewById(R.id.profile_name_et);
profileNameTextBox = view.findViewById(R.id.profile_name_otfb);
navTypeEt = view.findViewById(R.id.navigation_type_et);
navTypeTextBox = view.findViewById(R.id.navigation_type_otfb);
selectNavTypeBtn = view.findViewById(R.id.select_nav_type_btn);
cancelBtn = view.findViewById(R.id.cancel_button);
saveButton = view.findViewById(R.id.save_profile_btn);
mapConfigBtn = view.findViewById(R.id.map_config_btn);
screenConfigBtn = view.findViewById(R.id.screen_config_btn);
navConfigBtn = view.findViewById(R.id.nav_settings_btn);
buttonsLayout = view.findViewById(R.id.buttons_layout);
clickBlockLayout = view.findViewById(R.id.click_block_layout);
typeSelectionBtn = view.findViewById(R.id.type_selection_button);
baseModeIcon = view.findViewById(R.id.mode_icon);
baseModeTitle = view.findViewById(R.id.mode_title);
scrollContainer = view.findViewById(R.id.scroll_view_container);
buttonsLayoutSV = view.findViewById(R.id.buttons_layout_sv);
cancelBtnSV = view.findViewById(R.id.cancel_button_sv);
saveButtonSV = view.findViewById(R.id.save_profile_btn_sv);
profileNameEt.setFocusable(true);
profileNameEt.setSelectAllOnFocus(true);
profileIconBtn.setBackgroundResource(R.drawable.rounded_background_3dp);
GradientDrawable selectIconBtnBackground = (GradientDrawable) profileIconBtn
.getBackground();
if (isNightMode) {
profileNameTextBox
.setPrimaryColor(ContextCompat.getColor(app, R.color.color_dialog_buttons_dark));
navTypeTextBox
.setPrimaryColor(ContextCompat.getColor(app, R.color.color_dialog_buttons_dark));
selectIconBtnBackground
.setColor(app.getResources().getColor(R.color.text_field_box_dark));
} else {
selectIconBtnBackground
.setColor(app.getResources().getColor(R.color.text_field_box_light));
}
String title = "New Profile";
int startIconId = R.drawable.map_world_globe_dark;
if (isNew) {
isDataChanged = true;
startIconId = profile.parent.getSmallIconDark();
profile.iconId = startIconId;
} else if (isUserProfile) {
title = profile.userProfileTitle;
profileNameEt.setText(title);
startIconId = profile.iconId;
isDataChanged = false;
} else if (profile.key != -1) {
title = getResources().getString(profile.key);
profileNameEt.setText(profile.key);
startIconId = profile.iconId;
clickBlockLayout.setClickable(true);
}
profile.userProfileTitle = title;
if (profile.parent != null) {
setupBaseProfileView(profile.parent.getStringKey());
} else if (profile.key != -1) {
baseModeTitle.setText(profile.key);
baseModeIcon.setImageDrawable(
app.getUIUtilities().getIcon(profile.iconId, R.color.icon_color));
}
typeSelectionBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isUserProfile || isNew) {
final SelectProfileBottomSheetDialogFragment dialog = new SelectProfileBottomSheetDialogFragment();
Bundle bundle = new Bundle();
if (profile.parent != null) {
bundle.putString(SELECTED_KEY, profile.parent.getStringKey());
}
bundle.putString(DIALOG_TYPE, TYPE_APP_PROFILE);
dialog.setArguments(bundle);
if (getActivity() != null) {
getActivity().getSupportFragmentManager().beginTransaction()
.add(dialog, "select_base_type").commitAllowingStateLoss();
}
}
}
});
if (!Algorithms.isEmpty(mode.getRoutingProfile())) {
for (RoutingProfileDataObject r : routingProfileDataObjects) {
if (mode.getRoutingProfile().equals(r.getStringKey())) {
profile.routingProfileDataObject = r;
r.setSelected(true);
navTypeEt.setText(r.getName());
navTypeEt.clearFocus();
}
}
} else {
for (RoutingProfileDataObject rp : routingProfileDataObjects) {
if (profile.stringKey.equals(rp.getStringKey())) {
navTypeEt.setText(
RoutingProfilesResources.valueOf(rp.getStringKey().toUpperCase())
.getStringRes());
}
}
navTypeEt.clearFocus();
}
profileNameEt.clearFocus();
if (getActivity() != null
&& ((EditProfileActivity) getActivity()).getSupportActionBar() != null) {
((EditProfileActivity) getActivity()).getSupportActionBar().setTitle(title);
((EditProfileActivity) getActivity()).getSupportActionBar().setElevation(5.0f);
}
int iconColor;
if (!isUserProfile) {
iconColor = R.color.icon_color;
} else {
iconColor = isNightMode
? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light;
}
profileIcon.setImageDrawable(app.getUIUtilities().getIcon(startIconId, iconColor));
profileNameEt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (getActivity() instanceof OsmandActionBarActivity) {
ActionBar actionBar = ((OsmandActionBarActivity) getActivity())
.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(s.toString());
profile.userProfileTitle = s.toString();
isCancelAllowed = false;
}
}
}
});
selectNavTypeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isNew || isUserProfile) {
final SelectProfileBottomSheetDialogFragment fragment = new SelectProfileBottomSheetDialogFragment();
Bundle bundle = new Bundle();
if (profile.routingProfileDataObject != null) {
bundle.putString(SELECTED_KEY,
profile.routingProfileDataObject.getStringKey());
}
bundle.putString(DIALOG_TYPE, TYPE_NAV_PROFILE);
fragment.setArguments(bundle);
if (getActivity() != null) {
getActivity().getSupportFragmentManager().beginTransaction()
.add(fragment, "select_nav_type").commitAllowingStateLoss();
}
navTypeEt.setCursorVisible(false);
navTypeEt.setTextIsSelectable(false);
navTypeEt.clearFocus();
navTypeEt.requestFocus(ExtendedEditText.FOCUS_UP);
}
}
});
profileIconBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final SelectProfileBottomSheetDialogFragment iconSelectDialog = new SelectProfileBottomSheetDialogFragment();
Bundle bundle = new Bundle();
bundle.putString(DIALOG_TYPE, TYPE_ICON);
bundle.putInt(SELECTED_ICON, profile.iconId);
iconSelectDialog.setArguments(bundle);
if (getActivity() != null) {
getActivity().getSupportFragmentManager().beginTransaction()
.add(iconSelectDialog, "select_icon")
.commitAllowingStateLoss();
}
}
});
mapConfigBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isDataChanged) {
needSaveDialog();
} else if (getSettings() != null) {
activateMode(mode);
getSettings().APPLICATION_MODE.set(mode);
Intent i = new Intent(getActivity(), MapActivity.class);
i.putExtra(OPEN_CONFIG_ON_MAP, MAP_CONFIG);
i.putExtra(SELECTED_ITEM, profile.stringKey);
startActivity(i);
}
}
});
screenConfigBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isDataChanged) {
needSaveDialog();
} else if (getSettings() != null) {
activateMode(mode);
getSettings().APPLICATION_MODE.set(mode);
Intent i = new Intent(getActivity(), MapActivity.class);
i.putExtra(OPEN_CONFIG_ON_MAP, SCREEN_CONFIG);
i.putExtra(SELECTED_ITEM, profile.stringKey);
startActivity(i);
}
}
});
navConfigBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (isDataChanged) {
needSaveDialog();
} else if (getSettings() != null) {
activateMode(mode);
getSettings().APPLICATION_MODE.set(mode);
Intent i = new Intent(getActivity(), SettingsNavigationActivity.class);
i.putExtra(INTENT_SKIP_DIALOG, true);
i.putExtra(OPEN_CONFIG_ON_MAP, NAV_CONFIG);
i.putExtra(SELECTED_ITEM, profile.stringKey);
startActivity(i);
}
}
});
cancelBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (getActivity() != null) {
getActivity().onBackPressed();
}
}
});
cancelBtnSV.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (getActivity() != null) {
getActivity().onBackPressed();
}
}
});
saveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (saveNewProfile()) {
activateMode(mode);
getActivity().onBackPressed();
}
}
});
saveButtonSV.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (saveNewProfile()) {
activateMode(mode);
getActivity().onBackPressed();
}
}
});
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int marginShow = 66;
int marginHide = 0;
float d = getResources().getDisplayMetrics().density;
Rect r = new Rect();
view.getWindowVisibleDisplayFrame(r);
int screenHeight = view.getRootView().getHeight();
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15) {
buttonsLayout.setVisibility(View.GONE);
buttonsLayoutSV.setVisibility(View.VISIBLE);
setMargins(scrollContainer, 0, 0, 0, (int) (marginHide * d));
} else {
buttonsLayoutSV.setVisibility(View.GONE);
buttonsLayout.setVisibility(View.VISIBLE);
setMargins(scrollContainer, 0, 0, 0, (int) (marginShow * d));
}
}
});
return view;
}
@Override
public void onResume() {
getBaseProfileListener();
getNavProfileListener();
getIconListener();
super.onResume();
}
boolean onBackPressedAllowed() {
return isCancelAllowed;
}
SelectProfileListener getIconListener() {
if (iconIdListener == null) {
iconIdListener = new SelectProfileListener() {
@Override
public void onSelectedType(int pos) {
isDataChanged = true;
profile.iconId = pos;
profileIcon.setImageDrawable(app.getUIUtilities().getIcon(pos,
isNightMode ? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light));
}
};
}
return iconIdListener;
}
SelectProfileListener getBaseProfileListener() {
if (baseTypeListener == null) {
baseTypeListener = new SelectProfileListener() {
@Override
public void onSelectedType(int pos) {
String key = SettingsProfileFragment.getBaseProfiles(getMyApplication())
.get(pos).getStringKey();
setupBaseProfileView(key);
profile.parent = ApplicationMode.valueOfStringKey(key, ApplicationMode.DEFAULT);
}
};
}
return baseTypeListener;
}
SelectProfileListener getNavProfileListener() {
if (navTypeListener == null) {
navTypeListener = new SelectProfileListener() {
@Override
public void onSelectedType(int pos) {
updateRoutingProfile(pos);
}
};
}
return navTypeListener;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!isUserProfile && !isNew) {
profileNameEt.setFocusable(false);
navTypeEt.setFocusable(false);
}
if (isNew) {
profileNameEt.requestFocus();
} else {
scrollContainer.requestFocus();
}
}
void updateRoutingProfile(int pos) {
isDataChanged = true;
for (int i = 0; i < routingProfileDataObjects.size(); i++) {
if (i == pos) {
routingProfileDataObjects.get(i).setSelected(true);
} else {
routingProfileDataObjects.get(i).setSelected(false);
}
}
selectedRoutingProfileDataObject = routingProfileDataObjects.get(pos);
navTypeEt.setText(selectedRoutingProfileDataObject.getName());
profile.routingProfileDataObject = selectedRoutingProfileDataObject;
}
void activateMode(ApplicationMode mode) {
if (!ApplicationMode.values(app).contains(mode)) {
ApplicationMode.changeProfileStatus(mode, true, getMyApplication());
}
}
private void setupBaseProfileView(String stringKey) {
for (ApplicationMode am : ApplicationMode.getDefaultValues()) {
if (am.getStringKey().equals(stringKey)) {
baseModeIcon.setImageDrawable(
app.getUIUtilities().getIcon(am.getSmallIconDark(), R.color.icon_color));
baseModeTitle.setText(Algorithms.capitalizeFirstLetter(am.toHumanString(app)));
isDataChanged = false;
}
}
}
private void setMargins(View v, int l, int t, int r, int b) {
if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
p.setMargins(l, t, r, b);
v.requestLayout();
}
}
private boolean saveNewProfile() {
if (profile.routingProfileDataObject == null) {
showSaveWarningDialog(
getString(R.string.profile_alert_need_routing_type_title),
getString(R.string.profile_alert_need_routing_type_msg),
getActivity());
return false;
}
if (profile.userProfileTitle.isEmpty()
|| profile.userProfileTitle.replace(" ", "").length() < 1) {
showSaveWarningDialog(
getString(R.string.profile_alert_need_profile_name_title),
getString(R.string.profile_alert_need_profile_name_msg),
getActivity()
);
return false;
}
for (ApplicationMode m : ApplicationMode.allPossibleValues()) {
if (m.getUserProfileName() != null && getActivity() != null) {
if (m.getUserProfileName().equals(profile.userProfileTitle)) {
if (isNew || !Algorithms.isEmpty(mode.getUserProfileName())
&& !mode.getUserProfileName().equals(profile.userProfileTitle)) {
AlertDialog.Builder bld = new AlertDialog.Builder(getActivity());
bld.setTitle(R.string.profile_alert_duplicate_name_title);
bld.setMessage(R.string.profile_alert_duplicate_name_msg);
bld.setNegativeButton(R.string.shared_string_dismiss, null);
bld.show();
bld.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
profileNameEt.requestFocus();
}
});
return false;
}
}
}
}
if (isUserProfile && !isNew) {
ApplicationMode.deleteCustomMode(mode.getUserProfileName(), getMyApplication());
}
String customStringKey = profile.stringKey;
if (isNew && profile.parent != null) {
customStringKey =
profile.parent.getStringKey() + "_" + System.currentTimeMillis();
}
ApplicationMode.ApplicationModeBuilder builder = ApplicationMode
.createCustomMode(profile.userProfileTitle.trim(), customStringKey)
.parent(profile.parent)
.icon(profile.iconId, profile.iconId);
if (profile.routingProfileDataObject != null) {
builder.setRoutingProfile(profile.routingProfileDataObject.getStringKey());
}
ApplicationMode mode = builder.customReg();
ApplicationMode.saveCustomModeToSettings(getSettings());
if (!ApplicationMode.values(app).contains(mode)) {
boolean save = ApplicationMode.changeProfileStatus(mode, true, getMyApplication());
if (save && getSettings() != null) {
if (profile.routingProfileDataObject.getStringKey()
.equals(RoutingProfilesResources.STRAIGHT_LINE_MODE.toString())) {
getSettings().ROUTER_SERVICE.setModeValue(mode, RouteService.STRAIGHT);
} else if (profile.routingProfileDataObject.getStringKey()
.equals(RoutingProfilesResources.BROUTER_MODE.toString())) {
getSettings().ROUTER_SERVICE.setModeValue(mode, RouteService.BROUTER);
} else {
getSettings().ROUTER_SERVICE.setModeValue(mode, RouteService.OSMAND);
}
}
}
isDataChanged = false;
isCancelAllowed = true;
return true;
}
private void needSaveDialog() {
if (getActivity() != null) {
AlertDialog.Builder bld = new AlertDialog.Builder(getActivity());
bld.setTitle(R.string.profile_alert_need_save_title);
bld.setMessage(R.string.profile_alert_need_save_msg);
bld.setNegativeButton(R.string.shared_string_ok, null);
bld.show();
}
}
private void showSaveWarningDialog(String title, String message, Activity activity) {
AlertDialog.Builder bld = new AlertDialog.Builder(activity);
bld.setTitle(title);
bld.setMessage(message);
bld.setNegativeButton(R.string.shared_string_dismiss, null);
bld.show();
}
void confirmCancelDialog(final Activity activity) {
AlertDialog.Builder bld = new Builder(activity);
bld.setTitle(R.string.shared_string_dismiss);
bld.setMessage(R.string.exit_without_saving);
bld.setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isCancelAllowed = true;
activity.onBackPressed();
}
});
bld.setNegativeButton(R.string.shared_string_cancel, null);
bld.show();
}
void onDeleteProfileClick() {
if (getActivity() != null) {
if (isUserProfile) {
AlertDialog.Builder bld = new AlertDialog.Builder(getActivity());
bld.setTitle(R.string.profile_alert_delete_title);
bld.setMessage(String
.format(getString(R.string.profile_alert_delete_msg),
profile.userProfileTitle));
bld.setPositiveButton(R.string.shared_string_delete,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ApplicationMode
.deleteCustomMode(profile.userProfileTitle, getMyApplication());
if (getActivity() != null) {
getActivity().onBackPressed();
}
getSettings().APPLICATION_MODE.set(ApplicationMode.DEFAULT);
}
});
bld.setNegativeButton(R.string.shared_string_dismiss, null);
bld.show();
} else {
Toast.makeText(getActivity(), R.string.profile_alert_cant_delete_base,
Toast.LENGTH_SHORT).show();
}
}
}
static List<RoutingProfileDataObject> getRoutingProfiles(OsmandApplication context) {
List<RoutingProfileDataObject> profilesObjects = new ArrayList<>();
profilesObjects.add(new RoutingProfileDataObject(
RoutingProfilesResources.STRAIGHT_LINE_MODE.toString(),
context.getString(RoutingProfilesResources.STRAIGHT_LINE_MODE.getStringRes()),
context.getString(R.string.special_routing_type),
RoutingProfilesResources.STRAIGHT_LINE_MODE.getIconRes(),
false, null));
if (context.getBRouterService() != null) {
profilesObjects.add(new RoutingProfileDataObject(
RoutingProfilesResources.BROUTER_MODE.toString(),
context.getString(RoutingProfilesResources.BROUTER_MODE.getStringRes()),
context.getString(R.string.third_party_routing_type),
RoutingProfilesResources.BROUTER_MODE.getIconRes(),
false, null));
}
Map<String, GeneralRouter> inputProfiles = context.getRoutingConfig().getAllRouters();
for (Entry<String, GeneralRouter> e : inputProfiles.entrySet()) {
int iconRes = R.drawable.ic_action_gdirections_dark;
String name = e.getValue().getProfileName();
String description = context.getString(R.string.osmand_default_routing);
if (!Algorithms.isEmpty(e.getValue().getFilename())) {
description = e.getValue().getFilename();
} else if (RoutingProfilesResources.isRpValue(name.toUpperCase())){
iconRes = RoutingProfilesResources.valueOf(name.toUpperCase()).getIconRes();
name = context
.getString(RoutingProfilesResources.valueOf(name.toUpperCase()).getStringRes());
}
profilesObjects.add(new RoutingProfileDataObject(e.getKey(), name, description,
iconRes, false, e.getValue().getFilename()));
}
return profilesObjects;
}
public enum RoutingProfilesResources {
STRAIGHT_LINE_MODE(R.string.routing_profile_straightline,R.drawable.ic_action_split_interval),
BROUTER_MODE(R.string.routing_profile_broutrer, R.drawable.ic_action_split_interval),
CAR(R.string.rendering_value_car_name, R.drawable.ic_action_car_dark),
PEDESTRIAN(R.string.rendering_value_pedestrian_name, R.drawable.map_action_pedestrian_dark),
BICYCLE(R.string.rendering_value_bicycle_name, R.drawable.map_action_bicycle_dark),
SKI(R.string.routing_profile_ski, R.drawable.ic_plugin_skimaps),
PUBLIC_TRANSPORT(R.string.app_mode_public_transport, R.drawable.map_action_bus_dark),
BOAT(R.string.app_mode_boat, R.drawable.map_action_sail_boat_dark),
GEOCODING(R.string.routing_profile_geocoding, R.drawable.ic_action_world_globe);
int stringRes;
int iconRes;
RoutingProfilesResources(int stringRes, int iconRes) {
this.stringRes = stringRes;
this.iconRes = iconRes;
}
public int getStringRes() {
return stringRes;
}
public int getIconRes() {
return iconRes;
}
private static final List<String> rpValues = new ArrayList<>();
static {
for (RoutingProfilesResources rpr : RoutingProfilesResources.values()) {
rpValues.add(rpr.name());
}
}
public static boolean isRpValue(String value) {
return rpValues.contains(value);
}
}
private class ApplicationProfileObject {
int key = -1;
String stringKey;
String userProfileTitle = "";
ApplicationMode parent = null;
int iconId = R.drawable.map_world_globe_dark;
RoutingProfileDataObject routingProfileDataObject = null;
ApplicationProfileObject(ApplicationMode mode, boolean isNew, boolean isUserProfile) {
if (isNew) {
stringKey = mode.getStringKey() + System.currentTimeMillis();
parent = mode;
} else if (isUserProfile) {
stringKey = mode.getStringKey();
parent = mode.getParent();
iconId = mode.getSmallIconDark();
userProfileTitle = mode.getUserProfileName();
} else {
key = mode.getStringResource();
stringKey = mode.getStringKey();
iconId = mode.getSmallIconDark();
}
}
}
}

View file

@ -0,0 +1,42 @@
package net.osmand.plus.profiles;
public class ProfileDataObject {
private String name;
private String description;
private int iconRes;
private String stringKey;
private boolean isSelected;
public ProfileDataObject(String name, String description, String stringKey, int iconRes, boolean isSelected) {
this.name = name;
this.iconRes = iconRes;
this.description = description;
this.isSelected = isSelected;
this.stringKey = stringKey;
}
public String getName() {
return name;
}
public int getIconRes() {
return iconRes;
}
public String getDescription() {
return description;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
public String getStringKey() {
return stringKey;
}
}

View file

@ -0,0 +1,138 @@
package net.osmand.plus.profiles;
import android.support.annotation.ColorRes;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SwitchCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.List;
import java.util.Set;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.profiles.ProfileMenuAdapter.ProfileViewHolder;
import net.osmand.util.Algorithms;
public class ProfileMenuAdapter extends RecyclerView.Adapter<ProfileViewHolder> {
private List<ApplicationMode> items;
private Set<ApplicationMode> selectedItems;
private ProfileListener listener;
private final OsmandApplication app;
@ColorRes private int selectedIconColorRes;
public ProfileMenuAdapter(List<ApplicationMode> items, Set<ApplicationMode> selectedItems, OsmandApplication app, ProfileListener listener) {
this.items = items;
this.listener = listener;
this.app = app;
this.selectedItems = selectedItems;
selectedIconColorRes = isNightMode(app)
? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light;
}
public List<ApplicationMode> getItems() {
return items;
}
public void addItem(ApplicationMode profileItem) {
items.add(profileItem);
notifyDataSetChanged();
}
public void updateItemsList(List<ApplicationMode> newList, Set<ApplicationMode> selectedItems) {
items.clear();
this.selectedItems.clear();
items.addAll(newList);
this.selectedItems.addAll(selectedItems);
notifyDataSetChanged();
}
@NonNull
@Override
public ProfileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.profile_list_item, parent, false);
return new ProfileViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull final ProfileViewHolder holder, int position) {
final ApplicationMode item = items.get(position);
if (item.getParent() != null) {
holder.title.setText(item.getUserProfileName());
holder.descr.setText(String.format(app.getString(R.string.profile_type_descr_string),
Algorithms.capitalizeFirstLetterAndLowercase(item.getParent().getStringKey().replace("_", " "))));
} else {
holder.title.setText(app.getResources().getString(item.getStringResource()));
holder.descr.setText(R.string.profile_type_base_string);
}
holder.title.setTextColor(app.getResources().getColor(isNightMode(app)
? R.color.main_font_dark
: R.color.main_font_light));
if (selectedItems.contains(item)) {
holder.aSwitch.setChecked(true);
holder.icon.setImageDrawable(app.getUIUtilities().getIcon(item.getSmallIconDark(), selectedIconColorRes));
} else {
holder.aSwitch.setChecked(false);
holder.icon.setImageDrawable(app.getUIUtilities().getIcon(item.getSmallIconDark(), R.color.icon_color));
}
holder.aSwitch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.changeProfileStatus(item, holder.aSwitch.isChecked());
if (selectedItems.contains(item)) {
holder.icon.setImageDrawable(app.getUIUtilities().getIcon(item.getSmallIconDark(), selectedIconColorRes));
} else {
holder.icon.setImageDrawable(app.getUIUtilities().getIcon(item.getSmallIconDark(), R.color.icon_color));
}
}
});
holder.profileOptions.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.editProfile(item);
}
});
}
@Override
public int getItemCount() {
return items.size();
}
private static boolean isNightMode(OsmandApplication ctx) {
return !ctx.getSettings().isLightContent();
}
class ProfileViewHolder extends RecyclerView.ViewHolder {
TextView title, descr;
SwitchCompat aSwitch;
ImageView icon;
LinearLayout profileOptions;
ProfileViewHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.profile_title);
descr = itemView.findViewById(R.id.profile_descr);
aSwitch = itemView.findViewById(R.id.profile_switch);
icon = itemView.findViewById(R.id.profile_icon);
profileOptions = itemView.findViewById(R.id.profile_settings);
}
}
public interface ProfileListener {
void changeProfileStatus(ApplicationMode item, boolean isSelected);
void editProfile(ApplicationMode item);
}
}

View file

@ -0,0 +1,19 @@
package net.osmand.plus.profiles;
import android.os.Parcel;
public class RoutingProfileDataObject extends ProfileDataObject {
private String fileName;
public RoutingProfileDataObject(String stringKey, String name, String descr, int iconRes, boolean isSelected, String fileName) {
super(name, descr, stringKey, iconRes, isSelected);
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
}

View file

@ -0,0 +1,240 @@
package net.osmand.plus.profiles;
import static net.osmand.plus.profiles.EditProfileFragment.SELECTED_ICON;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import java.util.ArrayList;
import java.util.List;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import org.apache.commons.logging.Log;
public class SelectProfileBottomSheetDialogFragment extends MenuBottomSheetDialogFragment {
private static final Log LOG = PlatformUtil
.getLog(SelectProfileBottomSheetDialogFragment.class);
public static final String TAG = "SelectProfileBottomSheetDialogFragment";
public final static String DIALOG_TYPE = "dialog_type";
public final static String TYPE_APP_PROFILE = "base_profiles";
public final static String TYPE_NAV_PROFILE = "routing_profiles";
public final static String TYPE_ICON = "icon_type";
public final static String SELECTED_KEY = "selected_base";
String type;
private SelectProfileListener listener;
private List<ProfileDataObject> profiles = new ArrayList<>();
private List<IconResWithDescr> icons;
private String selectedItemKey;
private int selectedIconRes;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OsmandApplication app = getMyApplication();
Bundle args = getArguments();
if (args != null && args.get(DIALOG_TYPE) != null) {
type = args.getString(DIALOG_TYPE);
selectedItemKey = args.getString(SELECTED_KEY, null);
if (type.equals(TYPE_NAV_PROFILE)) {
profiles.addAll(EditProfileFragment.getRoutingProfiles(app));
} else if (type.equals(TYPE_APP_PROFILE)) {
profiles.addAll(SettingsProfileFragment.getBaseProfiles(app));
} else if (type.equals(TYPE_ICON)) {
selectedIconRes = args.getInt(SELECTED_ICON, -1);
icons = getProfileIcons();
} else {
LOG.error("Check intent data!");
dismiss();
}
}
}
@Override
public void createMenuItems(Bundle savedInstanceState) {
if (type.equals(TYPE_APP_PROFILE)) {
items.add(new TitleItem(getString(R.string.select_base_profile_dialog_title)));
items.add(new LongDescriptionItem(getString(R.string.select_base_profile_dialog_message)));
for (int i = 0; i < profiles.size(); i++) {
final int pos = i;
final ProfileDataObject profile = profiles.get(i);
final boolean isSelected = profile.getStringKey().equals(selectedItemKey);
final Drawable drawableIcon;
if (isSelected) {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(profile.getIconRes(), nightMode
? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light);
} else {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(profile.getIconRes(), R.color.icon_color);
}
items.add(new BottomSheetItemWithCompoundButton.Builder()
.setChecked(isSelected)
.setButtonTintList(isSelected
? ColorStateList.valueOf(getResolvedColor(getActiveColorId()))
: null)
.setDescription(profile.getDescription())
.setTitle(profile.getName())
.setIcon(drawableIcon)
.setLayoutId(R.layout.bottom_sheet_item_with_descr_and_radio_btn)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener == null) {
getListener();
}
if (listener != null) {
listener.onSelectedType(pos);
}
dismiss();
}
})
.create());
}
} else if (type.equals(TYPE_NAV_PROFILE)){
items.add(new TitleItem(getString(R.string.select_nav_profile_dialog_title)));
for (int i = 0; i < profiles.size(); i++) {
final int pos = i;
final ProfileDataObject profile = profiles.get(i);
final boolean isSelected = profile.getStringKey().equals(selectedItemKey);
final Drawable drawableIcon;
if (isSelected) {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(profile.getIconRes(), nightMode
? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light);
} else {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(profile.getIconRes(), R.color.icon_color);
}
items.add(new BottomSheetItemWithCompoundButton.Builder()
.setChecked(isSelected)
.setButtonTintList(isSelected
? ColorStateList.valueOf(getResolvedColor(getActiveColorId()))
: null)
.setDescription(profile.getDescription())
.setTitle(profile.getName())
.setIcon(drawableIcon)
.setLayoutId(R.layout.bottom_sheet_item_with_descr_and_radio_btn)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener == null) {
getListener();
}
if (listener != null) {
listener.onSelectedType(pos);
}
dismiss();
}
})
.create());
}
} else if (type.equals(TYPE_ICON)) {
items.add(new TitleItem(getString(R.string.select_icon_profile_dialog_title)));
for (final IconResWithDescr icon : icons) {
Drawable drawableIcon;
boolean isSelected = icon.resId == selectedIconRes;
if (isSelected) {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(icon.resId, nightMode
? R.color.active_buttons_and_links_dark
: R.color.active_buttons_and_links_light);
} else {
drawableIcon = getMyApplication().getUIUtilities()
.getIcon(icon.resId, R.color.icon_color);
}
items.add(new BottomSheetItemWithCompoundButton.Builder()
.setChecked(icon.resId == selectedIconRes)
.setButtonTintList(isSelected
? ColorStateList.valueOf(getResolvedColor(getActiveColorId()))
: null)
.setTitle(getMyApplication().getString(icon.titleId))
.setIcon(drawableIcon)
.setLayoutId(R.layout.bottom_sheet_item_with_radio_btn)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(listener == null) {
getListener();
}
if (listener != null) {
listener.onSelectedType(icon.resId);
}
dismiss();
}
})
.create()
);
}
}
}
private void getListener() {
if (getActivity() != null && getActivity() instanceof EditProfileActivity) {
EditProfileFragment f = (EditProfileFragment) getActivity().getSupportFragmentManager()
.findFragmentByTag(EditProfileActivity.EDIT_PROFILE_FRAGMENT_TAG);
if (type.equals(TYPE_APP_PROFILE)) {
listener = f.getBaseProfileListener();
} else if (type.equals(TYPE_NAV_PROFILE)) {
listener = f.getNavProfileListener();
} else if (type.equals(TYPE_ICON)) {
listener = f.getIconListener();
}
} else if (getActivity() != null && getActivity() instanceof SettingsProfileActivity) {
SettingsProfileFragment f = (SettingsProfileFragment) getActivity().getSupportFragmentManager()
.findFragmentByTag(SettingsProfileActivity.SETTINGS_PROFILE_FRAGMENT_TAG);
listener = f.getBaseProfileListener();
}
}
private List<IconResWithDescr> getProfileIcons() {
List<IconResWithDescr> icons = new ArrayList<>();
icons.add(new IconResWithDescr(R.drawable.ic_action_car_dark, R.string.rendering_value_car_name,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_bicycle_dark, R.string.rendering_value_bicycle_name,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_pedestrian_dark, R.string.rendering_value_pedestrian_name,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_bus_dark, R.string.app_mode_bus,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_sail_boat_dark, R.string.app_mode_boat,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_aircraft, R.string.app_mode_aircraft,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_truck_dark, R.string.app_mode_truck,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_motorcycle_dark, R.string.app_mode_motorcycle,false));
icons.add(new IconResWithDescr(R.drawable.ic_action_trekking_dark, R.string.app_mode_hiking,false));
icons.add(new IconResWithDescr(R.drawable.ic_plugin_skimaps, R.string.routing_profile_ski, false));
return icons;
}
interface SelectProfileListener {
void onSelectedType(int pos);
}
private class IconResWithDescr {
private int resId;
private int titleId;
private boolean isSelected;
public IconResWithDescr(int resId, int titleId, boolean isSelected) {
this.resId = resId;
this.titleId = titleId;
this.isSelected = isSelected;
}
}
}

View file

@ -0,0 +1,43 @@
package net.osmand.plus.profiles;
import android.os.Bundle;
import android.view.MenuItem;
import net.osmand.plus.R;
import net.osmand.plus.activities.OsmandActionBarActivity;
public class SettingsProfileActivity extends OsmandActionBarActivity {
public static final String SETTINGS_PROFILE_FRAGMENT_TAG = "settingsProfileFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
getMyApplication().applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.single_fragment_layout);
getSupportActionBar().setElevation(5.0f);
if (savedInstanceState == null) {
SettingsProfileFragment profileFragment = new SettingsProfileFragment();
profileFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, profileFragment, SETTINGS_PROFILE_FRAGMENT_TAG).commit();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
case android.R.id.home:
finish();
return true;
}
return false;
}
@Override
protected void onResume() {
super.onResume();
}
}

View file

@ -0,0 +1,176 @@
package net.osmand.plus.profiles;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.DIALOG_TYPE;
import static net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.TYPE_APP_PROFILE;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.osmand.PlatformUtil;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.R;
import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.profiles.ProfileMenuAdapter.ProfileListener;
import net.osmand.plus.profiles.SelectProfileBottomSheetDialogFragment.SelectProfileListener;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
public class SettingsProfileFragment extends BaseOsmAndFragment {
private static final Log LOG = PlatformUtil.getLog(SettingsProfileFragment.class);
public static final String PROFILE_STRING_KEY = "string_key";
public static final String IS_NEW_PROFILE = "new_profile";
public static final String IS_USER_PROFILE = "user_profile";
private ProfileMenuAdapter adapter;
private RecyclerView recyclerView;
private LinearLayout addNewProfileBtn;
ProfileListener listener = null;
SelectProfileListener typeListener = null;
private List<ApplicationMode> allAppModes;
private Set<ApplicationMode> availableAppModes;
private List<ProfileDataObject> baseProfiles;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
allAppModes = ApplicationMode.allPossibleValues();
allAppModes.remove(ApplicationMode.DEFAULT);
availableAppModes = new LinkedHashSet<>(ApplicationMode.values(getMyApplication()));
availableAppModes.remove(ApplicationMode.DEFAULT);
baseProfiles = getBaseProfiles(getMyActivity());
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
listener = new ProfileListener() {
@Override
public void changeProfileStatus(ApplicationMode am, boolean isSelected) {
if(isSelected) {
availableAppModes.add(am);
} else {
availableAppModes.remove(am);
}
ApplicationMode.changeProfileStatus(am, isSelected, getMyApplication());
}
@Override
public void editProfile(ApplicationMode item) {
Intent intent = new Intent(getActivity(), EditProfileActivity.class);
intent.putExtra(PROFILE_STRING_KEY, item.getStringKey());
if (!Algorithms.isEmpty(item.getUserProfileName())) {
intent.putExtra(IS_USER_PROFILE, true);
}
startActivity(intent);
}
};
View view = inflater.inflate(R.layout.profiles_list_fragment, container, false);
recyclerView = view.findViewById(R.id.profiles_list);
addNewProfileBtn = view.findViewById(R.id.add_profile_btn);
addNewProfileBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final SelectProfileBottomSheetDialogFragment dialog = new SelectProfileBottomSheetDialogFragment();
Bundle bundle = new Bundle();
bundle.putString(DIALOG_TYPE, TYPE_APP_PROFILE);
dialog.setArguments(bundle);
if (getActivity() != null) {
getActivity().getSupportFragmentManager().beginTransaction()
.add(dialog, "select_base_type").commitAllowingStateLoss();
}
}
});
adapter = new ProfileMenuAdapter(allAppModes, availableAppModes, getMyApplication(), listener);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
return view;
}
@Override
public void onResume() {
super.onResume();
getBaseProfileListener();
allAppModes = ApplicationMode.allPossibleValues();
allAppModes.remove(ApplicationMode.DEFAULT);
adapter.updateItemsList(allAppModes, new LinkedHashSet<>(ApplicationMode.values(getMyApplication())));
}
SelectProfileListener getBaseProfileListener() {
if (typeListener == null) {
typeListener = new SelectProfileListener() {
@Override
public void onSelectedType(int pos) {
Intent intent = new Intent(getActivity(), EditProfileActivity.class);
intent.putExtra(IS_NEW_PROFILE, true);
intent.putExtra(IS_USER_PROFILE, true);
intent.putExtra(PROFILE_STRING_KEY, baseProfiles.get(pos).getStringKey());
startActivity(intent);
}
};
}
return typeListener;
}
static List<ProfileDataObject> getBaseProfiles(Context ctx) {
List<ProfileDataObject> profiles = new ArrayList<>();
for (ApplicationMode mode : ApplicationMode.getDefaultValues()) {
if (mode != ApplicationMode.DEFAULT) {
profiles.add(new ProfileDataObject( mode.toHumanString(ctx),
ctx.getString(BaseProfilesDescr.valueOf(mode.getStringKey().toUpperCase()).getDescrRes()),
mode.getStringKey(), mode.getSmallIconDark(), false));
}
}
return profiles;
}
public enum BaseProfilesDescr {
CAR(R.string.base_profile_descr_car),
BICYCLE(R.string.base_profile_descr_bicycle),
PEDESTRIAN(R.string.base_profile_descr_pedestrian),
PUBLIC_TRANSPORT(R.string.base_profile_descr_public_transport),
BOAT(R.string.base_profile_descr_boat),
AIRCRAFT(R.string.base_profile_descr_aircraft);
private int descrRes;
BaseProfilesDescr(int descrRes) {
this.descrRes = descrRes;
}
public int getDescrRes() {
return descrRes;
}
}
}

View file

@ -245,6 +245,11 @@ public class ResourceManager {
float tiles = (dm.widthPixels / 256 + 2) * (dm.heightPixels / 256 + 2) * 3;
log.info("Bitmap tiles to load in memory : " + tiles);
bitmapTilesCache.setMaxCacheSize((int) (tiles));
File path = context.getAppPath(IndexConstants.ROUTING_PROFILES_DIR);
if (!path.exists()) {
path.mkdir();
}
}
public BitmapTilesCache getBitmapTilesCache() {

View file

@ -1,11 +1,14 @@
package net.osmand.plus.routepreparationmenu;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
@ -27,6 +30,7 @@ import net.osmand.data.PointDescription;
import net.osmand.plus.FavouritesDbHelper;
import net.osmand.plus.MapMarkersHelper;
import net.osmand.plus.MapMarkersHelper.MapMarker;
import net.osmand.plus.OsmAndLocationProvider;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.TargetPointsHelper;
@ -213,14 +217,17 @@ public class AddPointBottomSheetDialog extends MenuBottomSheetDialogFragment {
private void createMyLocItem() {
BaseBottomSheetItem myLocationItem = new SimpleBottomSheetItem.Builder()
.setIcon(getIcon(R.drawable.ic_action_location_color, 0))
.setIcon(getIcon(OsmAndLocationProvider.isLocationPermissionAvailable(getActivity())
? R.drawable.ic_action_location_color : R.drawable.ic_action_location_color_lost, 0))
.setTitle(getString(R.string.shared_string_my_location))
.setLayoutId(R.layout.bottom_sheet_item_simple_56dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
OsmandApplication app = getMyApplication();
Activity activity = getActivity();
if (app != null) {
if (OsmAndLocationProvider.isLocationPermissionAvailable(app)) {
TargetPointsHelper targetPointsHelper = app.getTargetPointsHelper();
Location myLocation = app.getLocationProvider().getLastKnownLocation();
if (myLocation != null) {
@ -250,6 +257,11 @@ public class AddPointBottomSheetDialog extends MenuBottomSheetDialogFragment {
break;
}
}
} else if (activity != null) {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
OsmAndLocationProvider.REQUEST_LOCATION_PERMISSION);
}
}
dismiss();
}

View file

@ -1640,9 +1640,14 @@ public class MapRouteInfoMenu implements IRouteInformationListener, CardListener
public void updateFromIcon(View parentView) {
MapActivity mapActivity = getMapActivity();
int locationIconResByStatus = OsmAndLocationProvider.isLocationPermissionAvailable(mapActivity)
? R.drawable.ic_action_location_color : R.drawable.ic_action_location_color_lost;
if (mapActivity != null) {
((ImageView) parentView.findViewById(R.id.fromIcon)).setImageDrawable(ContextCompat.getDrawable(mapActivity,
mapActivity.getMyApplication().getTargetPointsHelper().getPointToStart() == null ? R.drawable.ic_action_location_color : R.drawable.list_startpoint));
mapActivity.getMyApplication().getTargetPointsHelper().getPointToStart() == null
? locationIconResByStatus : R.drawable.list_startpoint));
}
}

View file

@ -72,11 +72,7 @@ public class RoutingOptionsHelper {
addRouteMenuAppModes(ApplicationMode.PEDESTRIAN, PermanentAppModeOptions.PEDESTRIAN.routingParameters);
addRouteMenuAppModes(ApplicationMode.PUBLIC_TRANSPORT, PermanentAppModeOptions.PUBLIC_TRANSPORT.routingParameters);
addRouteMenuAppModes(ApplicationMode.BOAT, PermanentAppModeOptions.BOAT.routingParameters);
addRouteMenuAppModes(ApplicationMode.AIRCRAFT, PermanentAppModeOptions.AIRCAFT.routingParameters);
addRouteMenuAppModes(ApplicationMode.HIKING, PermanentAppModeOptions.HIKING.routingParameters);
addRouteMenuAppModes(ApplicationMode.MOTORCYCLE, PermanentAppModeOptions.MOTORCYCLE.routingParameters);
addRouteMenuAppModes(ApplicationMode.TRUCK, PermanentAppModeOptions.TRUCK.routingParameters);
addRouteMenuAppModes(ApplicationMode.TRAIN, PermanentAppModeOptions.TRAIN.routingParameters);
addRouteMenuAppModes(ApplicationMode.AIRCRAFT, PermanentAppModeOptions.AIRCRAFT.routingParameters);
}
private void addRouteMenuAppModes(ApplicationMode am, List<String> routingParameters) {
@ -382,7 +378,7 @@ public class RoutingOptionsHelper {
public LocalRoutingParameter getRoutingParameterInnerById(ApplicationMode am, String parameterId) {
RouteProvider.GPXRouteParamsBuilder rparams = app.getRoutingHelper().getCurrentGPXRoute();
GeneralRouter rm = getRouter(app.getDefaultRoutingConfig(), am);
GeneralRouter rm = getRouter(app.getRoutingConfig(), am);
if (rm == null || (rparams != null && !rparams.isCalculateOsmAndRoute()) && !rparams.getFile().hasRtePt()) {
return null;
}
@ -407,7 +403,6 @@ public class RoutingOptionsHelper {
}
return rpg;
}
return rp;
}
@ -457,7 +452,7 @@ public class RoutingOptionsHelper {
public List<LocalRoutingParameter> getRoutingParametersInner(ApplicationMode am) {
RouteProvider.GPXRouteParamsBuilder rparams = app.getRoutingHelper().getCurrentGPXRoute();
List<LocalRoutingParameter> list = new ArrayList<LocalRoutingParameter>(getGpxAndOsmandRouterParameters(am));
GeneralRouter rm = SettingsNavigationActivity.getRouter(app.getDefaultRoutingConfig(), am);
GeneralRouter rm = SettingsNavigationActivity.getRouter(app.getRoutingConfig(), am);
if (rm == null || (rparams != null && !rparams.isCalculateOsmAndRoute()) && !rparams.getFile().hasRtePt()) {
return list;
}
@ -547,7 +542,7 @@ public class RoutingOptionsHelper {
public List<GeneralRouter.RoutingParameter> getAvoidRoutingPrefsForAppMode(ApplicationMode applicationMode) {
List<GeneralRouter.RoutingParameter> avoidParameters = new ArrayList<GeneralRouter.RoutingParameter>();
GeneralRouter router = getRouter(app.getDefaultRoutingConfig(), applicationMode);
GeneralRouter router = getRouter(app.getRoutingConfig(), applicationMode);
if (router != null) {
for (Map.Entry<String, GeneralRouter.RoutingParameter> e : router.getParameters().entrySet()) {
String param = e.getKey();
@ -561,7 +556,7 @@ public class RoutingOptionsHelper {
}
public GeneralRouter.RoutingParameter getRoutingPrefsForAppModeById(ApplicationMode applicationMode, String parameterId) {
GeneralRouter router = getRouter(app.getDefaultRoutingConfig(), applicationMode);
GeneralRouter router = getRouter(app.getRoutingConfig(), applicationMode);
GeneralRouter.RoutingParameter parameter = null;
if (router != null) {
@ -971,11 +966,7 @@ public class RoutingOptionsHelper {
PEDESTRIAN(MuteSoundRoutingParameter.KEY, GeneralRouter.USE_HEIGHT_OBSTACLES),
PUBLIC_TRANSPORT(MuteSoundRoutingParameter.KEY, AvoidPTTypesRoutingParameter.KEY),
BOAT(MuteSoundRoutingParameter.KEY),
AIRCAFT(MuteSoundRoutingParameter.KEY),
HIKING(MuteSoundRoutingParameter.KEY),
MOTORCYCLE(MuteSoundRoutingParameter.KEY),
TRUCK(MuteSoundRoutingParameter.KEY),
TRAIN(MuteSoundRoutingParameter.KEY);
AIRCRAFT(MuteSoundRoutingParameter.KEY);
List<String> routingParameters;

View file

@ -595,7 +595,7 @@ public class RouteProvider {
OsmandSettings settings = params.ctx.getSettings();
router.setUseFastRecalculation(settings.USE_FAST_RECALCULATION.get());
RoutingConfiguration.Builder config = params.ctx.getDefaultRoutingConfig();
RoutingConfiguration.Builder config = params.ctx.getRoutingConfig();
GeneralRouter generalRouter = SettingsNavigationActivity.getRouter(config, params.mode);
if(generalRouter == null) {
return applicationModeNotSupported(params);
@ -680,19 +680,6 @@ public class RouteProvider {
private RoutingConfiguration initOsmAndRoutingConfig(Builder config, final RouteCalculationParams params, OsmandSettings settings,
GeneralRouter generalRouter) throws IOException, FileNotFoundException {
GeneralRouterProfile p ;
if (params.mode.isDerivedRoutingFrom(ApplicationMode.BICYCLE)) {
p = GeneralRouterProfile.BICYCLE;
} else if (params.mode.isDerivedRoutingFrom(ApplicationMode.PEDESTRIAN)) {
p = GeneralRouterProfile.PEDESTRIAN;
} else if(params.mode.isDerivedRoutingFrom(ApplicationMode.CAR)){
p = GeneralRouterProfile.CAR;
} else if (params.mode.isDerivedRoutingFrom(ApplicationMode.BOAT)) {
p = GeneralRouterProfile.BOAT;
} else {
return null;
}
Map<String, String> paramsR = new LinkedHashMap<String, String>();
for(Map.Entry<String, RoutingParameter> e : generalRouter.getParameters().entrySet()){
String key = e.getKey();
@ -720,8 +707,7 @@ public class RouteProvider {
// make visible
int memoryLimit = (int) (0.95 * ((rt.maxMemory() - rt.totalMemory()) + rt.freeMemory()) / mb);
log.warn("Use " + memoryLimit + " MB Free " + rt.freeMemory() / mb + " of " + rt.totalMemory() / mb + " max " + rt.maxMemory() / mb);
RoutingConfiguration cf = config.build(p.name().toLowerCase(), params.start.hasBearing() ?
RoutingConfiguration cf = config.build( params.mode.getRoutingProfile(), params.start.hasBearing() ?
params.start.getBearing() / 180d * Math.PI : null,
memoryLimit, paramsR);
return cf;

View file

@ -443,11 +443,11 @@ public class TransportRoutingHelper {
}
private List<TransportRouteResult> calculateRouteImpl(TransportRouteCalculationParams params) throws IOException, InterruptedException {
RoutingConfiguration.Builder config = params.ctx.getDefaultRoutingConfig();
RoutingConfiguration.Builder config = params.ctx.getRoutingConfig();
BinaryMapIndexReader[] files = params.ctx.getResourceManager().getTransportRoutingMapFiles();
params.params.clear();
OsmandSettings settings = params.ctx.getSettings();
for(Map.Entry<String, GeneralRouter.RoutingParameter> e : config.getRouter(params.mode.getStringKey()).getParameters().entrySet()){
for(Map.Entry<String, GeneralRouter.RoutingParameter> e : config.getRouter(params.mode.getRoutingProfile()).getParameters().entrySet()){
String key = e.getKey();
GeneralRouter.RoutingParameter pr = e.getValue();
String vl;

View file

@ -18,6 +18,7 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu.TrackChartPoints;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.CompassRulerControlWidgetState;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopCoordinatesView;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopTextView;
import net.osmand.plus.views.mapwidgets.MapInfoWidgetsFactory.TopToolbarController;
@ -200,7 +201,7 @@ public class MapInfoLayer extends OsmandMapLayer {
TextInfoWidget battery = ric.createBatteryControl(map);
registerSideWidget(battery, R.drawable.ic_action_battery, R.string.map_widget_battery, "battery", false, 42);
TextInfoWidget ruler = mic.createRulerControl(map);
registerSideWidget(ruler, R.drawable.ic_action_ruler_circle, R.string.map_widget_ruler_control, "ruler", false, 43);
registerSideWidget(ruler, new CompassRulerControlWidgetState(app), "ruler", false, 43);
}
public void recreateControls() {

View file

@ -3,17 +3,23 @@ package net.osmand.plus.views;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.view.MotionEvent;
import android.view.View;
import net.osmand.AndroidUtils;
import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.data.QuadPoint;
@ -36,6 +42,7 @@ public class RulerControlLayer extends OsmandMapLayer {
private static final long DELAY_BEFORE_DRAW = 500;
private static final int TEXT_SIZE = 14;
private static final int DISTANCE_TEXT_SIZE = 16;
private static final int COMPASS_CIRCLE_ID = 2;
private final MapActivity mapActivity;
private OsmandApplication app;
@ -74,11 +81,26 @@ public class RulerControlLayer extends OsmandMapLayer {
private Bitmap centerIconDay;
private Bitmap centerIconNight;
private Paint bitmapPaint;
private Paint triangleHeadingPaint;
private Paint triangleNorthPaint;
private Paint redLinesPaint;
private Paint blueLinesPaint;
private RenderingLineAttributes lineAttrs;
private RenderingLineAttributes lineFontAttrs;
private RenderingLineAttributes circleAttrs;
private RenderingLineAttributes circleAttrsAlt;
private Path compass = new Path();
private Path arrow = new Path();
private Path arrowArc = new Path();
private Path redCompassLines = new Path();
private double[] degrees = new double[72];
private int[] arcColors = {Color.parseColor("#00237BFF"), Color.parseColor("#237BFF"), Color.parseColor("#00237BFF")};
private float cachedHeading = 0;
private Handler handler;
public RulerControlLayer(MapActivity mapActivity) {
@ -118,6 +140,14 @@ public class RulerControlLayer extends OsmandMapLayer {
bitmapPaint.setDither(true);
bitmapPaint.setFilterBitmap(true);
int colorNorthArrow = ContextCompat.getColor(app, R.color.compass_control_active);
int colorHeadingArrow = ContextCompat.getColor(app, R.color.active_buttons_and_links_light);
triangleNorthPaint = initPaintWithStyle(Style.FILL, colorNorthArrow);
triangleHeadingPaint = initPaintWithStyle(Style.FILL, colorHeadingArrow);
redLinesPaint = initPaintWithStyle(Style.STROKE, colorNorthArrow);
blueLinesPaint = initPaintWithStyle(Style.STROKE, colorHeadingArrow);
lineAttrs = new RenderingLineAttributes("rulerLine");
float circleTextSize = TEXT_SIZE * mapActivity.getResources().getDisplayMetrics().density;
@ -141,6 +171,18 @@ public class RulerControlLayer extends OsmandMapLayer {
view.refreshMap();
}
};
for (int i = 0; i < 72; i++) {
degrees[i] = Math.toRadians(i * 5);
}
}
private Paint initPaintWithStyle(Paint.Style style, int color) {
Paint paint = new Paint();
paint.setStyle(style);
paint.setColor(color);
paint.setAntiAlias(true);
return paint;
}
@Override
@ -190,6 +232,7 @@ public class RulerControlLayer extends OsmandMapLayer {
circleAttrsAlt.paint2.setStyle(Style.FILL);
final QuadPoint center = tb.getCenterPixelPoint();
final RulerMode mode = app.getSettings().RULER_MODE.get();
boolean showCompass = app.getSettings().SHOW_COMPASS_CONTROL_RULER.get();
final long currentTime = System.currentTimeMillis();
if (cacheMultiTouchEndTime != view.getMultiTouchEndTime()) {
@ -226,19 +269,41 @@ public class RulerControlLayer extends OsmandMapLayer {
}
if (mode == RulerMode.FIRST || mode == RulerMode.SECOND) {
updateData(tb, center);
if (showCompass) {
updateHeading();
resetDrawingPaths();
}
RenderingLineAttributes attrs = mode == RulerMode.FIRST ? circleAttrs : circleAttrsAlt;
for (int i = 1; i <= cacheDistances.size(); i++) {
if (showCompass && i == COMPASS_CIRCLE_ID) {
drawCompassCircle(canvas, tb, center, attrs);
} else {
drawCircle(canvas, tb, i, center, attrs);
}
}
}
}
}
public boolean rulerModeOn() {
return mapActivity.getMapLayers().getMapWidgetRegistry().isVisible("ruler") &&
rightWidgetsPanel.getVisibility() == View.VISIBLE;
}
private void updateHeading() {
Float heading = mapActivity.getMapViewTrackingUtilities().getHeading();
if (heading != null && heading != cachedHeading) {
cachedHeading = heading;
}
}
private void resetDrawingPaths() {
redCompassLines.reset();
arrowArc.reset();
compass.reset();
arrow.reset();
}
private void refreshMapDelayed() {
handler.sendEmptyMessageDelayed(0, DRAW_TIME + 50);
}
@ -393,8 +458,27 @@ public class RulerControlLayer extends OsmandMapLayer {
private void drawCircle(Canvas canvas, RotatedTileBox tb, int circleNumber, QuadPoint center,
RenderingLineAttributes attrs) {
if (!tb.isZoomAnimated()) {
Rect bounds = new Rect();
float circleRadius = radius * circleNumber;
String text = cacheDistances.get(circleNumber - 1);
float[] textCoords = calculateDistanceTextCoords(text, circleRadius, center, attrs);
canvas.rotate(-tb.getRotate(), center.x, center.y);
canvas.drawCircle(center.x, center.y, radius * circleNumber, attrs.shadowPaint);
canvas.drawCircle(center.x, center.y, radius * circleNumber, attrs.paint);
drawTextCoords(canvas, text, textCoords, attrs);
canvas.rotate(tb.getRotate(), center.x, center.y);
}
}
private void drawTextCoords(Canvas canvas, String text, float[] textCoords, RenderingLineAttributes attrs) {
canvas.drawText(text, textCoords[0], textCoords[1], attrs.paint3);
canvas.drawText(text, textCoords[0], textCoords[1], attrs.paint2);
canvas.drawText(text, textCoords[2], textCoords[3], attrs.paint3);
canvas.drawText(text, textCoords[2], textCoords[3], attrs.paint2);
}
private float[] calculateDistanceTextCoords(String text, float drawingTextRadius, QuadPoint center, RenderingLineAttributes attrs) {
Rect bounds = new Rect();
attrs.paint2.getTextBounds(text, 0, text.length(), bounds);
// coords of left or top text
@ -406,25 +490,164 @@ public class RulerControlLayer extends OsmandMapLayer {
if (textSide == TextSide.VERTICAL) {
x1 = center.x - bounds.width() / 2;
y1 = center.y - radius * circleNumber + bounds.height() / 2;
y1 = center.y - drawingTextRadius + bounds.height() / 2;
x2 = center.x - bounds.width() / 2;
y2 = center.y + radius * circleNumber + bounds.height() / 2;
y2 = center.y + drawingTextRadius + bounds.height() / 2;
} else if (textSide == TextSide.HORIZONTAL) {
x1 = center.x - radius * circleNumber - bounds.width() / 2;
x1 = center.x - drawingTextRadius - bounds.width() / 2;
y1 = center.y + bounds.height() / 2;
x2 = center.x + radius * circleNumber - bounds.width() / 2;
x2 = center.x + drawingTextRadius - bounds.width() / 2;
y2 = center.y + bounds.height() / 2;
}
canvas.rotate(-tb.getRotate(), center.x, center.y);
canvas.drawCircle(center.x, center.y, radius * circleNumber, attrs.shadowPaint);
canvas.drawCircle(center.x, center.y, radius * circleNumber, attrs.paint);
canvas.drawText(text, x1, y1, attrs.paint3);
canvas.drawText(text, x1, y1, attrs.paint2);
canvas.drawText(text, x2, y2, attrs.paint3);
canvas.drawText(text, x2, y2, attrs.paint2);
canvas.rotate(tb.getRotate(), center.x, center.y);
return new float[]{x1, y1, x2, y2};
}
private void drawCompassCircle(Canvas canvas, RotatedTileBox tileBox, QuadPoint center,
RenderingLineAttributes attrs) {
if (!tileBox.isZoomAnimated()) {
float radiusLength = radius * COMPASS_CIRCLE_ID;
float innerRadiusLength = radiusLength - attrs.paint.getStrokeWidth() / 2;
updateArcShader(radiusLength, center);
updateCompassPaths(center, innerRadiusLength, radiusLength);
drawCardinalDirections(canvas, center, radiusLength, tileBox, attrs);
redLinesPaint.setStrokeWidth(attrs.paint.getStrokeWidth());
blueLinesPaint.setStrokeWidth(attrs.paint.getStrokeWidth());
canvas.drawPath(compass, attrs.shadowPaint);
canvas.drawPath(compass, attrs.paint);
canvas.drawPath(redCompassLines, redLinesPaint);
canvas.rotate(cachedHeading, center.x, center.y);
canvas.drawPath(arrowArc, blueLinesPaint);
canvas.rotate(-cachedHeading, center.x, center.y);
canvas.drawPath(arrow, attrs.shadowPaint);
canvas.drawPath(arrow, triangleNorthPaint);
canvas.rotate(cachedHeading, center.x, center.y);
canvas.drawPath(arrow, attrs.shadowPaint);
canvas.drawPath(arrow, triangleHeadingPaint);
canvas.rotate(-cachedHeading, center.x, center.y);
String text = cacheDistances.get(COMPASS_CIRCLE_ID - 1);
float[] textCoords = calculateDistanceTextCoords(text, radiusLength + AndroidUtils.dpToPx(app, 16), center, attrs);
canvas.rotate(-tileBox.getRotate(), center.x, center.y);
drawTextCoords(canvas, text, textCoords, attrs);
canvas.rotate(tileBox.getRotate(), center.x, center.y);
}
}
private void updateCompassPaths(QuadPoint center, float innerRadiusLength, float radiusLength) {
compass.addCircle(center.x, center.y, radiusLength, Path.Direction.CCW);
arrowArc.addArc(new RectF(center.x - radiusLength, center.y - radiusLength, center.x + radiusLength, center.y + radiusLength), -45, -90);
for (int i = 0; i < degrees.length; i++) {
double degree = degrees[i];
float x = (float) Math.cos(degree);
float y = -(float) Math.sin(degree);
float lineStartX = center.x + x * innerRadiusLength;
float lineStartY = center.y + y * innerRadiusLength;
float lineLength = getCompassLineHeight(i);
float lineStopX = center.x + x * (innerRadiusLength - lineLength);
float lineStopY = center.y + y * (innerRadiusLength - lineLength);
if (i == 18) {
float shortLineMargin = AndroidUtils.dpToPx(app, 5.66f);
float shortLineHeight = AndroidUtils.dpToPx(app, 2.94f);
float startY = center.y + y * (radiusLength - shortLineMargin);
float stopY = center.y + y * (radiusLength - shortLineMargin - shortLineHeight);
compass.moveTo(center.x, startY);
compass.lineTo(center.x, stopY);
float firstPointY = center.y + y * (radiusLength + AndroidUtils.dpToPx(app, 5));
float secondPointX = center.x - AndroidUtils.dpToPx(app, 4);
float secondPointY = center.y + y * (radiusLength - AndroidUtils.dpToPx(app, 2));
float thirdPointX = center.x + AndroidUtils.dpToPx(app, 4);
float thirdPointY = center.y + y * (radiusLength - AndroidUtils.dpToPx(app, 2));
arrow.moveTo(center.x, firstPointY);
arrow.lineTo(secondPointX, secondPointY);
arrow.lineTo(thirdPointX, thirdPointY);
arrow.lineTo(center.x, firstPointY);
arrow.close();
} else {
compass.moveTo(lineStartX, lineStartY);
compass.lineTo(lineStopX, lineStopY);
}
if (i % 9 == 0 && i % 6 != 0) {
redCompassLines.moveTo(lineStartX, lineStartY);
redCompassLines.lineTo(lineStopX, lineStopY);
}
}
}
private float getCompassLineHeight(int index) {
if (index % 6 == 0) {
return AndroidUtils.dpToPx(app, 8);
} else if (index % 9 == 0 || index % 2 != 0) {
return AndroidUtils.dpToPx(app, 3);
} else {
return AndroidUtils.dpToPx(app, 6);
}
}
private void drawCardinalDirections(Canvas canvas, QuadPoint center, float radiusLength, RotatedTileBox tileBox, RenderingLineAttributes attrs) {
float textMargin = AndroidUtils.dpToPx(app, 14);
attrs.paint2.setTextAlign(Paint.Align.CENTER);
attrs.paint3.setTextAlign(Paint.Align.CENTER);
for (int i = 0; i < degrees.length; i += 9) {
String cardinalDirection = getCardinalDirection(i);
if (cardinalDirection != null) {
float textWidth = AndroidUtils.getTextWidth(attrs.paint2.getTextSize(), cardinalDirection);
canvas.save();
canvas.translate(center.x, center.y);
canvas.rotate(i * 5 + 90);
canvas.translate(0, radiusLength - textMargin - textWidth / 2);
canvas.rotate(-i * 5 - tileBox.getRotate() - 90);
canvas.drawText(cardinalDirection, 0, 0, attrs.paint3);
canvas.drawText(cardinalDirection, 0, 0, attrs.paint2);
canvas.restore();
}
}
}
private void updateArcShader(float radiusLength, QuadPoint center) {
float arcLength = (float) (2 * Math.PI * radiusLength * (90f / 360));
LinearGradient shader = new LinearGradient((float) (center.x - arcLength * 0.25), center.y, (float) (center.x + arcLength * 0.25), center.y, arcColors, null, Shader.TileMode.CLAMP);
blueLinesPaint.setShader(shader);
}
private String getCardinalDirection(int i) {
if (i == 0) {
return "E";
} else if (i == 9) {
return "NE";
} else if (i == 18) {
return "N";
} else if (i == 27) {
return "NW";
} else if (i == 36) {
return "W";
} else if (i == 45) {
return "SW";
} else if (i == 54) {
return "S";
} else if (i == 63) {
return "SE";
}
return null;
}
private enum TextSide {

View file

@ -56,6 +56,7 @@ import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.views.RulerControlLayer;
import net.osmand.plus.views.mapwidgets.MapWidgetRegistry.WidgetState;
import net.osmand.plus.views.mapwidgets.NextTurnInfoWidget.TurnDrawable;
import net.osmand.router.TurnType;
import net.osmand.util.Algorithms;
@ -138,6 +139,54 @@ public class MapInfoWidgetsFactory {
return gpsInfoControl;
}
public static class CompassRulerControlWidgetState extends WidgetState {
public static final int COMPASS_CONTROL_WIDGET_STATE_SHOW = R.id.compass_ruler_control_widget_state_show;
public static final int COMPASS_CONTROL_WIDGET_STATE_HIDE = R.id.compass_ruler_control_widget_state_hide;
private final OsmandSettings.OsmandPreference<Boolean> showCompass;
public CompassRulerControlWidgetState(OsmandApplication ctx) {
super(ctx);
showCompass = ctx.getSettings().SHOW_COMPASS_CONTROL_RULER;
}
@Override
public int getMenuTitleId() {
return R.string.map_widget_ruler_control;
}
@Override
public int getMenuIconId() {
return R.drawable.ic_action_ruler_circle;
}
@Override
public int getMenuItemId() {
return showCompass.get() ? COMPASS_CONTROL_WIDGET_STATE_SHOW : COMPASS_CONTROL_WIDGET_STATE_HIDE;
}
@Override
public int[] getMenuTitleIds() {
return new int[]{R.string.show_compass_ruler, R.string.hide_compass_ruler};
}
@Override
public int[] getMenuIconIds() {
return new int[]{R.drawable.ic_action_compass_widget, R.drawable.ic_action_compass_widget_hide};
}
@Override
public int[] getMenuItemIds() {
return new int[]{COMPASS_CONTROL_WIDGET_STATE_SHOW, COMPASS_CONTROL_WIDGET_STATE_HIDE};
}
@Override
public void changeState(int stateId) {
showCompass.set(stateId == COMPASS_CONTROL_WIDGET_STATE_SHOW);
}
}
public TextInfoWidget createRulerControl(final MapActivity map) {
final String title = "";
final TextInfoWidget rulerControl = new TextInfoWidget(map) {

View file

@ -66,7 +66,7 @@ Functionality
* Add POIs and directly upload them to OSM (or later if offline)
* Optional trip recording also in background mode (while device is in sleep mode)
OsmAnd is open source and actively being developed. Everyone can contribute to the application by reporting bugs, [improving translations](https://hosted.weblate.org/projects/osmand/), or coding new features. The project is in a lively state of continuous improvement by all these forms of developer and user interaction. The project progress also relies on financial contributions to fund the development, coding, and testing of new functionality. By buying OsmAnd+ and subscribing to OsmAnd Live you help the application to be even more awesome! It is also possible to make a general donation, see http://osmand.net/help-online#buy_app.
OsmAnd is open source and actively being developed. Everyone can contribute to the application by reporting bugs, [improving translations](https://hosted.weblate.org/projects/osmand/), or coding new features. The project is in a lively selected of continuous improvement by all these forms of developer and user interaction. The project progress also relies on financial contributions to fund the development, coding, and testing of new functionality. By buying OsmAnd+ and subscribing to OsmAnd Live you help the application to be even more awesome! It is also possible to make a general donation, see http://osmand.net/help-online#buy_app.
#### Approximate map coverage and quality: