Telegram - show images on map

This commit is contained in:
crimean 2018-06-14 21:01:10 +03:00
parent 8189ca95d5
commit 0d89b958ce
12 changed files with 285 additions and 30 deletions

View file

@ -42,6 +42,17 @@
</service> </service>
<receiver android:name=".OnTelegramServiceAlarmReceiver" /> <receiver android:name=".OnTelegramServiceAlarmReceiver" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="net.osmand.telegram.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
</application> </application>
</manifest> </manifest>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path
name="share"
path="share"/>
<external-files-path
name="files"
path="/"/>
<external-path
name="external_files"
path="." />
<files-path
name="files_path"
path="." />
<external-cache-path
name="external_cache_path"
path="." />
</paths>

View file

@ -6,9 +6,14 @@ import android.os.Parcelable;
import net.osmand.aidl.map.ALatLon; import net.osmand.aidl.map.ALatLon;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class AMapPoint implements Parcelable { public class AMapPoint implements Parcelable {
public static final int POINT_IMAGE_SIZE_PX = 160;
public static final String POINT_IMAGE_URI_PARAM = "point_image_uri_param";
private String id; private String id;
private String shortName; private String shortName;
private String fullName; private String fullName;
@ -16,9 +21,10 @@ public class AMapPoint implements Parcelable {
private int color; private int color;
private ALatLon location; private ALatLon location;
private List<String> details = new ArrayList<>(); private List<String> details = new ArrayList<>();
private Map<String, String> params = new HashMap<>();
public AMapPoint(String id, String shortName, String fullName, String typeName, int color, public AMapPoint(String id, String shortName, String fullName, String typeName, int color,
ALatLon location, List<String> details) { ALatLon location, List<String> details, Map<String, String> params) {
this.id = id; this.id = id;
this.shortName = shortName; this.shortName = shortName;
this.fullName = fullName; this.fullName = fullName;
@ -28,6 +34,9 @@ public class AMapPoint implements Parcelable {
if (details != null) { if (details != null) {
this.details.addAll(details); this.details.addAll(details);
} }
if (params != null) {
this.params.putAll(params);
}
} }
public AMapPoint(Parcel in) { public AMapPoint(Parcel in) {
@ -73,6 +82,10 @@ public class AMapPoint implements Parcelable {
return details; return details;
} }
public Map<String, String> getParams() {
return params;
}
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
out.writeString(id); out.writeString(id);
out.writeString(shortName); out.writeString(shortName);
@ -81,6 +94,7 @@ public class AMapPoint implements Parcelable {
out.writeInt(color); out.writeInt(color);
out.writeParcelable(location, flags); out.writeParcelable(location, flags);
out.writeStringList(details); out.writeStringList(details);
out.writeMap(params);
} }
private void readFromParcel(Parcel in) { private void readFromParcel(Parcel in) {
@ -91,6 +105,7 @@ public class AMapPoint implements Parcelable {
color = in.readInt(); color = in.readInt();
location = in.readParcelable(ALatLon.class.getClassLoader()); location = in.readParcelable(ALatLon.class.getClassLoader());
in.readStringList(details); in.readStringList(details);
in.readMap(params, HashMap.class.getClassLoader());
} }
public int describeContents() { public int describeContents() {

View file

@ -172,6 +172,10 @@ class MainActivity : AppCompatActivity(), TelegramListener {
} }
} }
override fun onTelegramUserChanged(user: TdApi.User) {
}
override fun onTelegramError(code: Int, message: String) { override fun onTelegramError(code: Int, message: String) {
runOnUi { runOnUi {
Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show() Toast.makeText(this@MainActivity, "$code - $message", Toast.LENGTH_LONG).show()

View file

@ -8,7 +8,6 @@ import android.content.ServiceConnection
import android.net.Uri import android.net.Uri
import android.os.IBinder import android.os.IBinder
import android.os.RemoteException import android.os.RemoteException
import android.widget.Toast
import net.osmand.aidl.IOsmAndAidlInterface import net.osmand.aidl.IOsmAndAidlInterface
import net.osmand.aidl.favorite.AFavorite import net.osmand.aidl.favorite.AFavorite
import net.osmand.aidl.favorite.AddFavoriteParams import net.osmand.aidl.favorite.AddFavoriteParams
@ -537,10 +536,10 @@ class OsmandAidlHelper(private val app: Application) {
* @param details - list of details. Displayed under context menu. * @param details - list of details. Displayed under context menu.
*/ */
fun addMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, fun addMapPoint(layerId: String, pointId: String, shortName: String, fullName: String,
typeName: String, color: Int, location: ALatLon, details: List<String>?): Boolean { typeName: String, color: Int, location: ALatLon, details: List<String>?, params: Map<String, String>?): Boolean {
if (mIOsmAndAidlInterface != null) { if (mIOsmAndAidlInterface != null) {
try { try {
val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details, params)
return mIOsmAndAidlInterface!!.addMapPoint(AddMapPointParams(layerId, point)) return mIOsmAndAidlInterface!!.addMapPoint(AddMapPointParams(layerId, point))
} catch (e: RemoteException) { } catch (e: RemoteException) {
e.printStackTrace() e.printStackTrace()
@ -563,10 +562,10 @@ class OsmandAidlHelper(private val app: Application) {
* @param details - list of details. Displayed under context menu. * @param details - list of details. Displayed under context menu.
*/ */
fun updateMapPoint(layerId: String, pointId: String, shortName: String, fullName: String, fun updateMapPoint(layerId: String, pointId: String, shortName: String, fullName: String,
typeName: String, color: Int, location: ALatLon, details: List<String>?): Boolean { typeName: String, color: Int, location: ALatLon, details: List<String>?, params: Map<String, String>?): Boolean {
if (mIOsmAndAidlInterface != null) { if (mIOsmAndAidlInterface != null) {
try { try {
val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details) val point = AMapPoint(pointId, shortName, fullName, typeName, color, location, details, params)
return mIOsmAndAidlInterface!!.updateMapPoint(UpdateMapPointParams(layerId, point)) return mIOsmAndAidlInterface!!.updateMapPoint(UpdateMapPointParams(layerId, point))
} catch (e: RemoteException) { } catch (e: RemoteException) {
e.printStackTrace() e.printStackTrace()

View file

@ -1,9 +1,15 @@
package net.osmand.telegram.helpers package net.osmand.telegram.helpers
import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.net.Uri
import android.text.TextUtils
import net.osmand.aidl.map.ALatLon import net.osmand.aidl.map.ALatLon
import net.osmand.aidl.maplayer.point.AMapPoint
import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramApplication
import net.osmand.telegram.utils.AndroidUtils
import org.drinkless.td.libcore.telegram.TdApi import org.drinkless.td.libcore.telegram.TdApi
import java.io.File
class ShowLocationHelper(private val app: TelegramApplication) { class ShowLocationHelper(private val app: TelegramApplication) {
@ -26,6 +32,7 @@ class ShowLocationHelper(private val app: TelegramApplication) {
val content = message.content val content = message.content
if (content is TdApi.MessageLocation) { if (content is TdApi.MessageLocation) {
var userName = "" var userName = ""
var photoUri: Uri? = null
val user = telegramHelper.getUser(message.senderUserId) val user = telegramHelper.getUser(message.senderUserId)
if (user != null) { if (user != null) {
userName = "${user.firstName} ${user.lastName}".trim() userName = "${user.firstName} ${user.lastName}".trim()
@ -35,13 +42,22 @@ class ShowLocationHelper(private val app: TelegramApplication) {
if (userName.isEmpty()) { if (userName.isEmpty()) {
userName = user.phoneNumber userName = user.phoneNumber
} }
val photoPath = telegramHelper.getUserPhotoPath(user)
if (!TextUtils.isEmpty(photoPath)) {
photoUri = AndroidUtils.getUriForFile(app, File(photoPath))
app.grantUriPermission("net.osmand.plus", photoUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
} }
if (userName.isEmpty()) { if (userName.isEmpty()) {
userName = message.senderUserId.toString() userName = message.senderUserId.toString()
} }
setupMapLayer() setupMapLayer()
val params = mutableMapOf<String, String>()
if (photoUri != null) {
params[AMapPoint.POINT_IMAGE_URI_PARAM] = photoUri.toString()
}
osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName, osmandHelper.addMapPoint(MAP_LAYER_ID, "${chatTitle}_${message.senderUserId}", userName, userName,
chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null) chatTitle, Color.RED, ALatLon(content.location.latitude, content.location.longitude), null, params)
} }
} else if (osmandHelper.isOsmandBound()) { } else if (osmandHelper.isOsmandBound()) {
osmandHelper.connectOsmand() osmandHelper.connectOsmand()

View file

@ -42,6 +42,7 @@ class TelegramHelper private constructor() {
private val chatLiveMessages = ConcurrentHashMap<Long, Long>() private val chatLiveMessages = ConcurrentHashMap<Long, Long>()
private val downloadChatFilesMap = ConcurrentHashMap<String, TdApi.Chat>() private val downloadChatFilesMap = ConcurrentHashMap<String, TdApi.Chat>()
private val downloadUserFilesMap = ConcurrentHashMap<String, TdApi.User>()
private val usersLiveMessages = ConcurrentHashMap<Long, TdApi.Message>() private val usersLiveMessages = ConcurrentHashMap<Long, TdApi.Message>()
@ -140,6 +141,7 @@ class TelegramHelper private constructor() {
fun onTelegramChatsRead() fun onTelegramChatsRead()
fun onTelegramChatsChanged() fun onTelegramChatsChanged()
fun onTelegramChatChanged(chat: TdApi.Chat) fun onTelegramChatChanged(chat: TdApi.Chat)
fun onTelegramUserChanged(user: TdApi.User)
fun onTelegramError(code: Int, message: String) fun onTelegramError(code: Int, message: String)
fun onSendLiveLicationError(code: Int, message: String) fun onSendLiveLicationError(code: Int, message: String)
} }
@ -215,6 +217,54 @@ class TelegramHelper private constructor() {
return client != null && haveAuthorization return client != null && haveAuthorization
} }
fun getUserPhotoPath(user: TdApi.User):String? {
return if (hasLocalUserPhoto(user)) {
user.profilePhoto?.small?.local?.path
} else {
if (hasRemoteUserPhoto(user)) {
requestUserPhoto(user)
}
null
}
}
private fun hasLocalUserPhoto(user: TdApi.User): Boolean {
val localPhoto = user.profilePhoto?.small?.local
return if (localPhoto != null) {
localPhoto.canBeDownloaded && localPhoto.isDownloadingCompleted && localPhoto.path.isNotEmpty()
} else {
false
}
}
private fun hasRemoteUserPhoto(user: TdApi.User): Boolean {
val remotePhoto = user.profilePhoto?.small?.remote
return remotePhoto?.id?.isNotEmpty() ?: false
}
private fun requestUserPhoto(user: TdApi.User) {
val remotePhoto = user.profilePhoto?.small?.remote
if (remotePhoto != null && remotePhoto.id.isNotEmpty()) {
downloadUserFilesMap[remotePhoto.id] = user
client!!.send(TdApi.GetRemoteFile(remotePhoto.id, null)) { obj ->
when (obj.constructor) {
TdApi.Error.CONSTRUCTOR -> {
val error = obj as TdApi.Error
val code = error.code
if (code != IGNORED_ERROR_CODE) {
listener?.onTelegramError(code, error.message)
}
}
TdApi.File.CONSTRUCTOR -> {
val file = obj as TdApi.File
client!!.send(TdApi.DownloadFile(file.id, 10), defaultHandler)
}
else -> listener?.onTelegramError(-1, "Receive wrong response from TDLib: $obj")
}
}
}
}
private fun requestChats(reload: Boolean = false) { private fun requestChats(reload: Boolean = false) {
synchronized(chatList) { synchronized(chatList) {
if (reload) { if (reload) {
@ -776,8 +826,17 @@ class TelegramHelper private constructor() {
if (chat != null) { if (chat != null) {
synchronized(chat) { synchronized(chat) {
chat.photo?.small = updateFile.file chat.photo?.small = updateFile.file
listener?.onTelegramChatChanged(chat)
} }
listener?.onTelegramChatChanged(chat)
return
}
val user = downloadUserFilesMap.remove(remoteId)
if (user != null) {
synchronized(user) {
user.profilePhoto?.small = updateFile.file
}
listener?.onTelegramUserChanged(user)
return
} }
} }
} }

View file

@ -5,14 +5,17 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.support.annotation.AttrRes import android.support.annotation.AttrRes
import android.support.annotation.ColorInt import android.support.annotation.ColorInt
import android.support.v4.app.ActivityCompat import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat import android.support.v4.content.FileProvider
import android.util.TypedValue import android.util.TypedValue
import android.util.TypedValue.COMPLEX_UNIT_DIP import android.util.TypedValue.COMPLEX_UNIT_DIP
import android.view.View import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import java.io.File
object AndroidUtils { object AndroidUtils {
@ -61,4 +64,12 @@ object AndroidUtils {
ta.recycle() ta.recycle()
return color return color
} }
fun getUriForFile(context: Context, file: File): Uri {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
Uri.fromFile(file)
} else {
FileProvider.getUriForFile(context, "net.osmand.telegram.fileprovider", file)
}
}
} }

View file

@ -93,7 +93,7 @@ class UiUtils(private val app: TelegramApplication) {
return getDrawable(id, if (light) R.color.icon_color_light else 0) return getDrawable(id, if (light) R.color.icon_color_light else 0)
} }
fun createCircleBitmap(source: Bitmap, recycleSource: Boolean = false): Bitmap { private fun createCircleBitmap(source: Bitmap, recycleSource: Boolean = false): Bitmap {
val size = Math.min(source.width, source.height) val size = Math.min(source.width, source.height)
val width = (source.width - size) / 2 val width = (source.width - size) / 2

View file

@ -7,7 +7,9 @@ import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.drawable.ClipDrawable; import android.graphics.drawable.ClipDrawable;
@ -99,6 +101,20 @@ public class AndroidUtils {
} }
public static Bitmap scaleBitmap(Bitmap bm, int newWidth, int newHeight, boolean keepOriginalBitmap) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
if (!keepOriginalBitmap) {
bm.recycle();
}
return resizedBitmap;
}
public static ColorStateList createBottomNavColorStateList(Context ctx, boolean nightMode) { public static ColorStateList createBottomNavColorStateList(Context ctx, boolean nightMode) {
return AndroidUtils.createCheckedColorStateList(ctx, nightMode, return AndroidUtils.createCheckedColorStateList(ctx, nightMode,
R.color.icon_color, R.color.wikivoyage_active_light, R.color.icon_color, R.color.wikivoyage_active_light,
@ -130,7 +146,7 @@ public class AndroidUtils {
while (i < text.length() && i != -1) { while (i < text.length() && i != -1) {
ImageSpan span = new ImageSpan(icon) { ImageSpan span = new ImageSpan(icon) {
public void draw(Canvas canvas, CharSequence text, int start, int end, public void draw(Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) { float x, int top, int y, int bottom, Paint paint) {
Drawable drawable = getDrawable(); Drawable drawable = getDrawable();
canvas.save(); canvas.save();
int transY = bottom - drawable.getBounds().bottom; int transY = bottom - drawable.getBounds().bottom;
@ -197,7 +213,7 @@ public class AndroidUtils {
ViewParent viewParent = view.getParent(); ViewParent viewParent = view.getParent();
while (viewParent != null && viewParent instanceof View) { while (viewParent != null && viewParent instanceof View) {
View parentView = (View)viewParent; View parentView = (View) viewParent;
if (parentView.getId() == id) if (parentView.getId() == id)
return parentView; return parentView;

View file

@ -6,9 +6,14 @@ import android.os.Parcelable;
import net.osmand.aidl.map.ALatLon; import net.osmand.aidl.map.ALatLon;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class AMapPoint implements Parcelable { public class AMapPoint implements Parcelable {
public static final int POINT_IMAGE_SIZE_PX = 160;
public static final String POINT_IMAGE_URI_PARAM = "point_image_uri_param";
private String id; private String id;
private String shortName; private String shortName;
private String fullName; private String fullName;
@ -16,9 +21,10 @@ public class AMapPoint implements Parcelable {
private int color; private int color;
private ALatLon location; private ALatLon location;
private List<String> details = new ArrayList<>(); private List<String> details = new ArrayList<>();
private Map<String, String> params = new HashMap<>();
public AMapPoint(String id, String shortName, String fullName, String typeName, int color, public AMapPoint(String id, String shortName, String fullName, String typeName, int color,
ALatLon location, List<String> details) { ALatLon location, List<String> details, Map<String, String> params) {
this.id = id; this.id = id;
this.shortName = shortName; this.shortName = shortName;
this.fullName = fullName; this.fullName = fullName;
@ -28,6 +34,9 @@ public class AMapPoint implements Parcelable {
if (details != null) { if (details != null) {
this.details.addAll(details); this.details.addAll(details);
} }
if (params != null) {
this.params.putAll(params);
}
} }
public AMapPoint(Parcel in) { public AMapPoint(Parcel in) {
@ -73,6 +82,10 @@ public class AMapPoint implements Parcelable {
return details; return details;
} }
public Map<String, String> getParams() {
return params;
}
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
out.writeString(id); out.writeString(id);
out.writeString(shortName); out.writeString(shortName);
@ -81,6 +94,7 @@ public class AMapPoint implements Parcelable {
out.writeInt(color); out.writeInt(color);
out.writeParcelable(location, flags); out.writeParcelable(location, flags);
out.writeStringList(details); out.writeStringList(details);
out.writeMap(params);
} }
private void readFromParcel(Parcel in) { private void readFromParcel(Parcel in) {
@ -91,6 +105,7 @@ public class AMapPoint implements Parcelable {
color = in.readInt(); color = in.readInt();
location = in.readParcelable(ALatLon.class.getClassLoader()); location = in.readParcelable(ALatLon.class.getClassLoader());
in.readStringList(details); in.readStringList(details);
in.readMap(params, HashMap.class.getClassLoader());
} }
public int describeContents() { public int describeContents() {

View file

@ -1,13 +1,16 @@
package net.osmand.plus.views; package net.osmand.plus.views;
import android.content.Context; import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PointF; import android.graphics.PointF;
import android.util.DisplayMetrics; import android.net.Uri;
import android.view.WindowManager; import android.os.AsyncTask;
import android.text.TextUtils;
import net.osmand.AndroidUtils;
import net.osmand.aidl.map.ALatLon; import net.osmand.aidl.map.ALatLon;
import net.osmand.aidl.maplayer.AMapLayer; import net.osmand.aidl.maplayer.AMapLayer;
import net.osmand.aidl.maplayer.point.AMapPoint; import net.osmand.aidl.maplayer.point.AMapPoint;
@ -17,8 +20,18 @@ import net.osmand.data.RotatedTileBox;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider; import net.osmand.plus.views.ContextMenuLayer.IContextMenuProvider;
import net.osmand.plus.widgets.tools.CropCircleTransformation;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static net.osmand.aidl.maplayer.point.AMapPoint.POINT_IMAGE_SIZE_PX;
public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider { public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider {
private static int POINT_OUTER_COLOR = 0x88555555; private static int POINT_OUTER_COLOR = 0x88555555;
@ -29,9 +42,12 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
private OsmandMapTileView view; private OsmandMapTileView view;
private Paint pointInnerCircle; private Paint pointInnerCircle;
private Paint pointOuter; private Paint pointOuter;
private Paint bitmapPaint;
private final static float startZoom = 7; private final static float startZoom = 7;
private Paint paintTextIcon; private Paint paintTextIcon;
private Map<String, Bitmap> pointImages = new ConcurrentHashMap<>();
public AidlMapLayer(MapActivity map, AMapLayer aidlLayer) { public AidlMapLayer(MapActivity map, AMapLayer aidlLayer) {
this.map = map; this.map = map;
this.aidlLayer = aidlLayer; this.aidlLayer = aidlLayer;
@ -40,9 +56,6 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
@Override @Override
public void initLayer(OsmandMapTileView view) { public void initLayer(OsmandMapTileView view) {
this.view = view; this.view = view;
DisplayMetrics dm = new DisplayMetrics();
WindowManager wmgr = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
wmgr.getDefaultDisplay().getMetrics(dm);
pointInnerCircle = new Paint(); pointInnerCircle = new Paint();
pointInnerCircle.setColor(view.getApplication().getResources().getColor(R.color.poi_background)); pointInnerCircle.setColor(view.getApplication().getResources().getColor(R.color.poi_background));
@ -60,6 +73,11 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
pointOuter.setColor(POINT_OUTER_COLOR); pointOuter.setColor(POINT_OUTER_COLOR);
pointOuter.setAntiAlias(true); pointOuter.setAntiAlias(true);
pointOuter.setStyle(Paint.Style.FILL_AND_STROKE); pointOuter.setStyle(Paint.Style.FILL_AND_STROKE);
bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapPaint.setDither(true);
bitmapPaint.setFilterBitmap(true);
} }
private int getRadiusPoi(RotatedTileBox tb) { private int getRadiusPoi(RotatedTileBox tb) {
@ -78,24 +96,48 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
} }
@Override @Override
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings nightMode) { public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {
final int r = getRadiusPoi(tileBox); }
@Override
public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {
float density = (float) Math.ceil(tileBox.getDensity());
final int radius = getRadiusPoi(tileBox);
final int maxRadius = (int) (Math.max(radius, POINT_IMAGE_SIZE_PX) + density);
canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY());
paintTextIcon.setTextSize(radius * 3 / 2);
Set<String> imageRequests = new HashSet<>();
List<AMapPoint> points = aidlLayer.getPoints(); List<AMapPoint> points = aidlLayer.getPoints();
for (AMapPoint point : points) { for (AMapPoint point : points) {
ALatLon l = point.getLocation(); ALatLon l = point.getLocation();
if (l != null) { if (l != null) {
int x = (int) tileBox.getPixXFromLatLon(l.getLatitude(), l.getLongitude()); int x = (int) tileBox.getPixXFromLatLon(l.getLatitude(), l.getLongitude());
int y = (int) tileBox.getPixYFromLatLon(l.getLatitude(), l.getLongitude()); int y = (int) tileBox.getPixYFromLatLon(l.getLatitude(), l.getLongitude());
pointInnerCircle.setColor(point.getColor()); if (tileBox.containsPoint(x, y, maxRadius)) {
pointOuter.setColor(POINT_OUTER_COLOR); Map<String, String> params = point.getParams();
paintTextIcon.setColor(PAINT_TEXT_ICON_COLOR); String imageUriStr = params.get(AMapPoint.POINT_IMAGE_URI_PARAM);
canvas.drawCircle(x, y, r + (float)Math.ceil(tileBox.getDensity()), pointOuter); if (!TextUtils.isEmpty(imageUriStr)) {
canvas.drawCircle(x, y, r - (float)Math.ceil(tileBox.getDensity()), pointInnerCircle); Bitmap bitmap = pointImages.get(imageUriStr);
paintTextIcon.setTextSize(r * 3 / 2); if (bitmap == null) {
canvas.drawText(point.getShortName(), x, y + r * 2.5f, paintTextIcon); imageRequests.add(imageUriStr);
} else {
canvas.drawBitmap(bitmap, x - bitmap.getHeight() / 2, y - bitmap.getWidth() / 2, bitmapPaint);
canvas.drawText(point.getShortName(), x, y + maxRadius * 0.9f, paintTextIcon);
}
} else {
pointInnerCircle.setColor(point.getColor());
pointOuter.setColor(POINT_OUTER_COLOR);
canvas.drawCircle(x, y, radius + density, pointOuter);
canvas.drawCircle(x, y, radius - density, pointInnerCircle);
canvas.drawText(point.getShortName(), x, y + radius * 2.5f, paintTextIcon);
}
}
} }
} }
if (imageRequests.size() > 0) {
executeTaskInBackground(new PointImageReaderTask(this), imageRequests.toArray(new String[imageRequests.size()]));
}
} }
@Override @Override
@ -178,4 +220,47 @@ public class AidlMapLayer extends OsmandMapLayer implements IContextMenuProvider
} }
} }
} }
private static class PointImageReaderTask extends AsyncTask<String, Void, Void> {
private WeakReference<AidlMapLayer> layerRef;
private CropCircleTransformation circleTransformation = new CropCircleTransformation();
PointImageReaderTask(AidlMapLayer layer) {
this.layerRef = new WeakReference<>(layer);
}
@Override
protected Void doInBackground(String... imageUriStrs) {
for (String imageUriStr : imageUriStrs) {
Uri fileUri = Uri.parse(imageUriStr);
try {
AidlMapLayer layer = layerRef.get();
if (layer != null) {
try {
InputStream ims = layer.map.getContentResolver().openInputStream(fileUri);
if (ims != null) {
Bitmap bitmap = BitmapFactory.decodeStream(ims);
if (bitmap != null) {
bitmap = circleTransformation.transform(bitmap);
if (bitmap.getWidth() != POINT_IMAGE_SIZE_PX || bitmap.getHeight() != POINT_IMAGE_SIZE_PX) {
bitmap = AndroidUtils.scaleBitmap(bitmap, POINT_IMAGE_SIZE_PX, POINT_IMAGE_SIZE_PX, false);
}
layer.pointImages.put(imageUriStr, bitmap);
}
ims.close();
}
} catch (IOException e) {
// ignore
}
} else {
break;
}
} catch (Throwable e) {
// ignore
}
}
return null;
}
}
} }