[android] add support for editing charging stations details

While here, uncluttered a little the charging station editor,
removing fields like the POI address which is basically irrelevant for
charging stations.

Signed-off-by: Séverin Lemaignan <severin@guakamole.org>
This commit is contained in:
Séverin Lemaignan
2025-09-29 17:53:11 +02:00
committed by x7z4w
parent 509ff4de72
commit d168855ef5
19 changed files with 899 additions and 197 deletions

View File

@@ -1,13 +1,19 @@
package app.organicmaps.editor; package app.organicmaps.editor;
import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.InputType; import android.text.InputType;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.GridLayout;
import android.widget.Toast;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes; import androidx.annotation.IdRes;
@@ -22,6 +28,7 @@ import app.organicmaps.base.BaseMwmFragment;
import app.organicmaps.dialog.EditTextDialogFragment; import app.organicmaps.dialog.EditTextDialogFragment;
import app.organicmaps.editor.data.TimeFormatUtils; import app.organicmaps.editor.data.TimeFormatUtils;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.ChargeSocketDescriptor;
import app.organicmaps.sdk.bookmarks.data.Metadata; import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.sdk.editor.Editor; import app.organicmaps.sdk.editor.Editor;
import app.organicmaps.sdk.editor.OpeningHours; import app.organicmaps.sdk.editor.OpeningHours;
@@ -30,26 +37,33 @@ import app.organicmaps.sdk.editor.data.LocalizedStreet;
import app.organicmaps.sdk.editor.data.Timetable; import app.organicmaps.sdk.editor.data.Timetable;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
import app.organicmaps.sdk.util.Utils; import app.organicmaps.sdk.util.Utils;
import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.util.Graphics; import app.organicmaps.util.Graphics;
import app.organicmaps.util.InputUtils; import app.organicmaps.util.InputUtils;
import app.organicmaps.util.UiUtils; import app.organicmaps.util.UiUtils;
import com.google.android.material.button.MaterialButton; import com.google.android.material.button.MaterialButton;
import com.google.android.material.card.MaterialCardView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.imageview.ShapeableImageView; import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout; import com.google.android.material.textfield.TextInputLayout;
import com.google.android.material.textview.MaterialTextView; import com.google.android.material.textview.MaterialTextView;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class EditorFragment extends BaseMwmFragment implements View.OnClickListener public class EditorFragment extends BaseMwmFragment implements View.OnClickListener
{ {
final static String LAST_INDEX_OF_NAMES_ARRAY = "LastIndexOfNamesArray"; final static String LAST_INDEX_OF_NAMES_ARRAY = "LastIndexOfNamesArray";
private static final String CHARGE_SOCKETS_TAG = "CHARGE_SOCKETS_TAG";
private MaterialTextView mCategory; private MaterialTextView mCategory;
private View mCardName; private View mCardName;
private View mCardAddress; private View mCardAddress;
private View mCardChargingStation;
private View mCardDetails; private View mCardDetails;
private View mCardSocialMedia; private View mCardSocialMedia;
private View mCardBuilding; private View mCardBuilding;
@@ -130,6 +144,8 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
private TextInputLayout mInputHouseNumber; private TextInputLayout mInputHouseNumber;
private TextInputLayout mInputBuildingLevels; private TextInputLayout mInputBuildingLevels;
private View mChargeSockets;
private View mEmptyOpeningHours; private View mEmptyOpeningHours;
private MaterialTextView mOpeningHours; private MaterialTextView mOpeningHours;
private View mEditOpeningHours; private View mEditOpeningHours;
@@ -206,6 +222,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mWifi.setChecked(Editor.nativeHasWifi()); mWifi.setChecked(Editor.nativeHasWifi());
// TODO Reimplement this to avoid https://github.com/organicmaps/organicmaps/issues/9049 // TODO Reimplement this to avoid https://github.com/organicmaps/organicmaps/issues/9049
// mOutdoorSeating.setChecked(Editor.nativeGetSwitchInput(Metadata.MetadataType.FMD_OUTDOOR_SEATING.toInt(),"yes")); // mOutdoorSeating.setChecked(Editor.nativeGetSwitchInput(Metadata.MetadataType.FMD_OUTDOOR_SEATING.toInt(),"yes"));
refreshChargeSockets();
refreshOpeningTime(); refreshOpeningTime();
refreshEditableFields(); refreshEditableFields();
refreshResetButton(); refreshResetButton();
@@ -329,6 +346,14 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
final int[] editableDetails = Editor.nativeGetEditableProperties(); final int[] editableDetails = Editor.nativeGetEditableProperties();
// charge sockets have their own card; check whether we should display it.
boolean hasChargeSockets = false;
for (int type : editableDetails)
{
hasChargeSockets = hasChargeSockets || (type == Metadata.MetadataType.FMD_CHARGE_SOCKETS.toInt());
}
UiUtils.showIf(hasChargeSockets, mCardChargingStation);
setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails); setCardVisibility(mCardDetails, mDetailsBlocks, editableDetails);
setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails); setCardVisibility(mCardSocialMedia, mSocialMediaBlocks, editableDetails);
} }
@@ -351,6 +376,283 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
UiUtils.showIf(anyBlockElement, card); UiUtils.showIf(anyBlockElement, card);
} }
/**
* Builds a dialog for editing or adding a charge socket.
*
* @param socketIndex The index of the socket to edit, or -1 to add a new socket.
* @param type The current type of the socket (e.g., "type2", "type2_combo").
* @param count The current number of sockets of this type or 0 for new socket.
* @param power The current power output of the socket in kW or 0 for new socket.
* @return A MaterialAlertDialogBuilder instance for the configured dialog.
*/
private MaterialAlertDialogBuilder buildChargeSocketDialog(int socketIndex, String type, int count, double power)
{
LayoutInflater inflater = LayoutInflater.from(getActivity());
View dialogView = inflater.inflate(R.layout.dialog_edit_socket, null);
GridLayout typeBtns = dialogView.findViewById(R.id.edit_socket_type_grid);
typeBtns.removeAllViews();
List<String> SOCKET_TYPES = Arrays.stream(getResources().getStringArray(R.array.charge_socket_types)).toList();
for (String socket : SOCKET_TYPES)
{
MaterialButton btn = (MaterialButton) inflater.inflate(R.layout.button_socket_type, typeBtns, false);
btn.setTag(R.id.socket_type, socket);
// load SVG icon converted into VectorDrawable in res/drawable
@SuppressLint("DiscouragedApi")
int resIconId =
getResources().getIdentifier("ic_charge_socket_" + socket, "drawable", requireContext().getPackageName());
if (resIconId != 0)
{
btn.setIcon(getResources().getDrawable(resIconId));
}
@SuppressLint("DiscouragedApi")
int resTypeId =
getResources().getIdentifier("charge_socket_" + socket, "string", requireContext().getPackageName());
if (resTypeId != 0)
{
btn.setText(getResources().getString(resTypeId));
}
if (socket.equals(type))
{
btn.setChecked(true);
}
typeBtns.addView(btn);
}
// manage the grid of socket type buttons as a single 'radio group'
// (this can not be done with a MaterialButtonToggleGroup because it does
// not support GridLayout)
List<MaterialButton> buttonList = new ArrayList<>();
for (int i = 0; i < typeBtns.getChildCount(); i++)
{
View child = typeBtns.getChildAt(i);
if (child instanceof MaterialButton button)
{
buttonList.add(button);
button.setOnClickListener(view -> {
// deselect all
for (MaterialButton b : buttonList)
{
b.setChecked(false);
}
// select clicked
button.setChecked(true);
});
}
}
TextInputLayout countInputLayout = dialogView.findViewById(R.id.edit_socket_count_layout);
AutoCompleteTextView countView = dialogView.findViewById(R.id.edit_socket_count);
if (count > 0)
{
countView.setText(String.valueOf(count));
}
// Add a TextWatcher to validate on text change
countView.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) {
validatePositiveField(s.toString(), countInputLayout);
}
});
TextInputLayout powerInputLayout = dialogView.findViewById(R.id.edit_socket_power_layout);
AutoCompleteTextView powerView = dialogView.findViewById(R.id.edit_socket_power);
if (power > 0)
{
powerView.setText(String.valueOf(power));
}
// Add a TextWatcher to validate on text change
powerView.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) {
validatePositiveField(s.toString(), powerInputLayout);
}
});
return new MaterialAlertDialogBuilder(requireActivity(), R.style.MwmTheme_AlertDialog)
.setTitle(R.string.editor_socket)
.setView(dialogView)
.setPositiveButton(R.string.save,
(dialog, which) -> {
String socketType = "";
for (MaterialButton b : buttonList)
{
if (b.isChecked())
{
socketType = b.getTag(R.id.socket_type).toString();
break;
}
}
int countValue = 0; // 0 means 'unknown count'
try
{
countValue = Integer.parseInt(countView.getText().toString());
}
catch (NumberFormatException ignored)
{
Logger.w(CHARGE_SOCKETS_TAG, "Invalid count value for socket:" + countView.getText().toString());
}
if (countValue < 0)
{
countValue = 0;
Logger.w(CHARGE_SOCKETS_TAG, "Invalid count value for socket:" + countView.getText().toString());
}
double powerValue = 0; // 0 means 'unknown power'
try
{
powerValue = Double.parseDouble(powerView.getText().toString());
}
catch (NumberFormatException ignored)
{
Logger.w(CHARGE_SOCKETS_TAG, "Invalid power value for socket:" + powerView.getText().toString());
}
if (powerValue < 0)
{
powerValue = 0;
Logger.w(CHARGE_SOCKETS_TAG, "Invalid power value for socket:" + powerView.getText().toString());
}
ChargeSocketDescriptor socket =
new ChargeSocketDescriptor(socketType, countValue, powerValue);
updateChargeSockets(socketIndex, socket);
})
.setNegativeButton(R.string.cancel, (dialog, which) -> { dialog.dismiss(); });
}
// Helper method for validation logic
private boolean validatePositiveField(String text, TextInputLayout layout) {
if (text.isEmpty()) {
layout.setError(null); // No error if empty (assuming 0 is the default)
return true;
}
try {
double value = Double.parseDouble(text);
if (value < 0) {
layout.setError(getString(R.string.error_value_must_be_positive));
return false;
} else {
layout.setError(null);
return true;
}
} catch (NumberFormatException e) {
layout.setError(getString(R.string.error_invalid_number));
return false;
}
}
/**
* Updates the list of charge sockets.
* If socketIndex is >=0, it updates the socket at that index.
* Otherwise, it adds the new socket to the list.
*
* @param socketIndex The index of the socket to update, or -1 to add a new socket.
* @param socket The ChargeSocketDescriptor of the socket to add or update.
*/
private void updateChargeSockets(int socketIndex, ChargeSocketDescriptor socket)
{
ChargeSocketDescriptor[] sockets = Editor.nativeGetChargeSockets();
if (socketIndex >= 0)
{
sockets[socketIndex] = socket;
}
else {
List<ChargeSocketDescriptor> list = new ArrayList<>(Arrays.asList(sockets));
list.add(socket);
sockets = list.toArray(new ChargeSocketDescriptor[0]);
}
Editor.nativeSetChargeSockets(sockets);
refreshChargeSockets();
}
private void refreshChargeSockets()
{
ChargeSocketDescriptor[] sockets = Editor.nativeGetChargeSockets();
LayoutInflater inflater = LayoutInflater.from(requireContext());
GridLayout socketsGrid = mChargeSockets.findViewById(R.id.socket_grid_editor);
socketsGrid.removeAllViews();
for (int i = 0; i < sockets.length; i++)
{
final int currentIndex = i;
ChargeSocketDescriptor socket = sockets[i];
View itemView = inflater.inflate(R.layout.item_charge_socket, socketsGrid, false);
MaterialTextView type = itemView.findViewById(R.id.socket_type);
ShapeableImageView icon = itemView.findViewById(R.id.socket_icon);
MaterialTextView power = itemView.findViewById(R.id.socket_power);
MaterialTextView count = itemView.findViewById(R.id.socket_count);
// load SVG icon converted into VectorDrawable in res/drawable
@SuppressLint("DiscouragedApi")
int resIconId = getResources().getIdentifier("ic_charge_socket_" + socket.type(), "drawable",
requireContext().getPackageName());
if (resIconId != 0)
{
icon.setImageResource(resIconId);
}
@SuppressLint("DiscouragedApi")
int resTypeId =
getResources().getIdentifier("charge_socket_" + socket.type(), "string", requireContext().getPackageName());
if (resTypeId != 0)
{
type.setText(resTypeId);
}
if (socket.power() != 0)
{
DecimalFormat df = new DecimalFormat("#.##");
power.setText(getString(R.string.kw_label, df.format(socket.power())));
}
if (socket.count() != 0)
{
count.setText(getString(R.string.count_label, socket.count()));
}
itemView.setOnClickListener(v -> {
buildChargeSocketDialog(currentIndex, socket.type(), socket.count(), socket.power()).show();
});
socketsGrid.addView(itemView);
}
// add a 'new item' button at the end, to create new sockets
View btnNewItemView = inflater.inflate(R.layout.button_new_item, socketsGrid, false);
btnNewItemView.setOnClickListener(v -> {
buildChargeSocketDialog(-1, "unknown", -1, -1).show();
});
socketsGrid.addView(btnNewItemView);
}
private void refreshOpeningTime() private void refreshOpeningTime()
{ {
final String openingHours = Editor.nativeGetOpeningHours(); final String openingHours = Editor.nativeGetOpeningHours();
@@ -435,6 +737,7 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
mCategory = categoryBlock.findViewById(R.id.name); mCategory = categoryBlock.findViewById(R.id.name);
mCardName = view.findViewById(R.id.cv__name); mCardName = view.findViewById(R.id.cv__name);
mCardAddress = view.findViewById(R.id.cv__address); mCardAddress = view.findViewById(R.id.cv__address);
mCardChargingStation = view.findViewById(R.id.cv__charging_station);
mCardDetails = view.findViewById(R.id.cv__details); mCardDetails = view.findViewById(R.id.cv__details);
mCardSocialMedia = view.findViewById(R.id.cv__social_media); mCardSocialMedia = view.findViewById(R.id.cv__social_media);
mCardBuilding = view.findViewById(R.id.cv__building); mCardBuilding = view.findViewById(R.id.cv__building);
@@ -507,6 +810,9 @@ public class EditorFragment extends BaseMwmFragment implements View.OnClickListe
View blockOutdoorSeating = view.findViewById(R.id.block_outdoor_seating); View blockOutdoorSeating = view.findViewById(R.id.block_outdoor_seating);
mOutdoorSeating = view.findViewById(R.id.sw__outdoor_seating); mOutdoorSeating = view.findViewById(R.id.sw__outdoor_seating);
blockOutdoorSeating.setOnClickListener(this); blockOutdoorSeating.setOnClickListener(this);
mChargeSockets = view.findViewById(R.id.block_charge_sockets);
View blockOpeningHours = view.findViewById(R.id.block_opening_hours); View blockOpeningHours = view.findViewById(R.id.block_opening_hours);
mEditOpeningHours = blockOpeningHours.findViewById(R.id.edit_opening_hours); mEditOpeningHours = blockOpeningHours.findViewById(R.id.edit_opening_hours);
mEditOpeningHours.setOnClickListener(this); mEditOpeningHours.setOnClickListener(this);

View File

@@ -79,6 +79,9 @@ public class PlacePageChargeSocketsFragment extends Fragment implements Observer
{ {
View itemView = inflater.inflate(R.layout.item_charge_socket, mGrid, false); View itemView = inflater.inflate(R.layout.item_charge_socket, mGrid, false);
itemView.setClickable(false);
itemView.setFocusable(false);
MaterialTextView type = itemView.findViewById(R.id.socket_type); MaterialTextView type = itemView.findViewById(R.id.socket_type);
ShapeableImageView icon = itemView.findViewById(R.id.socket_icon); ShapeableImageView icon = itemView.findViewById(R.id.socket_icon);
MaterialTextView power = itemView.findViewById(R.id.socket_power); MaterialTextView power = itemView.findViewById(R.id.socket_power);

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp"
android:baselineAligned="false">
<!-- Left Side: Icon Grid -->
<ScrollView
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:scrollbarFadeDuration="0"
android:verticalScrollbarPosition="left">
<GridLayout
android:id="@+id/edit_socket_type_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="3"
android:paddingEnd="16dp"/>
</ScrollView>
<!-- Right Side: Input Fields -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="top"
android:orientation="vertical">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_socket_power_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
app:endIconMode="clear_text">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/edit_socket_power"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:gravity="end"
android:hint="@string/charge_socket_power"
android:inputType="numberDecimal"
app:simpleItems="@array/charge_sockets_common_powers" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_socket_count_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:endIconMode="clear_text">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/edit_socket_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:completionThreshold="1"
android:gravity="end"
android:hint="@string/charge_socket_count"
android:inputType="numberDecimal"
app:simpleItems="@array/charge_sockets_common_counts" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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:id="@+id/button_new_item"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_margin="4dp"
android:clickable="true"
android:focusable="true"
app:cardCornerRadius="12dp"
app:cardElevation="2dp"
tools:showIn="@layout/item_charging_sockets">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="4dp"
android:text="+"
android:textSize="40sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.button.MaterialButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/materialButtonOutlinedStyle"
android:id="@+id/button_socket_type"
android:checkable="true"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:layout_margin="2dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:text="@string/charge_socket_unknown_other"
app:icon="@drawable/ic_charge_socket_unknown"
app:iconGravity="textTop"
android:textAppearance="@style/MwmTextAppearance.Body5"
/>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<!-- dynamically filled with one button for each socket type -->
<GridLayout
android:id="@+id/edit_socket_type_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="4"
android:padding="8dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_socket_power_layout"
android:layout_width="0dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:layout_weight="1"
android:layout_height="wrap_content"
app:endIconMode="clear_text">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/edit_socket_power"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:completionThreshold="1"
android:inputType="numberDecimal"
android:hint="@string/charge_socket_power"
app:simpleItems="@array/charge_sockets_common_powers"
/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_socket_count_layout"
android:layout_width="0dp"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:layout_weight="1"
android:layout_height="wrap_content"
app:endIconMode="clear_text"
>
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/edit_socket_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:completionThreshold="1"
android:inputType="numberDecimal"
android:hint="@string/charge_socket_count"
app:simpleItems="@array/charge_sockets_common_counts"
/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:context=".editor.EditorActivity"
tools:ignore="DuplicateIds">
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_margin="@dimen/margin_half" android:clipToPadding="false"
android:orientation="vertical"> tools:context=".editor.EditorActivity"
tools:ignore="DuplicateIds">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_half"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/osm_info" android:id="@+id/osm_info"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -22,131 +22,154 @@
android:text="@string/editor_about_osm" android:text="@string/editor_about_osm"
android:textAppearance="@style/MwmTextAppearance.Body4"/> android:textAppearance="@style/MwmTextAppearance.Body4"/>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__category" android:id="@+id/cv__category"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView">
<RelativeLayout <RelativeLayout
android:id="@+id/category" android:id="@+id/category"
style="@style/MwmWidget.Editor.MetadataBlock" style="@style/MwmWidget.Editor.MetadataBlock"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
android:padding="@dimen/margin_half_plus">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/icon"
style="@style/MwmWidget.Editor.MetadataIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_marginBottom="0dp"
android:layout_marginEnd="@dimen/margin_half_plus" android:padding="@dimen/margin_half_plus">
android:layout_marginStart="@dimen/margin_quarter" <com.google.android.material.imageview.ShapeableImageView
tools:src="@drawable/ic_operator" android:id="@+id/icon"
app:tint="?iconTint" /> style="@style/MwmWidget.Editor.MetadataIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/margin_half_plus"
android:layout_marginStart="@dimen/margin_quarter"
tools:src="@drawable/ic_operator"
app:tint="?iconTint" />
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginStart="@dimen/margin_quarter" android:layout_marginStart="@dimen/margin_quarter"
android:layout_toEndOf="@id/icon" android:layout_toEndOf="@id/icon"
android:text="@string/editor_edit_place_category_title" android:text="@string/editor_edit_place_category_title"
android:textAppearance="@style/MwmTextAppearance.Body4" android:textAppearance="@style/MwmTextAppearance.Body4"
tools:text="Trololo"/> tools:text="Trololo"/>
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/name" android:id="@+id/name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignStart="@id/title" android:layout_alignStart="@id/title"
android:layout_below="@id/title" android:layout_below="@id/title"
android:textAppearance="@style/MwmTextAppearance.Body1" android:textAppearance="@style/MwmTextAppearance.Body1"
tools:text="Ololo"/> tools:text="Ololo"/>
</RelativeLayout> </RelativeLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__name" android:id="@+id/cv__charging_station"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView" >
<include layout="@layout/localized_name"/>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cv__address"
style="@style/MwmWidget.Editor.CardView">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="@dimen/margin_base"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base" android:orientation="vertical"
android:fontFamily="@string/robotoMedium" android:paddingEnd="@dimen/margin_base"
android:text="@string/address" android:paddingStart="@dimen/margin_base">
android:textAppearance="@style/MwmTextAppearance.Body3" <com.google.android.material.textview.MaterialTextView
tools:ignore="UnusedAttribute"/> android:layout_width="wrap_content"
<RelativeLayout
android:id="@+id/block_street"
style="@style/MwmWidget.Editor.MetadataBlock.Clickable"
android:paddingBottom="@dimen/margin_half"
android:paddingTop="@dimen/margin_half">
<com.google.android.material.imageview.ShapeableImageView
style="@style/MwmWidget.Editor.MetadataIcon"
app:srcCompat="@drawable/ic_street_address"
tools:ignore="ContentDescription" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/street_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@dimen/editor_margin_left" android:layout_marginTop="@dimen/margin_base"
android:text="@string/street" android:fontFamily="@string/robotoMedium"
android:textAppearance="@style/MwmTextAppearance.Body4"/> android:text="@string/charging_station_available_sockets"
<com.google.android.material.textview.MaterialTextView android:textAppearance="@style/MwmTextAppearance.Body3"
android:id="@+id/street" tools:ignore="UnusedAttribute"/>
style="@style/MwmWidget.Editor.FieldLayout"
android:layout_below="@id/street_title"
android:layout_marginTop="@dimen/margin_quarter"
android:gravity="center_vertical"
android:textAppearance="@style/MwmTextAppearance.Body1"
tools:text="Red str."
app:drawableEndCompat="@drawable/ic_arrow_down" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignStart="@id/street"
android:layout_below="@id/street"
android:layout_marginTop="@dimen/margin_quarter_plus"
android:background="?dividerHorizontal"/>
</RelativeLayout>
<include <include
android:id="@+id/block_building" android:id="@+id/block_charge_sockets"
layout="@layout/item_editor_input"/> layout="@layout/item_charging_sockets"/>
<include
android:id="@+id/block_zipcode"
layout="@layout/item_editor_input"/>
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__details" android:id="@+id/cv__name"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView">
<include layout="@layout/localized_name"/>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cv__address"
style="@style/MwmWidget.Editor.CardView">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="@dimen/margin_base"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base" android:orientation="vertical"
android:paddingBottom="@dimen/margin_half" android:paddingEnd="@dimen/margin_base"
android:fontFamily="@string/robotoMedium" android:paddingStart="@dimen/margin_base">
android:text="@string/details" <com.google.android.material.textview.MaterialTextView
android:textAppearance="@style/MwmTextAppearance.Body3" android:layout_width="wrap_content"
tools:ignore="UnusedAttribute"/> android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base"
android:fontFamily="@string/robotoMedium"
android:text="@string/address"
android:textAppearance="@style/MwmTextAppearance.Body3"
tools:ignore="UnusedAttribute"/>
<RelativeLayout
android:id="@+id/block_street"
style="@style/MwmWidget.Editor.MetadataBlock.Clickable"
android:paddingBottom="@dimen/margin_half"
android:paddingTop="@dimen/margin_half">
<com.google.android.material.imageview.ShapeableImageView
style="@style/MwmWidget.Editor.MetadataIcon"
app:srcCompat="@drawable/ic_street_address"
tools:ignore="ContentDescription" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/street_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/editor_margin_left"
android:text="@string/street"
android:textAppearance="@style/MwmTextAppearance.Body4"/>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/street"
style="@style/MwmWidget.Editor.FieldLayout"
android:layout_below="@id/street_title"
android:layout_marginTop="@dimen/margin_quarter"
android:gravity="center_vertical"
android:textAppearance="@style/MwmTextAppearance.Body1"
tools:text="Red str."
app:drawableEndCompat="@drawable/ic_arrow_down" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignStart="@id/street"
android:layout_below="@id/street"
android:layout_marginTop="@dimen/margin_quarter_plus"
android:background="?dividerHorizontal"/>
</RelativeLayout>
<include
android:id="@+id/block_building"
layout="@layout/item_editor_input"/>
<include
android:id="@+id/block_zipcode"
layout="@layout/item_editor_input"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView
android:id="@+id/cv__details"
style="@style/MwmWidget.Editor.CardView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="@dimen/margin_base"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base"
android:paddingBottom="@dimen/margin_half"
android:fontFamily="@string/robotoMedium"
android:text="@string/details"
android:textAppearance="@style/MwmTextAppearance.Body3"
tools:ignore="UnusedAttribute"/>
<include <include
android:id="@+id/block_opening_hours" android:id="@+id/block_opening_hours"
layout="@layout/item_opening_hours"/> layout="@layout/item_opening_hours"/>
<RelativeLayout <RelativeLayout
android:id="@+id/block_cuisine" android:id="@+id/block_cuisine"
@@ -183,12 +206,12 @@
</RelativeLayout> </RelativeLayout>
<include <include
android:id="@+id/block_operator" android:id="@+id/block_operator"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_website" android:id="@+id/block_website"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_website_menu" android:id="@+id/block_website_menu"
@@ -224,12 +247,12 @@
</RelativeLayout> </RelativeLayout>
<include <include
android:id="@+id/block_email" android:id="@+id/block_email"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_level" android:id="@+id/block_level"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<RelativeLayout <RelativeLayout
android:id="@+id/block_wifi" android:id="@+id/block_wifi"
@@ -314,42 +337,42 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__social_media" android:id="@+id/cv__social_media"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="@dimen/margin_base"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base" android:orientation="vertical"
android:paddingBottom="@dimen/margin_half" android:paddingEnd="@dimen/margin_base"
android:fontFamily="@string/robotoMedium" android:paddingStart="@dimen/margin_base">
android:text="@string/social_media" <com.google.android.material.textview.MaterialTextView
android:textAppearance="@style/MwmTextAppearance.Body3" android:layout_width="wrap_content"
tools:ignore="UnusedAttribute"/> android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base"
android:paddingBottom="@dimen/margin_half"
android:fontFamily="@string/robotoMedium"
android:text="@string/social_media"
android:textAppearance="@style/MwmTextAppearance.Body3"
tools:ignore="UnusedAttribute"/>
<include <include
android:id="@+id/block_fediverse" android:id="@+id/block_fediverse"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_facebook" android:id="@+id/block_facebook"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_instagram" android:id="@+id/block_instagram"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_twitter" android:id="@+id/block_twitter"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_vk" android:id="@+id/block_vk"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_line" android:id="@+id/block_line"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
<include <include
android:id="@+id/block_bluesky" android:id="@+id/block_bluesky"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
@@ -357,61 +380,61 @@
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__building" android:id="@+id/cv__building"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingEnd="@dimen/margin_base"
android:paddingStart="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base" android:orientation="vertical"
android:paddingBottom="@dimen/margin_half" android:paddingEnd="@dimen/margin_base"
android:fontFamily="@string/robotoMedium" android:paddingStart="@dimen/margin_base">
android:text="@string/building" <com.google.android.material.textview.MaterialTextView
android:textAppearance="@style/MwmTextAppearance.Body3" android:layout_width="wrap_content"
tools:ignore="UnusedAttribute"/> android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_base"
android:paddingBottom="@dimen/margin_half"
android:fontFamily="@string/robotoMedium"
android:text="@string/building"
android:textAppearance="@style/MwmTextAppearance.Body3"
tools:ignore="UnusedAttribute"/>
<include <include
android:id="@+id/block_levels" android:id="@+id/block_levels"
layout="@layout/item_editor_input"/> layout="@layout/item_editor_input"/>
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/cv__more" android:id="@+id/cv__more"
style="@style/MwmWidget.Editor.CardView"> style="@style/MwmWidget.Editor.CardView">
<LinearLayout <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/margin_base">
<com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@string/robotoMedium" android:orientation="vertical"
android:text="@string/editor_other_info" android:padding="@dimen/margin_base">
android:textAppearance="@style/MwmTextAppearance.Body3"/> <com.google.android.material.textview.MaterialTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@string/robotoMedium"
android:text="@string/editor_other_info"
android:textAppearance="@style/MwmTextAppearance.Body3"/>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/custom_input" android:id="@+id/custom_input"
style="@style/MwmWidget.Editor.CustomTextInput" style="@style/MwmWidget.Editor.CustomTextInput"
android:gravity="center_vertical" android:gravity="center_vertical"
android:minHeight="74dp" android:minHeight="74dp"
android:textColorHint="?android:textColorSecondary" android:textColorHint="?android:textColorSecondary"
app:hintEnabled="false"> app:hintEnabled="false">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/input" android:id="@+id/input"
style="@style/MwmWidget.Editor.FieldLayout.EditText" style="@style/MwmWidget.Editor.FieldLayout.EditText"
android:inputType="textMultiLine" android:inputType="textMultiLine"
android:hint="@string/editor_note_hint"/> android:hint="@string/editor_note_hint"/>
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/reset" android:id="@+id/reset"
style="@style/MwmWidget.M3.Button.Secondary" style="@style/MwmWidget.M3.Button.Secondary"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -2,8 +2,10 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="120dp" android:layout_width="120dp"
android:layout_height="wrap_content" android:layout_height="120dp"
android:layout_margin="8dp" android:layout_margin="4dp"
android:clickable="true"
android:focusable="true"
app:cardCornerRadius="12dp" app:cardCornerRadius="12dp"
app:cardElevation="2dp"> app:cardElevation="2dp">

View File

@@ -0,0 +1,25 @@
<?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"
android:orientation="vertical">
<GridLayout
android:id="@+id/socket_grid_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="3"
android:padding="0dp"
android:alignmentMode="alignMargins"
android:columnOrderPreserved="false">
<include layout="@layout/button_new_item" />
</GridLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/edit_socket_info_tooltip"
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>

View File

@@ -877,11 +877,14 @@
<string name="hours_confirmed_time_ago">Confirmado %s</string> <string name="hours_confirmed_time_ago">Confirmado %s</string>
<string name="share_track">Compartir traza</string> <string name="share_track">Compartir traza</string>
<string name="pref_tts_no_system_tts_short">No se ha encontrado ningún motor de texto-a-voz, comprueba la configuración de la aplicación</string> <string name="pref_tts_no_system_tts_short">No se ha encontrado ningún motor de texto-a-voz, comprueba la configuración de la aplicación</string>
<string name="unknown_power_output">potencia desconocida</string> <string name="unknown_power_output">desconocida</string>
<string name="charge_socket_type2">Tipo 2 (sin cable)</string> <string name="charge_socket_type2">Tipo 2 (sin cable)</string>
<string name="charge_socket_type2_cable">Tipo 2 (con cable)</string> <string name="charge_socket_type2_cable">Tipo 2 (con cable)</string>
<string name="charge_socket_type2_combo">Tipo 2 combo</string> <string name="charge_socket_type2_combo">Tipo 2 combo</string>
<string name="charge_socket_type1">Tipo 1</string> <string name="charge_socket_type1">Tipo 1</string>
<string name="charge_socket_nacs">NACS</string> <string name="charge_socket_nacs">NACS</string>
<string name="charge_socket_chademo">CHAdeMO</string> <string name="charge_socket_chademo">CHAdeMO</string>
<string name="charge_socket_count">Recuento</string>
<string name="charge_socket_power">Potencia (kW)</string>
<string name="unknown_count">desconocido</string>
</resources> </resources>

View File

@@ -876,11 +876,14 @@
<string name="existence_confirmed_time_ago">Existence confirmée %s</string> <string name="existence_confirmed_time_ago">Existence confirmée %s</string>
<string name="hours_confirmed_time_ago">Confirmé %s</string> <string name="hours_confirmed_time_ago">Confirmé %s</string>
<string name="pref_tts_no_system_tts_short">Impossible de lire ce texte à voix haute, vérifiez les paramètres de lapplication</string> <string name="pref_tts_no_system_tts_short">Impossible de lire ce texte à voix haute, vérifiez les paramètres de lapplication</string>
<string name="unknown_power_output">puissance inconnue</string> <string name="unknown_power_output">inconnue</string>
<string name="charge_socket_type2">Type 2 (sans câble)</string> <string name="charge_socket_type2">Type 2 (sans câble)</string>
<string name="charge_socket_type2_cable">Type 2 (avec câble)</string> <string name="charge_socket_type2_cable">Type 2 (avec câble)</string>
<string name="charge_socket_type2_combo">Type 2 combo</string> <string name="charge_socket_type2_combo">Type 2 combo</string>
<string name="charge_socket_type1">Type 1</string> <string name="charge_socket_type1">Type 1</string>
<string name="charge_socket_nacs">NACS</string> <string name="charge_socket_nacs">NACS</string>
<string name="charge_socket_chademo">CHAdeMO</string> <string name="charge_socket_chademo">CHAdeMO</string>
<string name="charge_socket_count">Quantité</string>
<string name="charge_socket_power">Puissance (kW)</string>
<string name="unknown_count">inconnu</string>
</resources> </resources>

View File

@@ -22,4 +22,32 @@
<item>@drawable/route_point_19</item> <item>@drawable/route_point_19</item>
<item>@drawable/route_point_20</item> <item>@drawable/route_point_20</item>
</integer-array> </integer-array>
<string-array name="charge_socket_types">
<item>type2_combo</item>
<item>nacs</item>
<item>chademo</item>
<item>type1</item>
<item>type2_cable</item>
<item>type2</item>
<item>unknown</item>
</string-array>
<string-array name="charge_sockets_common_powers">
<item>22</item>
<item>43</item>
<item>50</item>
<item>100</item>
<item>150</item>
<item>200</item>
<item>350</item>
</string-array>
<string-array name="charge_sockets_common_counts">
<item>1</item>
<item>2</item>
<item>4</item>
<item>8</item>
<item>10</item>
</string-array>
</resources> </resources>

View File

@@ -913,7 +913,7 @@
<string name="share_track">Share Track</string> <string name="share_track">Share Track</string>
<string name="delete_track_dialog_title">Delete %s?</string> <string name="delete_track_dialog_title">Delete %s?</string>
<string name="pref_tts_no_system_tts_short">No text-to-speech engine found, check the app settings</string> <string name="pref_tts_no_system_tts_short">No text-to-speech engine found, check the app settings</string>
<string name="unknown_power_output">unknown power</string> <string name="unknown_power_output">unknown</string>
<string name="charge_socket_type2">Type 2 (no cable)</string> <string name="charge_socket_type2">Type 2 (no cable)</string>
<string name="charge_socket_type2_cable">Type 2 (w/ cable)</string> <string name="charge_socket_type2_cable">Type 2 (w/ cable)</string>
<string name="charge_socket_type2_combo">Type 2 combo</string> <string name="charge_socket_type2_combo">Type 2 combo</string>
@@ -921,4 +921,14 @@
<string name="charge_socket_nacs">NACS</string> <string name="charge_socket_nacs">NACS</string>
<string name="charge_socket_chademo">CHAdeMO</string> <string name="charge_socket_chademo">CHAdeMO</string>
<string name="unknown_socket_type">unknown socket</string> <string name="unknown_socket_type">unknown socket</string>
<string name="unknow_socket_type">unknown socket</string>
<string name="edit_socket_info_tooltip">Create new sockets or edit existing ones.</string>
<string name="charging_station_available_sockets">Available sockets</string>
<string name="charge_socket_unknown_other">Other or unknown</string>
<string name="charge_socket_count">Count</string>
<string name="charge_socket_power">Power (kW)</string>
<string name="editor_socket">Edit socket</string>
<string name="unknown_count">unknown</string>
<string name="error_value_must_be_positive">The value must be positive</string>
<string name="error_invalid_number">Invalid number</string>
</resources> </resources>

View File

@@ -7,6 +7,7 @@
#include "indexer/cuisines.hpp" #include "indexer/cuisines.hpp"
#include "indexer/editable_map_object.hpp" #include "indexer/editable_map_object.hpp"
#include "indexer/feature_charge_sockets.hpp"
#include "indexer/feature_utils.hpp" #include "indexer/feature_utils.hpp"
#include "indexer/validate_and_format_contacts.hpp" #include "indexer/validate_and_format_contacts.hpp"
@@ -94,6 +95,64 @@ JNIEXPORT void JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeSetOpeningHo
g_editableMapObject.SetOpeningHours(jni::ToNativeString(env, value)); g_editableMapObject.SetOpeningHours(jni::ToNativeString(env, value));
} }
JNIEXPORT jobjectArray JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeGetChargeSockets(JNIEnv * env, jclass)
{
auto sockets = g_editableMapObject.GetChargeSockets();
jclass descClass = env->FindClass("app/organicmaps/sdk/bookmarks/data/ChargeSocketDescriptor");
jmethodID ctor = env->GetMethodID(descClass, "<init>", "(Ljava/lang/String;ID)V");
// Create a Java array
jobjectArray result = env->NewObjectArray(sockets.size(), descClass, nullptr);
for (size_t i = 0; i < sockets.size(); ++i)
{
auto const & s = sockets[i];
jstring jType = env->NewStringUTF(s.type.c_str());
jobject jDesc = env->NewObject(descClass, ctor, jType, (jint)s.count, (jdouble)s.power);
env->SetObjectArrayElement(result, i, jDesc);
env->DeleteLocalRef(jType);
env->DeleteLocalRef(jDesc);
}
return result;
}
JNIEXPORT void JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeSetChargeSockets(JNIEnv * env, jclass,
jobjectArray jSockets)
{
ChargeSocketsHelper chargeSockets;
jsize len = env->GetArrayLength(jSockets);
jclass descClass = env->FindClass("app/organicmaps/sdk/bookmarks/data/ChargeSocketDescriptor");
jfieldID fidType = env->GetFieldID(descClass, "type", "Ljava/lang/String;");
jfieldID fidCount = env->GetFieldID(descClass, "count", "I");
jfieldID fidPower = env->GetFieldID(descClass, "power", "D");
for (jsize i = 0; i < len; ++i)
{
jobject jDesc = env->GetObjectArrayElement(jSockets, i);
jstring jType = (jstring)env->GetObjectField(jDesc, fidType);
char const * cType = env->GetStringUTFChars(jType, nullptr);
jint count = env->GetIntField(jDesc, fidCount);
jdouble power = env->GetDoubleField(jDesc, fidPower);
chargeSockets.AddSocket(cType, static_cast<unsigned int>(count), static_cast<double>(power));
env->ReleaseStringUTFChars(jType, cType);
env->DeleteLocalRef(jType);
env->DeleteLocalRef(jDesc);
}
g_editableMapObject.SetChargeSockets(chargeSockets.ToString());
}
JNIEXPORT jstring JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeGetMetadata(JNIEnv * env, jclass, jint id) JNIEXPORT jstring JNICALL Java_app_organicmaps_sdk_editor_Editor_nativeGetMetadata(JNIEnv * env, jclass, jint id)
{ {
auto const metaID = static_cast<osm::MapObject::MetadataID>(id); auto const metaID = static_cast<osm::MapObject::MetadataID>(id);

View File

@@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Size; import androidx.annotation.Size;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import app.organicmaps.sdk.Framework; import app.organicmaps.sdk.Framework;
import app.organicmaps.sdk.bookmarks.data.ChargeSocketDescriptor;
import app.organicmaps.sdk.bookmarks.data.Metadata; import app.organicmaps.sdk.bookmarks.data.Metadata;
import app.organicmaps.sdk.editor.data.FeatureCategory; import app.organicmaps.sdk.editor.data.FeatureCategory;
import app.organicmaps.sdk.editor.data.Language; import app.organicmaps.sdk.editor.data.Language;
@@ -62,6 +63,8 @@ public final class Editor
public static native void nativeSetMetadata(int id, String value); public static native void nativeSetMetadata(int id, String value);
public static native String nativeGetOpeningHours(); public static native String nativeGetOpeningHours();
public static native void nativeSetOpeningHours(String openingHours); public static native void nativeSetOpeningHours(String openingHours);
public static native ChargeSocketDescriptor[] nativeGetChargeSockets();
public static native void nativeSetChargeSockets(ChargeSocketDescriptor[] sockets);
public static String nativeGetPhone() public static String nativeGetPhone()
{ {
return nativeGetMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER.toInt()); return nativeGetMetadata(Metadata.MetadataType.FMD_PHONE_NUMBER.toInt());

View File

@@ -160,7 +160,26 @@
<field name="drive_through"> <field name="drive_through">
<tag k="drive_through" /> <tag k="drive_through" />
</field> </field>
<!-- charging station socket information -->
<field name="socket_type1_count">
<tag k="socket:type1" />
</field>
<field name="socket_type1_output">
<tag k="socket:type1:output" />
</field>
<field name="socket_type2_count">
<tag k="socket:type2" />
</field>
<field name="socket_type2_output">
<tag k="socket:type2:output" />
</field>
<!-- Field groups. --> <!-- Field groups. -->
<field_group name="charge_sockets">
<field_ref name="socket_type1_count" />
<field_ref name="socket_type1_output" />
<field_ref name="socket_type2_count" />
<field_ref name="socket_type2_output" />
</field_group>
<field_group name="address"> <field_group name="address">
<field_ref name="street" /> <field_ref name="street" />
<field_ref name="housenumber" /> <field_ref name="housenumber" />
@@ -463,7 +482,9 @@
<include group="poi_internet" /> <include group="poi_internet" />
</type> </type>
<type id="amenity-charging_station"> <type id="amenity-charging_station">
<include group="poi" /> <include field="name" />
<include group="charge_sockets" />
<include field="opening_hours" />
<include field="operator" /> <include field="operator" />
</type> </type>
<type id="building-guardhouse"> <type id="building-guardhouse">

View File

@@ -46,7 +46,12 @@ static std::unordered_map<std::string, EType> const kNamesToFMD = {
{"drive_through", EType::FMD_DRIVE_THROUGH}, {"drive_through", EType::FMD_DRIVE_THROUGH},
{"website_menu", EType::FMD_WEBSITE_MENU}, {"website_menu", EType::FMD_WEBSITE_MENU},
{"self_service", EType::FMD_SELF_SERVICE}, {"self_service", EType::FMD_SELF_SERVICE},
{"outdoor_seating", EType::FMD_OUTDOOR_SEATING} {"outdoor_seating", EType::FMD_OUTDOOR_SEATING},
// TODO(skadge): this won't work, obv
{"socket_type1_count", EType::FMD_CHARGE_SOCKETS},
{"socket_type1_output", EType::FMD_CHARGE_SOCKETS},
{"socket_type2_count", EType::FMD_CHARGE_SOCKETS},
{"socket_type2_output", EType::FMD_CHARGE_SOCKETS},
/// @todo Add description? /// @todo Add description?
}; };

View File

@@ -2,6 +2,8 @@
#include "indexer/classificator.hpp" #include "indexer/classificator.hpp"
#include "indexer/edit_journal.hpp" #include "indexer/edit_journal.hpp"
#include "indexer/feature_charge_sockets.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/ftypes_matcher.hpp" #include "indexer/ftypes_matcher.hpp"
#include "indexer/postcodes_matcher.hpp" #include "indexer/postcodes_matcher.hpp"
#include "indexer/validate_and_format_contacts.hpp" #include "indexer/validate_and_format_contacts.hpp"
@@ -158,6 +160,7 @@ void EditableMapObject::ForEachMetadataItem(function<void(string_view tag, strin
case MetadataID::FMD_EXTERNAL_URI: case MetadataID::FMD_EXTERNAL_URI:
case MetadataID::FMD_WHEELCHAIR: // Value is runtime only, data is taken from the classificator types, should not case MetadataID::FMD_WHEELCHAIR: // Value is runtime only, data is taken from the classificator types, should not
// be used to update the OSM database // be used to update the OSM database
case MetadataID::FMD_CHARGE_SOCKETS: // multiple keys; handled via the edit journal
break; break;
default: fn(ToString(type), value); break; default: fn(ToString(type), value); break;
} }
@@ -325,6 +328,14 @@ void EditableMapObject::SetOpeningHours(std::string oh)
m_metadata.Set(MetadataID::FMD_OPEN_HOURS, std::move(oh)); m_metadata.Set(MetadataID::FMD_OPEN_HOURS, std::move(oh));
} }
void EditableMapObject::SetChargeSockets(std::string sockets)
{
// parse the list of sockets provided by the frontend, and re-generate the
// socket list, thus ensuring it is valid & sorted.
ChargeSocketsHelper helper(sockets);
m_metadata.Set(MetadataID::FMD_CHARGE_SOCKETS, helper.ToString());
}
void EditableMapObject::SetInternet(feature::Internet internet) void EditableMapObject::SetInternet(feature::Internet internet)
{ {
m_metadata.Set(MetadataID::FMD_INTERNET, DebugPrint(internet)); m_metadata.Set(MetadataID::FMD_INTERNET, DebugPrint(internet));
@@ -761,6 +772,10 @@ void EditableMapObject::LogDiffInJournal(EditableMapObject const & unedited_emo)
for (uint8_t i = 0; i < static_cast<uint8_t>(feature::Metadata::FMD_COUNT); ++i) for (uint8_t i = 0; i < static_cast<uint8_t>(feature::Metadata::FMD_COUNT); ++i)
{ {
auto const type = static_cast<feature::Metadata::EType>(i); auto const type = static_cast<feature::Metadata::EType>(i);
// CHARGE_SOCKETS have multiple keys/values; handled separately further down
if (type == feature::Metadata::FMD_CHARGE_SOCKETS)
continue;
std::string_view const & value = GetMetadata(type); std::string_view const & value = GetMetadata(type);
std::string_view const & old_value = unedited_emo.GetMetadata(type); std::string_view const & old_value = unedited_emo.GetMetadata(type);
@@ -811,6 +826,15 @@ void EditableMapObject::LogDiffInJournal(EditableMapObject const & unedited_emo)
if (cuisinesModified) if (cuisinesModified)
m_journal.AddTagChange("cuisine", strings::JoinStrings(old_cuisines, ";"), strings::JoinStrings(new_cuisines, ";")); m_journal.AddTagChange("cuisine", strings::JoinStrings(old_cuisines, ";"), strings::JoinStrings(new_cuisines, ";"));
// charge sockets
auto chargeSocketsDiff = ChargeSocketsHelper(GetChargeSockets()).Diff(unedited_emo.GetChargeSockets());
for (auto const & kvdiff : chargeSocketsDiff)
{
std::string key, old_value, new_value;
std::tie(key, old_value, new_value) = kvdiff;
m_journal.AddTagChange(key, old_value, new_value);
}
} }
bool AreObjectsEqualIgnoringStreet(EditableMapObject const & lhs, EditableMapObject const & rhs) bool AreObjectsEqualIgnoringStreet(EditableMapObject const & lhs, EditableMapObject const & rhs)

View File

@@ -108,6 +108,7 @@ public:
bool UpdateMetadataValue(std::string_view key, std::string value); bool UpdateMetadataValue(std::string_view key, std::string value);
void SetOpeningHours(std::string oh); void SetOpeningHours(std::string oh);
void SetChargeSockets(std::string sockets);
void SetInternet(feature::Internet internet); void SetInternet(feature::Internet internet);
/// @param[in] cuisine is a vector of osm cuisine ids. /// @param[in] cuisine is a vector of osm cuisine ids.