Telegram - show images on map
This commit is contained in:
parent
8189ca95d5
commit
0d89b958ce
12 changed files with 285 additions and 30 deletions
|
@ -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>
|
24
OsmAnd-telegram/res/xml/paths.xml
Normal file
24
OsmAnd-telegram/res/xml/paths.xml
Normal 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>
|
|
@ -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() {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue