Added multi menu for map context menu
This commit is contained in:
parent
04bb3a63a7
commit
db8b44f18f
6 changed files with 154 additions and 71 deletions
|
@ -62,6 +62,7 @@ public class DownloadActivityType {
|
|||
public DownloadActivityType(int stringResource, String tag, int orderIndex) {
|
||||
this.stringResource = stringResource;
|
||||
this.tag = tag;
|
||||
this.orderIndex = orderIndex;
|
||||
byTag.put(tag, this);
|
||||
iconResource = R.drawable.ic_map;
|
||||
}
|
||||
|
|
|
@ -948,7 +948,7 @@ public class MapContextMenuFragment extends Fragment implements DownloadEvents {
|
|||
mapActivity.getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(slideInAnim, slideOutAnim, slideInAnim, slideOutAnim)
|
||||
.add(R.id.fragmentContainer, fragment, TAG)
|
||||
.addToBackStack(TAG).commit();
|
||||
.addToBackStack(TAG).commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
//DownloadEvents
|
||||
|
|
|
@ -4,7 +4,6 @@ import android.graphics.drawable.Drawable;
|
|||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import net.osmand.binary.BinaryMapDataObject;
|
||||
import net.osmand.data.Amenity;
|
||||
import net.osmand.data.FavouritePoint;
|
||||
import net.osmand.data.LatLon;
|
||||
|
@ -15,26 +14,27 @@ import net.osmand.plus.OsmandApplication;
|
|||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.TargetPointsHelper.TargetPoint;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.audionotes.AudioVideoNoteMenuController;
|
||||
import net.osmand.plus.audionotes.AudioVideoNotesPlugin.Recording;
|
||||
import net.osmand.plus.helpers.SearchHistoryHelper;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.AmenityMenuController;
|
||||
import net.osmand.plus.audionotes.AudioVideoNoteMenuController;
|
||||
import net.osmand.plus.osmedit.EditPOIMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.FavouritePointMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.GpxItemMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.HistoryMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.MapDataMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.MyLocationMenuController;
|
||||
import net.osmand.plus.osmo.OsMoMenuController;
|
||||
import net.osmand.plus.osmedit.OsmBugMenuController;
|
||||
import net.osmand.plus.parkingpoint.ParkingPositionMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.PointDescriptionMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.TargetPointMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.controllers.WptPtMenuController;
|
||||
import net.osmand.plus.mapcontextmenu.other.ShareMenu;
|
||||
import net.osmand.plus.osmedit.EditPOIMenuController;
|
||||
import net.osmand.plus.osmedit.OsmBugMenuController;
|
||||
import net.osmand.plus.osmedit.OsmBugsLayer.OpenStreetNote;
|
||||
import net.osmand.plus.osmedit.OsmPoint;
|
||||
import net.osmand.plus.osmo.OsMoGroupsStorage.OsMoDevice;
|
||||
import net.osmand.plus.osmo.OsMoMenuController;
|
||||
import net.osmand.plus.parkingpoint.ParkingPositionMenuController;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer.DownloadMapObject;
|
||||
|
||||
public abstract class MenuController extends BaseMenuController {
|
||||
|
||||
|
@ -92,8 +92,8 @@ public abstract class MenuController extends BaseMenuController {
|
|||
menuController = new EditPOIMenuController(app, mapActivity, pointDescription, (OsmPoint) object);
|
||||
} else if (object instanceof WptPt) {
|
||||
menuController = new WptPtMenuController(app, mapActivity, pointDescription, (WptPt) object);
|
||||
} else if (object instanceof BinaryMapDataObject) {
|
||||
menuController = new MapDataMenuController(app, mapActivity, pointDescription, (BinaryMapDataObject) object);
|
||||
} else if (object instanceof DownloadMapObject) {
|
||||
menuController = new MapDataMenuController(app, mapActivity, pointDescription, (DownloadMapObject) object);
|
||||
} else if (object instanceof OpenStreetNote) {
|
||||
menuController = new OsmBugMenuController(app, mapActivity, pointDescription, (OpenStreetNote) object);
|
||||
} else if (object instanceof GpxDisplayItem) {
|
||||
|
|
|
@ -3,54 +3,87 @@ package net.osmand.plus.mapcontextmenu.controllers;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.osmand.binary.BinaryMapDataObject;
|
||||
import net.osmand.access.AccessibleToast;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.PointDescription;
|
||||
import net.osmand.map.OsmandRegions;
|
||||
import net.osmand.map.WorldRegion;
|
||||
import net.osmand.plus.OsmandApplication;
|
||||
import net.osmand.plus.OsmandPlugin;
|
||||
import net.osmand.plus.R;
|
||||
import net.osmand.plus.activities.MapActivity;
|
||||
import net.osmand.plus.download.DownloadActivity;
|
||||
import net.osmand.plus.download.DownloadActivityType;
|
||||
import net.osmand.plus.download.DownloadIndexesThread;
|
||||
import net.osmand.plus.download.DownloadResourceGroup;
|
||||
import net.osmand.plus.download.DownloadValidationManager;
|
||||
import net.osmand.plus.download.IndexItem;
|
||||
import net.osmand.plus.helpers.FileNameTranslationHelper;
|
||||
import net.osmand.plus.mapcontextmenu.MenuBuilder;
|
||||
import net.osmand.plus.mapcontextmenu.MenuController;
|
||||
import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
|
||||
import net.osmand.plus.views.DownloadedRegionsLayer.DownloadMapObject;
|
||||
import net.osmand.util.Algorithms;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MapDataMenuController extends MenuController {
|
||||
private WorldRegion region;
|
||||
private DownloadMapObject mapObject;
|
||||
private IndexItem indexItem;
|
||||
private List<IndexItem> otherIndexItems;
|
||||
private boolean srtmDisabled;
|
||||
private boolean srtmNeedsInstallation;
|
||||
|
||||
private DownloadIndexesThread downloadThread;
|
||||
|
||||
public MapDataMenuController(OsmandApplication app, MapActivity mapActivity, PointDescription pointDescription, BinaryMapDataObject dataObject) {
|
||||
public MapDataMenuController(OsmandApplication app, final MapActivity mapActivity, PointDescription pointDescription, final DownloadMapObject mapObject) {
|
||||
super(new MenuBuilder(app), pointDescription, mapActivity);
|
||||
initData(app, dataObject);
|
||||
this.mapObject = mapObject;
|
||||
indexItem = mapObject.getIndexItem();
|
||||
downloadThread = app.getDownloadThread();
|
||||
if (indexItem != null) {
|
||||
otherIndexItems = new LinkedList<>(downloadThread.getIndexes().getIndexItems(mapObject.getWorldRegion()));
|
||||
otherIndexItems.remove(indexItem);
|
||||
}
|
||||
|
||||
srtmDisabled = OsmandPlugin.getEnabledPlugin(SRTMPlugin.class) == null;
|
||||
OsmandPlugin srtmPlugin = OsmandPlugin.getPlugin(SRTMPlugin.class);
|
||||
srtmNeedsInstallation = srtmPlugin == null || srtmPlugin.needsInstallation();
|
||||
|
||||
leftTitleButtonController = new TitleButtonController() {
|
||||
@Override
|
||||
public void buttonPressed() {
|
||||
if (indexItem != null) {
|
||||
if ((indexItem.getType() == DownloadActivityType.SRTM_COUNTRY_FILE
|
||||
|| indexItem.getType() == DownloadActivityType.HILLSHADE_FILE)
|
||||
&& srtmDisabled) {
|
||||
getMapActivity().getContextMenu().close();
|
||||
|
||||
if (srtmNeedsInstallation) {
|
||||
OsmandPlugin plugin = OsmandPlugin.getPlugin(SRTMPlugin.class);
|
||||
if (plugin != null) {
|
||||
mapActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL())));
|
||||
}
|
||||
} else {
|
||||
mapActivity.startActivity(new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization()
|
||||
.getPluginsActivity()));
|
||||
AccessibleToast.makeText(mapActivity, mapActivity.getString(R.string.activate_srtm_plugin),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
new DownloadValidationManager(getMapActivity().getMyApplication())
|
||||
.startDownload(getMapActivity(), indexItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
leftTitleButtonController.caption = getMapActivity().getString(R.string.shared_string_download);
|
||||
leftTitleButtonController.leftIconId = R.drawable.ic_action_import;
|
||||
|
@ -71,14 +104,15 @@ public class MapDataMenuController extends MenuController {
|
|||
public void buttonPressed() {
|
||||
getMapActivity().getContextMenu().close();
|
||||
|
||||
DownloadResourceGroup group = downloadThread.getIndexes().getRegionGroup(region);
|
||||
if (group != null) {
|
||||
final Intent intent = new Intent(getMapActivity(), getMapActivity().getMyApplication()
|
||||
.getAppCustomization().getDownloadIndexActivity());
|
||||
intent.putExtra(DownloadActivity.FILTER_GROUP, group.getUniqueId());
|
||||
intent.putExtra(DownloadActivity.TAB_TO_OPEN, DownloadActivity.DOWNLOAD_TAB);
|
||||
getMapActivity().startActivity(intent);
|
||||
Map<Object, IContextMenuProvider> selectedObjects = new HashMap<>();
|
||||
IContextMenuProvider provider = mapActivity.getMapLayers().getDownloadedRegionsLayer();
|
||||
for (IndexItem item : otherIndexItems) {
|
||||
selectedObjects.put(
|
||||
new DownloadMapObject(mapObject.getDataObject(), mapObject.getWorldRegion(), item),
|
||||
provider);
|
||||
}
|
||||
mapActivity.getContextMenu().getMultiSelectionMenu().show(
|
||||
mapActivity.getContextMenu().getLatLon(), selectedObjects);
|
||||
}
|
||||
};
|
||||
topRightTitleButtonController.caption = getMapActivity().getString(R.string.download_select_map_types);
|
||||
|
@ -99,16 +133,10 @@ public class MapDataMenuController extends MenuController {
|
|||
updateData();
|
||||
}
|
||||
|
||||
private void initData(OsmandApplication app, BinaryMapDataObject dataObject) {
|
||||
OsmandRegions osmandRegions = app.getRegions();
|
||||
String fullName = osmandRegions.getFullName(dataObject);
|
||||
this.region = osmandRegions.getRegionData(fullName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setObject(Object object) {
|
||||
if (object instanceof BinaryMapDataObject) {
|
||||
initData(getMapActivity().getMyApplication(), (BinaryMapDataObject) object);
|
||||
if (object instanceof DownloadMapObject) {
|
||||
this.mapObject = (DownloadMapObject) object;
|
||||
updateData();
|
||||
}
|
||||
}
|
||||
|
@ -125,14 +153,20 @@ public class MapDataMenuController extends MenuController {
|
|||
|
||||
@Override
|
||||
public Drawable getLeftIcon() {
|
||||
return getIcon(R.drawable.ic_map, R.color.osmand_orange);
|
||||
int iconResId;
|
||||
if (indexItem != null) {
|
||||
iconResId = indexItem.getType().getIconResource();
|
||||
} else {
|
||||
iconResId = R.drawable.ic_map;
|
||||
}
|
||||
return getIcon(iconResId, R.color.osmand_orange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeStr() {
|
||||
String res;
|
||||
if (region != null && region.getSuperregion() != null) {
|
||||
res = region.getSuperregion().getLocaleName();
|
||||
if (mapObject.getWorldRegion().getSuperregion() != null) {
|
||||
res = mapObject.getWorldRegion().getSuperregion().getLocaleName();
|
||||
} else {
|
||||
res = getMapActivity().getString(R.string.shared_string_map);
|
||||
}
|
||||
|
@ -150,10 +184,11 @@ public class MapDataMenuController extends MenuController {
|
|||
@Override
|
||||
public void addPlainMenuItems(String typeStr, PointDescription pointDescription, LatLon latLon) {
|
||||
if (indexItem != null) {
|
||||
addPlainMenuItem(R.drawable.ic_action_info_dark, indexItem.getType().getString(getMapActivity()), false);
|
||||
addPlainMenuItem(R.drawable.ic_action_info_dark, indexItem.getSizeDescription(getMapActivity()), false);
|
||||
}
|
||||
if (region != null && !Algorithms.isEmpty(region.getParams().getWikiLink())) {
|
||||
String[] items = region.getParams().getWikiLink().split(":");
|
||||
if (!Algorithms.isEmpty(mapObject.getWorldRegion().getParams().getWikiLink())) {
|
||||
String[] items = mapObject.getWorldRegion().getParams().getWikiLink().split(":");
|
||||
String url;
|
||||
if (items.length > 1) {
|
||||
url = "https://" + items[0] + ".wikipedia.org/wiki/" + items[1].replace(' ', '_');
|
||||
|
@ -181,7 +216,7 @@ public class MapDataMenuController extends MenuController {
|
|||
@Override
|
||||
public void updateData() {
|
||||
if (indexItem == null) {
|
||||
otherIndexItems = new LinkedList<>(downloadThread.getIndexes().getIndexItems(region));
|
||||
otherIndexItems = new LinkedList<>(downloadThread.getIndexes().getIndexItems(mapObject.getWorldRegion()));
|
||||
Iterator<IndexItem> it = otherIndexItems.iterator();
|
||||
while (it.hasNext()) {
|
||||
IndexItem i = it.next();
|
||||
|
@ -193,15 +228,22 @@ public class MapDataMenuController extends MenuController {
|
|||
}
|
||||
}
|
||||
|
||||
topRightTitleButtonController.visible = otherIndexItems.size() > 0;
|
||||
leftTitleButtonController.leftIconId = R.drawable.ic_action_import;
|
||||
if (indexItem != null) {
|
||||
if (indexItem.isOutdated()) {
|
||||
if ((indexItem.getType() == DownloadActivityType.SRTM_COUNTRY_FILE
|
||||
|| indexItem.getType() == DownloadActivityType.HILLSHADE_FILE)
|
||||
&& srtmDisabled) {
|
||||
leftTitleButtonController.caption = getMapActivity().getString(R.string.get_plugin);
|
||||
leftTitleButtonController.leftIconId = 0;
|
||||
} else if (indexItem.isOutdated()) {
|
||||
leftTitleButtonController.caption = getMapActivity().getString(R.string.shared_string_update);
|
||||
} else {
|
||||
leftTitleButtonController.caption = getMapActivity().getString(R.string.shared_string_download);
|
||||
}
|
||||
}
|
||||
|
||||
rightTitleButtonController.visible = indexItem != null && indexItem.isDownloaded();
|
||||
topRightTitleButtonController.visible = otherIndexItems.size() > 0;
|
||||
|
||||
boolean hasIndexes = downloadThread.getIndexes().isDownloadedFromInternet;
|
||||
boolean isDownloading = indexItem != null && downloadThread.isDownloading(indexItem);
|
||||
|
|
|
@ -214,9 +214,6 @@ public class ContextMenuLayer extends OsmandMapLayer {
|
|||
l.collectObjectsFromPoint(point, tileBox, s);
|
||||
for (Object o : s) {
|
||||
selectedObjects.put(o, l);
|
||||
// if (l instanceof IContextMenuProviderSelection) {
|
||||
// ((IContextMenuProviderSelection) l).setSelectedObject(o);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,30 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
private static int ZOOM_TO_SHOW_SELECTION_ST = 3;
|
||||
private static int ZOOM_TO_SHOW_SELECTION = 10;
|
||||
|
||||
public static class DownloadMapObject {
|
||||
private BinaryMapDataObject dataObject;
|
||||
private WorldRegion worldRegion;
|
||||
private IndexItem indexItem;
|
||||
|
||||
public BinaryMapDataObject getDataObject() {
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
public WorldRegion getWorldRegion() {
|
||||
return worldRegion;
|
||||
}
|
||||
|
||||
public IndexItem getIndexItem() {
|
||||
return indexItem;
|
||||
}
|
||||
|
||||
public DownloadMapObject(BinaryMapDataObject dataObject, WorldRegion worldRegion, IndexItem indexItem) {
|
||||
this.dataObject = dataObject;
|
||||
this.worldRegion = worldRegion;
|
||||
this.indexItem = indexItem;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initLayer(final OsmandMapTileView view) {
|
||||
this.view = view;
|
||||
|
@ -421,12 +445,9 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
|
||||
@Override
|
||||
public LatLon getObjectLocation(Object o) {
|
||||
if (o instanceof BinaryMapDataObject) {
|
||||
String fullName = osmandRegions.getFullName((BinaryMapDataObject) o);
|
||||
final WorldRegion region = osmandRegions.getRegionData(fullName);
|
||||
if (region != null) {
|
||||
return region.getRegionCenter();
|
||||
}
|
||||
if (o instanceof DownloadMapObject) {
|
||||
DownloadMapObject mapObject = ((DownloadMapObject) o);
|
||||
return mapObject.worldRegion.getRegionCenter();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -438,16 +459,10 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
|
||||
@Override
|
||||
public PointDescription getObjectName(Object o) {
|
||||
if (o instanceof BinaryMapDataObject) {
|
||||
String fullName = osmandRegions.getFullName((BinaryMapDataObject) o);
|
||||
final WorldRegion region = osmandRegions.getRegionData(fullName);
|
||||
if (region != null) {
|
||||
if (o instanceof DownloadMapObject) {
|
||||
DownloadMapObject mapObject = ((DownloadMapObject) o);
|
||||
return new PointDescription(PointDescription.POINT_TYPE_WORLD_REGION,
|
||||
view.getContext().getString(R.string.shared_string_map), region.getLocaleName());
|
||||
} else {
|
||||
return new PointDescription(PointDescription.POINT_TYPE_WORLD_REGION,
|
||||
view.getContext().getString(R.string.shared_string_map), ((BinaryMapDataObject) o).getName());
|
||||
}
|
||||
view.getContext().getString(R.string.shared_string_map), mapObject.worldRegion.getLocaleName());
|
||||
}
|
||||
return new PointDescription(PointDescription.POINT_TYPE_WORLD_REGION,
|
||||
view.getContext().getString(R.string.shared_string_map), "");
|
||||
|
@ -463,7 +478,7 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
return false;
|
||||
}
|
||||
|
||||
private void getWorldRegionFromPoint(RotatedTileBox tb, PointF point, List<? super BinaryMapDataObject> dataObjects) {
|
||||
private void getWorldRegionFromPoint(RotatedTileBox tb, PointF point, List<? super DownloadMapObject> dataObjects) {
|
||||
int zoom = tb.getZoom();
|
||||
if (zoom >= ZOOM_TO_SHOW_SELECTION_ST && zoom < ZOOM_TO_SHOW_SELECTION && osmandRegions.isInitialized()) {
|
||||
LatLon pointLatLon = tb.getLatLonFromPixel(point.x, point.y);
|
||||
|
@ -481,32 +496,60 @@ public class DownloadedRegionsLayer extends OsmandMapLayer implements IContextMe
|
|||
|
||||
selectedObjects = result;
|
||||
|
||||
OsmandRegions osmandRegions = app.getRegions();
|
||||
for (BinaryMapDataObject o : result) {
|
||||
dataObjects.add(o);
|
||||
String fullName = osmandRegions.getFullName(o);
|
||||
WorldRegion region = osmandRegions.getRegionData(fullName);
|
||||
if (region != null) {
|
||||
List<IndexItem> indexItems = app.getDownloadThread().getIndexes().getIndexItems(region);
|
||||
List<IndexItem> dataItems = new LinkedList<>();
|
||||
IndexItem regularMapItem = null;
|
||||
for (IndexItem item : indexItems) {
|
||||
if (item.isDownloaded()) {
|
||||
dataItems.add(item);
|
||||
if (item.getType() == DownloadActivityType.NORMAL_FILE) {
|
||||
regularMapItem = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataItems.isEmpty() && regularMapItem != null) {
|
||||
dataItems.add(regularMapItem);
|
||||
}
|
||||
|
||||
if (!dataItems.isEmpty()) {
|
||||
for (IndexItem item : dataItems) {
|
||||
dataObjects.add(new DownloadMapObject(o, region, item));
|
||||
}
|
||||
} else {
|
||||
dataObjects.add(new DownloadMapObject(o, region, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder(Object o) {
|
||||
if (o instanceof BinaryMapDataObject) {
|
||||
String fullName = osmandRegions.getFullName((BinaryMapDataObject) o);
|
||||
final WorldRegion region = osmandRegions.getRegionData(fullName);
|
||||
if (region != null) {
|
||||
return region.getLevel() - 1000;
|
||||
int order = 0;
|
||||
if (o instanceof DownloadMapObject) {
|
||||
DownloadMapObject mapObject = ((DownloadMapObject) o);
|
||||
order = mapObject.worldRegion.getLevel() * 1000 - 100000;
|
||||
if (mapObject.indexItem != null) {
|
||||
order += mapObject.indexItem.getType().getOrderIndex();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedObject(Object o) {
|
||||
if (o instanceof BinaryMapDataObject) {
|
||||
if (o instanceof DownloadMapObject) {
|
||||
DownloadMapObject mapObject = ((DownloadMapObject) o);
|
||||
List<BinaryMapDataObject> list = new LinkedList<>();
|
||||
if (selectedObjects. size() > 0) {
|
||||
list.addAll(selectedObjects);
|
||||
}
|
||||
list.add((BinaryMapDataObject) o);
|
||||
list.add(mapObject.dataObject);
|
||||
selectedObjects = list;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue