diff --git a/OsmAnd-telegram/res/drawable/btn_round_border.xml b/OsmAnd-telegram/res/drawable/btn_round_border.xml new file mode 100644 index 0000000000..ae932320c8 --- /dev/null +++ b/OsmAnd-telegram/res/drawable/btn_round_border.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml b/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml new file mode 100644 index 0000000000..957b6db002 --- /dev/null +++ b/OsmAnd-telegram/res/layout/fragment_timeline_tab.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml new file mode 100644 index 0000000000..b10ec52cd8 --- /dev/null +++ b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/menu/bottom_navigation_menu.xml b/OsmAnd-telegram/res/menu/bottom_navigation_menu.xml index ba71c44bc8..d12e0981d5 100644 --- a/OsmAnd-telegram/res/menu/bottom_navigation_menu.xml +++ b/OsmAnd-telegram/res/menu/bottom_navigation_menu.xml @@ -9,4 +9,9 @@ android:id="@+id/action_live_now" android:icon="@drawable/ic_action_live_now" android:title="@string/live_now"/> + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/values-ru/strings.xml b/OsmAnd-telegram/res/values-ru/strings.xml index ce07a813e2..7896e335c8 100644 --- a/OsmAnd-telegram/res/values-ru/strings.xml +++ b/OsmAnd-telegram/res/values-ru/strings.xml @@ -1,5 +1,6 @@ + Включите мониторинг, для сбора данных о перемещении в фоновом режиме. Последнее обновление в Telegram Имя устройства Спрятать diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index 3e134ade9b..c7586500ca 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,4 +1,13 @@ + Monitoring is enabled + Monitoring is disabled + time on the move + Average altitude + Average speed + Open in OsmAnd + End date + Start date + Enable monitoring to collect movement data in the background. Send location as Choose how messages with your location will look like. Map @@ -176,6 +185,7 @@ OsmAnd Location Sharing lets you share your location and see that of others in the OsmAnd.

The app uses Telegram API and you need a Telegram account.]]>
My location Live now + Timeline
diff --git a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl index be010bb062..6f0cb8f65b 100644 --- a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl +++ b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlCallback.aidl @@ -1,9 +1,14 @@ package net.osmand.aidl; import net.osmand.aidl.search.SearchResult; +import net.osmand.aidl.gpx.AGpxBitmap; interface IOsmAndAidlCallback { void onSearchComplete(in List resultSet); void onUpdate(); + + void onAppInitialized(); + + void onGpxBitmapCreated(in AGpxBitmap bitmap); } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl index d7ebb4492d..59fd213ab6 100644 --- a/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl +++ b/OsmAnd-telegram/src/net/osmand/aidl/IOsmAndAidlInterface.aidl @@ -55,6 +55,9 @@ import net.osmand.aidl.maplayer.point.ShowMapPointParams; import net.osmand.aidl.navdrawer.SetNavDrawerItemsParams; +import net.osmand.aidl.navdrawer.NavDrawerFooterParams; +import net.osmand.aidl.navdrawer.NavDrawerHeaderParams; + import net.osmand.aidl.navigation.PauseNavigationParams; import net.osmand.aidl.navigation.ResumeNavigationParams; import net.osmand.aidl.navigation.StopNavigationParams; @@ -67,6 +70,16 @@ import net.osmand.aidl.search.SearchResult; import net.osmand.aidl.search.SearchParams; import net.osmand.aidl.navigation.NavigateSearchParams; +import net.osmand.aidl.customization.SetWidgetsParams; +import net.osmand.aidl.customization.OsmandSettingsParams; + +import net.osmand.aidl.gpx.AGpxFile; +import net.osmand.aidl.gpx.AGpxFileDetails; +import net.osmand.aidl.gpx.CreateGpxBitmapParams; +import net.osmand.aidl.tiles.ASqliteDbFile; + +import net.osmand.aidl.plugins.PluginParams; + // NOTE: Add new methods at the end of file!!! interface IOsmAndAidlInterface { @@ -133,4 +146,34 @@ interface IOsmAndAidlInterface { long registerForUpdates(in long updateTimeMS, IOsmAndAidlCallback callback); boolean unregisterFromUpdates(in long callbackId); + + boolean setNavDrawerLogo(in String imageUri); + + boolean setEnabledIds(in List ids); + boolean setDisabledIds(in List ids); + boolean setEnabledPatterns(in List patterns); + boolean setDisabledPatterns(in List patterns); + + boolean regWidgetVisibility(in SetWidgetsParams params); + boolean regWidgetAvailability(in SetWidgetsParams params); + + boolean customizeOsmandSettings(in OsmandSettingsParams params); + + boolean getImportedGpx(out List files); + + boolean getSqliteDbFiles(out List files); + boolean getActiveSqliteDbFiles(out List files); + boolean showSqliteDbFile(String fileName); + boolean hideSqliteDbFile(String fileName); + + boolean setNavDrawerLogoWithParams(in NavDrawerHeaderParams params); + boolean setNavDrawerFooterWithParams(in NavDrawerFooterParams params); + + boolean restoreOsmand(); + + boolean changePluginState(in PluginParams params); + + boolean registerForOsmandInitListener(in IOsmAndAidlCallback callback); + + boolean getBitmapForGpx(in CreateGpxBitmapParams file, IOsmAndAidlCallback callback); } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl new file mode 100644 index 0000000000..770070bb46 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.customization; + +parcelable OsmandSettingsParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java new file mode 100644 index 0000000000..bff8c68018 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/OsmandSettingsParams.java @@ -0,0 +1,60 @@ +package net.osmand.aidl.customization; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class OsmandSettingsParams implements Parcelable { + + private String sharedPreferencesName; + private Bundle bundle; + + public OsmandSettingsParams(@NonNull String sharedPreferencesName, @Nullable Bundle bundle) { + this.sharedPreferencesName = sharedPreferencesName; + this.bundle = bundle; + } + + public OsmandSettingsParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public OsmandSettingsParams createFromParcel(Parcel in) { + return new OsmandSettingsParams(in); + } + + @Override + public OsmandSettingsParams[] newArray(int size) { + return new OsmandSettingsParams[size]; + } + }; + + public String getSharedPreferencesName() { + return sharedPreferencesName; + } + + public Bundle getBundle() { + return bundle; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(sharedPreferencesName); + out.writeBundle(bundle); + } + + @SuppressLint("ParcelClassLoader") + private void readFromParcel(Parcel in) { + sharedPreferencesName = in.readString(); + bundle = in.readBundle(); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl new file mode 100644 index 0000000000..235a4abe51 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.customization; + +parcelable SetWidgetsParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java new file mode 100644 index 0000000000..d9343b920e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/customization/SetWidgetsParams.java @@ -0,0 +1,93 @@ +package net.osmand.aidl.customization; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class SetWidgetsParams implements Parcelable { + + private String widgetKey; + private List appModesKeys; + + public SetWidgetsParams(String widgetKey, @Nullable List appModesKeys) { + this.widgetKey = widgetKey; + this.appModesKeys = appModesKeys; + } + + public SetWidgetsParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new Creator() { + @Override + public SetWidgetsParams createFromParcel(Parcel in) { + return new SetWidgetsParams(in); + } + + @Override + public SetWidgetsParams[] newArray(int size) { + return new SetWidgetsParams[size]; + } + }; + + public String getWidgetKey() { + return widgetKey; + } + + public List getAppModesKeys() { + return appModesKeys; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(widgetKey); + writeStringList(out, appModesKeys); + } + + private void readFromParcel(Parcel in) { + widgetKey = in.readString(); + appModesKeys = readStringList(in); + } + + @Override + public int describeContents() { + return 0; + } + + private void writeStringList(Parcel out, List val) { + if (val == null) { + out.writeInt(-1); + return; + } + int N = val.size(); + int i = 0; + out.writeInt(N); + while (i < N) { + out.writeString(val.get(i)); + i++; + } + } + + private List readStringList(Parcel in) { + List list = new ArrayList<>(); + int M = list.size(); + int N = in.readInt(); + if (N == -1) { + return null; + } + int i = 0; + for (; i < M && i < N; i++) { + list.set(i, in.readString()); + } + for (; i < N; i++) { + list.add(in.readString()); + } + for (; i < M; i++) { + list.remove(N); + } + return list; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl new file mode 100644 index 0000000000..128f5e6b94 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.aidl @@ -0,0 +1,4 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxBitmap; + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java new file mode 100644 index 0000000000..2f423e126e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxBitmap.java @@ -0,0 +1,50 @@ +package net.osmand.aidl.gpx; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public class AGpxBitmap implements Parcelable { + + private Bitmap bitmap; + + public AGpxBitmap(@NonNull Bitmap bitmap) { + this.bitmap = bitmap; + } + + public AGpxBitmap(Parcel in) { + readFromParcel(in); + } + + public Bitmap getBitmap() { + return bitmap; + } + + public void setBitmap(Bitmap bitmap) { + this.bitmap = bitmap; + } + + public static final Creator CREATOR = new + Creator() { + public AGpxBitmap createFromParcel(Parcel in) { + return new AGpxBitmap(in); + } + + public AGpxBitmap[] newArray(int size) { + return new AGpxBitmap[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(bitmap, flags); + } + + private void readFromParcel(Parcel in) { + bitmap = in.readParcelable(Bitmap.class.getClassLoader()); + } + + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl new file mode 100644 index 0000000000..413d34a571 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxFile; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java new file mode 100644 index 0000000000..9572057fdc --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFile.java @@ -0,0 +1,89 @@ +package net.osmand.aidl.gpx; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class AGpxFile implements Parcelable { + + private String fileName; + private long modifiedTime; + private long fileSize; + private boolean active; + private AGpxFileDetails details; + + public AGpxFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active, @Nullable AGpxFileDetails details) { + this.fileName = fileName; + this.modifiedTime = modifiedTime; + this.fileSize = fileSize; + this.active = active; + this.details = details; + } + + public AGpxFile(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public AGpxFile createFromParcel(Parcel in) { + return new AGpxFile(in); + } + + public AGpxFile[] newArray(int size) { + return new AGpxFile[size]; + } + }; + + public String getFileName() { + return fileName; + } + + public long getModifiedTime() { + return modifiedTime; + } + + public long getFileSize() { + return fileSize; + } + + public boolean isActive() { + return active; + } + + public AGpxFileDetails getDetails() { + return details; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(fileName); + out.writeLong(modifiedTime); + out.writeLong(fileSize); + out.writeByte((byte) (active ? 1 : 0)); + + out.writeByte((byte) (details != null ? 1 : 0)); + if (details != null) { + out.writeParcelable(details, flags); + } + } + + private void readFromParcel(Parcel in) { + fileName = in.readString(); + modifiedTime = in.readLong(); + fileSize = in.readLong(); + active = in.readByte() != 0; + + boolean hasDetails= in.readByte() != 0; + if (hasDetails) { + details = in.readParcelable(AGpxFileDetails.class.getClassLoader()); + } else { + details = null; + } + } + + public int describeContents() { + return 0; + } +} + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl new file mode 100644 index 0000000000..1e2cdec2b1 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxFileDetails; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java new file mode 100644 index 0000000000..3fe755135e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/AGpxFileDetails.java @@ -0,0 +1,196 @@ +package net.osmand.aidl.gpx; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class AGpxFileDetails implements Parcelable { + + private float totalDistance = 0; + private int totalTracks = 0; + private long startTime = Long.MAX_VALUE; + private long endTime = Long.MIN_VALUE; + private long timeSpan = 0; + private long timeMoving = 0; + private float totalDistanceMoving = 0; + + private double diffElevationUp = 0; + private double diffElevationDown = 0; + private double avgElevation = 0; + private double minElevation = 99999; + private double maxElevation = -100; + + private float minSpeed = Float.MAX_VALUE; + private float maxSpeed = 0; + private float avgSpeed; + + private int points; + private int wptPoints = 0; + + private List wptCategoryNames = new ArrayList<>(); + + public AGpxFileDetails(float totalDistance, int totalTracks, + long startTime, long endTime, + long timeSpan, long timeMoving, float totalDistanceMoving, + double diffElevationUp, double diffElevationDown, + double avgElevation, double minElevation, double maxElevation, + float minSpeed, float maxSpeed, float avgSpeed, + int points, int wptPoints, Set wptCategoryNames) { + this.totalDistance = totalDistance; + this.totalTracks = totalTracks; + this.startTime = startTime; + this.endTime = endTime; + this.timeSpan = timeSpan; + this.timeMoving = timeMoving; + this.totalDistanceMoving = totalDistanceMoving; + this.diffElevationUp = diffElevationUp; + this.diffElevationDown = diffElevationDown; + this.avgElevation = avgElevation; + this.minElevation = minElevation; + this.maxElevation = maxElevation; + this.minSpeed = minSpeed; + this.maxSpeed = maxSpeed; + this.avgSpeed = avgSpeed; + this.points = points; + this.wptPoints = wptPoints; + if (wptCategoryNames != null) { + this.wptCategoryNames = new ArrayList<>(wptCategoryNames); + } + } + + public AGpxFileDetails(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public AGpxFileDetails createFromParcel(Parcel in) { + return new AGpxFileDetails(in); + } + + public AGpxFileDetails[] newArray(int size) { + return new AGpxFileDetails[size]; + } + }; + + public float getTotalDistance() { + return totalDistance; + } + + public int getTotalTracks() { + return totalTracks; + } + + public long getStartTime() { + return startTime; + } + + public long getEndTime() { + return endTime; + } + + public long getTimeSpan() { + return timeSpan; + } + + public long getTimeMoving() { + return timeMoving; + } + + public float getTotalDistanceMoving() { + return totalDistanceMoving; + } + + public double getDiffElevationUp() { + return diffElevationUp; + } + + public double getDiffElevationDown() { + return diffElevationDown; + } + + public double getAvgElevation() { + return avgElevation; + } + + public double getMinElevation() { + return minElevation; + } + + public double getMaxElevation() { + return maxElevation; + } + + public float getMinSpeed() { + return minSpeed; + } + + public float getMaxSpeed() { + return maxSpeed; + } + + public float getAvgSpeed() { + return avgSpeed; + } + + public int getPoints() { + return points; + } + + public int getWptPoints() { + return wptPoints; + } + + public List getWptCategoryNames() { + return wptCategoryNames; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeFloat(totalDistance); + out.writeInt(totalTracks); + out.writeLong(startTime); + out.writeLong(endTime); + out.writeLong(timeSpan); + out.writeLong(timeMoving); + out.writeFloat(totalDistanceMoving); + out.writeDouble(diffElevationUp); + out.writeDouble(diffElevationDown); + out.writeDouble(avgElevation); + out.writeDouble(minElevation); + out.writeDouble(maxElevation); + out.writeFloat(minSpeed); + out.writeFloat(maxSpeed); + out.writeFloat(avgSpeed); + out.writeInt(points); + out.writeInt(wptPoints); + out.writeStringList(wptCategoryNames); + } + + private void readFromParcel(Parcel in) { + totalDistance = in.readFloat(); + totalTracks = in.readInt(); + startTime = in.readLong(); + endTime = in.readLong(); + timeSpan = in.readLong(); + timeMoving = in.readLong(); + totalDistanceMoving = in.readFloat(); + diffElevationUp = in.readDouble(); + diffElevationDown = in.readDouble(); + avgElevation = in.readDouble(); + minElevation = in.readDouble(); + maxElevation = in.readDouble(); + minSpeed = in.readFloat(); + maxSpeed = in.readFloat(); + avgSpeed = in.readFloat(); + points = in.readInt(); + wptPoints = in.readInt(); + in.readStringList(wptCategoryNames); + } + + public int describeContents() { + return 0; + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl new file mode 100644 index 0000000000..b02d5c6e70 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable CreateGpxBitmapParams; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java new file mode 100644 index 0000000000..b6af1d354c --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java @@ -0,0 +1,101 @@ +package net.osmand.aidl.gpx; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; + +public class CreateGpxBitmapParams implements Parcelable { + + private File gpxFile; + private Uri gpxUri; + private float density; + private int widthPixels; + private int heightPixels; + private int color; //ARGB color int + + public CreateGpxBitmapParams(File gpxFile, float density, int widthPixels, int heightPixels, int color) { + this.gpxFile = gpxFile; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Uri gpxUri, float density, int widthPixels, int heightPixels, int color) { + this.gpxUri = gpxUri; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public CreateGpxBitmapParams createFromParcel(Parcel in) { + return new CreateGpxBitmapParams(in); + } + + public CreateGpxBitmapParams[] newArray(int size) { + return new CreateGpxBitmapParams[size]; + } + }; + + public File getGpxFile() { + return gpxFile; + } + + public Uri getGpxUri() { + return gpxUri; + } + + public int getWidthPixels() { + return widthPixels; + } + + public int getHeightPixels() { + return heightPixels; + } + + public float getDensity() { + return density; + } + + public int getColor() { + return color; + } + + public void writeToParcel(Parcel out, int flags) { + if (gpxFile != null) { + out.writeString(gpxFile.getAbsolutePath()); + } else { + out.writeString(null); + } + out.writeParcelable(gpxUri, flags); + out.writeFloat(density); + out.writeInt(widthPixels); + out.writeInt(heightPixels); + out.writeInt(color); + } + + private void readFromParcel(Parcel in) { + String gpxAbsolutePath = in.readString(); + if (gpxAbsolutePath != null) { + gpxFile = new File(gpxAbsolutePath); + } + gpxUri = in.readParcelable(Uri.class.getClassLoader()); + density = in.readFloat(); + widthPixels = in.readInt(); + heightPixels = in.readInt(); + color = in.readInt(); + } + + public int describeContents() { + return 0; + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl new file mode 100644 index 0000000000..fc1271a8ca --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.navdrawer; + +parcelable NavDrawerFooterParams; diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java new file mode 100644 index 0000000000..9bfd70193f --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerFooterParams.java @@ -0,0 +1,68 @@ +package net.osmand.aidl.navdrawer; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class NavDrawerFooterParams implements Parcelable { + + @NonNull + private String packageName; + @Nullable + private String intent; + @Nullable + private String appName; + + @NonNull + public String getPackageName() { + return packageName; + } + + @Nullable + public String getIntent() { + return intent; + } + + @Nullable + public String getAppName() { + return appName; + } + + public NavDrawerFooterParams(@NonNull String packageName, @Nullable String intent, + @Nullable String appName) { + this.packageName = packageName; + this.intent = intent; + this.appName = appName; + } + + protected NavDrawerFooterParams(Parcel in) { + packageName = in.readString(); + intent = in.readString(); + appName = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeString(intent); + dest.writeString(appName); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NavDrawerFooterParams createFromParcel(Parcel in) { + return new NavDrawerFooterParams(in); + } + + @Override + public NavDrawerFooterParams[] newArray(int size) { + return new NavDrawerFooterParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl new file mode 100644 index 0000000000..c230824841 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.navdrawer; + +parcelable NavDrawerHeaderParams; diff --git a/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java new file mode 100644 index 0000000000..c88950fd10 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/navdrawer/NavDrawerHeaderParams.java @@ -0,0 +1,68 @@ +package net.osmand.aidl.navdrawer; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +public class NavDrawerHeaderParams implements Parcelable { + + @NonNull + private String imageUri; + @NonNull + private String packageName; + @Nullable + private String intent; + + @NonNull + public String getImageUri() { + return imageUri; + } + + @NonNull + public String getPackageName() { + return packageName; + } + + @Nullable + public String getIntent() { + return intent; + } + + public NavDrawerHeaderParams(@NonNull String imageUri, @NonNull String packageName, + @Nullable String intent) { + this.imageUri = imageUri; + this.packageName = packageName; + this.intent = intent; + } + + public NavDrawerHeaderParams(Parcel in) { + imageUri = in.readString(); + packageName = in.readString(); + intent = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(imageUri); + dest.writeString(packageName); + dest.writeString(intent); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public NavDrawerHeaderParams createFromParcel(Parcel in) { + return new NavDrawerHeaderParams(in); + } + + @Override + public NavDrawerHeaderParams[] newArray(int size) { + return new NavDrawerHeaderParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl new file mode 100644 index 0000000000..beff693f5a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.aidl @@ -0,0 +1,5 @@ +// PluginParams.aidl +package net.osmand.aidl.plugins; + +parcelable PluginParams; + diff --git a/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java new file mode 100644 index 0000000000..028bd8676a --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/plugins/PluginParams.java @@ -0,0 +1,51 @@ +package net.osmand.aidl.plugins; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PluginParams implements Parcelable { + + private String pluginId; + private int newState; //0- off, 1 - on + + public PluginParams(String pluginId, int newState) { + this.pluginId = pluginId; + this.newState = newState; + } + + public String getPluginId() { + return pluginId; + } + + public int getNewState() { + return newState; + } + + protected PluginParams(Parcel in) { + pluginId = in.readString(); + newState = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(pluginId); + dest.writeInt(newState); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public PluginParams createFromParcel(Parcel in) { + return new PluginParams(in); + } + + @Override + public PluginParams[] newArray(int size) { + return new PluginParams[size]; + } + }; +} diff --git a/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl new file mode 100644 index 0000000000..319cfd5035 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.tiles; + +parcelable ASqliteDbFile; \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java new file mode 100644 index 0000000000..ac6f5e55d2 --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/aidl/tiles/ASqliteDbFile.java @@ -0,0 +1,69 @@ +package net.osmand.aidl.tiles; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public class ASqliteDbFile implements Parcelable { + + private String fileName; + private long modifiedTime; + private long fileSize; + private boolean active; + + public ASqliteDbFile(@NonNull String fileName, long modifiedTime, long fileSize, boolean active) { + this.fileName = fileName; + this.modifiedTime = modifiedTime; + this.fileSize = fileSize; + this.active = active; + } + + public ASqliteDbFile(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public ASqliteDbFile createFromParcel(Parcel in) { + return new ASqliteDbFile(in); + } + + public ASqliteDbFile[] newArray(int size) { + return new ASqliteDbFile[size]; + } + }; + + public String getFileName() { + return fileName; + } + + public long getModifiedTime() { + return modifiedTime; + } + + public long getFileSize() { + return fileSize; + } + + public boolean isActive() { + return active; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(fileName); + out.writeLong(modifiedTime); + out.writeLong(fileSize); + out.writeByte((byte) (active ? 1 : 0)); + } + + private void readFromParcel(Parcel in) { + fileName = in.readString(); + modifiedTime = in.readLong(); + fileSize = in.readLong(); + active = in.readByte() != 0; + } + + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt index a08e5763ee..8b9cb02a60 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramSettings.kt @@ -79,6 +79,8 @@ private const val SHARE_CHATS_INFO_KEY = "share_chats_info" private const val BATTERY_OPTIMISATION_ASKED = "battery_optimisation_asked" +private const val MONITORING_ENABLED = "monitoring_enabled" + private const val SHARING_INITIALIZATION_TIME = 60 * 2L // 2 minutes private const val GPS_UPDATE_EXPIRED_TIME = 60 * 3L // 3 minutes @@ -111,6 +113,8 @@ class TelegramSettings(private val app: TelegramApplication) { var batteryOptimisationAsked = false + var monitoringEnabled = false + init { updatePrefs() read() @@ -435,6 +439,8 @@ class TelegramSettings(private val app: TelegramApplication) { edit.putBoolean(BATTERY_OPTIMISATION_ASKED, batteryOptimisationAsked) + edit.putBoolean(MONITORING_ENABLED, monitoringEnabled) + val jArray = convertShareChatsInfoToJson() if (jArray != null) { edit.putString(SHARE_CHATS_INFO_KEY, jArray.toString()) @@ -491,6 +497,8 @@ class TelegramSettings(private val app: TelegramApplication) { ) batteryOptimisationAsked = prefs.getBoolean(BATTERY_OPTIMISATION_ASKED,false) + + monitoringEnabled = prefs.getBoolean(MONITORING_ENABLED,false) } private fun convertShareDevicesToJson():JSONObject?{ diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt index edac62b427..621b2cb65f 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/OsmandAidlHelper.kt @@ -75,6 +75,12 @@ class OsmandAidlHelper(private val app: TelegramApplication) { fun onSearchComplete(resultSet: List) } + private var gpxBitmapCreatedListener: GpxBitmapCreatedListener? = null + + interface GpxBitmapCreatedListener { + fun onGpxBitmapCreated(bitmap: AGpxBitmap) + } + private val mIOsmAndAidlCallback = object : IOsmAndAidlCallback.Stub() { @Throws(RemoteException::class) @@ -90,12 +96,26 @@ class OsmandAidlHelper(private val app: TelegramApplication) { mUpdatesListener!!.update() } } + + override fun onAppInitialized() { + + } + + override fun onGpxBitmapCreated(bitmap: AGpxBitmap) { + if (gpxBitmapCreatedListener != null) { + gpxBitmapCreatedListener!!.onGpxBitmapCreated(bitmap) + } + } } fun setSearchCompleteListener(mSearchCompleteListener: SearchCompleteListener) { this.mSearchCompleteListener = mSearchCompleteListener } + fun setGpxBitmapCreatedListener(gpxBitmapCreatedListener: GpxBitmapCreatedListener) { + this.gpxBitmapCreatedListener = gpxBitmapCreatedListener + } + private var mUpdatesListener: UpdatesListener? = null interface UpdatesListener { @@ -1059,4 +1079,16 @@ class OsmandAidlHelper(private val app: TelegramApplication) { } return false } + + fun getBitmapForGpx(gpxUri: Uri, density: Float, widthPixels: Int, heightPixels: Int, color: Int): Boolean { + if (mIOsmAndAidlInterface != null) { + try { + app.grantUriPermission(app.settings.appToConnectPackage, gpxUri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + return mIOsmAndAidlInterface!!.getBitmapForGpx(CreateGpxBitmapParams(gpxUri, density, widthPixels, heightPixels, color), mIOsmAndAidlCallback) + } catch (e: RemoteException) { + e.printStackTrace() + } + } + return false + } } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java b/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java index 70d91ccc96..b3c1faf20d 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/SavingTracksDbHelper.java @@ -7,7 +7,6 @@ import android.os.AsyncTask; import net.osmand.PlatformUtil; import net.osmand.telegram.TelegramApplication; -import net.osmand.telegram.ui.LiveNowTabFragment; import net.osmand.telegram.utils.GPXUtilities; import net.osmand.telegram.utils.GPXUtilities.GPXFile; import net.osmand.telegram.utils.GPXUtilities.Track; @@ -19,7 +18,6 @@ import org.drinkless.td.libcore.telegram.TdApi; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -50,7 +48,7 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { + TRACK_COL_ALTITUDE + " double, " + TRACK_COL_SPEED + " double, " //$NON-NLS-1$ //$NON-NLS-2$ + TRACK_COL_HDOP + " double, " + TRACK_COL_DATE + " long, " + TRACK_COL_TEXT_INFO + " int )"; - public final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class); + private final static Log log = PlatformUtil.getLog(SavingTracksDbHelper.class); private final TelegramApplication app; @@ -110,23 +108,32 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { } } - public void saveAsyncUserDataToGpx(LiveNowTabFragment fragment, File dir, int userId, long interval) { - GPXFile gpxFile = app.getSavingTracksDbHelper().collectRecordedDataForUser(userId, interval); + public void saveUserDataToGpx(SaveGpxListener listener, File dir, int userId, long chatId, long start, long end) { + GPXFile gpxFile = collectRecordedDataForUserAndChat(userId, chatId, start, end); if (gpxFile != null && !gpxFile.isEmpty()) { - SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(fragment, gpxFile, dir, userId); + SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, userId); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + + public void saveGpx(SaveGpxListener listener, File dir, GPXFile gpxFile) { + if (gpxFile != null && !gpxFile.isEmpty()) { + SaveGPXTrackToFileTask task = new SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } private void updateLocationMessage(TdApi.Message message) { + log.debug(message); TdApi.MessageContent content = message.content; + int senderId = app.getTelegramHelper().getSenderMessageId(message); if (content instanceof TdApi.MessageLocation) { long lastTextMessageUpdate = getLastTextTrackPointTimeForUser(message.senderUserId); long currentTime = System.currentTimeMillis(); if (lastTextMessageUpdate == 0 || currentTime - lastTextMessageUpdate < 10 * 1000) { - log.debug("Add map message" + message.senderUserId); + log.debug("Add map message " + message.senderUserId); TdApi.MessageLocation messageLocation = (TdApi.MessageLocation) content; - insertData(message.senderUserId, message.chatId, messageLocation.location.latitude, + insertData(senderId, message.chatId, messageLocation.location.latitude, messageLocation.location.longitude, 0.0, 0.0, 0.0, Math.max(message.date, message.editDate), 0); } else { @@ -135,7 +142,7 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { } else if (content instanceof TelegramHelper.MessageLocation) { log.debug("Add text message " + message.senderUserId); TelegramHelper.MessageLocation messageLocation = (TelegramHelper.MessageLocation) content; - insertData(message.senderUserId, message.chatId, messageLocation.getLat(), messageLocation.getLon(), + insertData(senderId, message.chatId, messageLocation.getLat(), messageLocation.getLon(), messageLocation.getAltitude(), messageLocation.getSpeed(), messageLocation.getHdop(), messageLocation.getLastUpdated() * 1000L, 1); } @@ -181,12 +188,12 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { return res; } - private GPXFile collectRecordedDataForUser(int userId, long interval) { + public GPXFile collectRecordedDataForUserAndChat(int userId, long chatId, long start, long end) { GPXFile gpxFile = null; SQLiteDatabase db = getReadableDatabase(); if (db != null && db.isOpen()) { try { - gpxFile = collectDBTracksForUser(db, userId, interval); + gpxFile = collectDBTracksForUser(db, userId, chatId, start, end); } finally { db.close(); } @@ -194,21 +201,52 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { return gpxFile; } - private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long interval) { - Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ - + TRACK_COL_SPEED + "," + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + - " WHERE " + TRACK_COL_USER_ID + " = ? ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)}); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + public GPXFile collectRecordedDataForUser(int userId, long chatId, long start, long end) { + GPXFile gpxFile = null; + SQLiteDatabase db = getReadableDatabase(); + if (db != null && db.isOpen()) { + try { + if (chatId == 0) { + gpxFile = collectDBTracksForUser(db, userId, start, end); + } else { + gpxFile = collectDBTracksForUser(db, userId, chatId, start, end); + } + } finally { + db.close(); + } + } + return gpxFile; + } + + public ArrayList collectRecordedDataForUsers(long start, long end, ArrayList ignoredUsersIds) { + ArrayList data = new ArrayList<>(); + SQLiteDatabase db = getReadableDatabase(); + if (db != null && db.isOpen()) { + try { + collectDBTracksForUsers(db, data, start, end, ignoredUsersIds); + } finally { + db.close(); + } + } + return data; + } + + private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long chatId, long start, long end) { + Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + "," + + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?" + + " AND " + TRACK_COL_CHAT_ID + " = ?" + " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end + + " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId), String.valueOf(chatId)}); + GPXFile gpxFile = new GPXFile(); + gpxFile.chatId = chatId; + gpxFile.userId = userId; long previousTime = 0; TrkSegment segment = null; Track track = null; if (query.moveToFirst()) { do { long time = query.getLong(7); - long curTime = System.currentTimeMillis(); - if (curTime - time > interval) { - continue; - } WptPt pt = new WptPt(); pt.userId = query.getInt(0); pt.chatId = query.getLong(1); @@ -244,19 +282,135 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { return gpxFile; } + private GPXFile collectDBTracksForUser(SQLiteDatabase db, int userId, long start, long end) { + Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + "," + + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_USER_ID + " = ?" + + " AND " + TRACK_COL_DATE + " BETWEEN " + start + " AND " + end + + " ORDER BY " + TRACK_COL_DATE + " ASC ", new String[]{String.valueOf(userId)}); + + GPXFile gpxFile = new GPXFile(); + gpxFile.userId = userId; + long previousTime = 0; + TrkSegment segment = null; + Track track = null; + if (query.moveToFirst()) { + do { + long time = query.getLong(7); + WptPt pt = new WptPt(); + pt.userId = query.getInt(0); + pt.chatId = query.getLong(1); + pt.lat = query.getDouble(2); + pt.lon = query.getDouble(3); + pt.ele = query.getDouble(4); + pt.speed = query.getDouble(5); + pt.hdop = query.getDouble(6); + pt.time = time; + long currentInterval = Math.abs(time - previousTime); + + if (track != null) { + if (currentInterval < 30 * 60 * 1000) { + // 30 minute - same segment + segment.points.add(pt); + } else { + segment = new TrkSegment(); + segment.points.add(pt); + track.segments.add(segment); + } + } else { + track = new Track(); + segment = new TrkSegment(); + track.segments.add(segment); + segment.points.add(pt); + + gpxFile.tracks.add(track); + } + previousTime = time; + } while (query.moveToNext()); + } + query.close(); + return gpxFile; + } + + private void collectDBTracksForUsers(SQLiteDatabase db, ArrayList dataTracks, long start, long end, ArrayList ignoredUsersIds) { + Cursor query = db.rawQuery("SELECT " + TRACK_COL_USER_ID + "," + TRACK_COL_CHAT_ID + "," + + TRACK_COL_LAT + "," + TRACK_COL_LON + "," + TRACK_COL_ALTITUDE + "," + TRACK_COL_SPEED + "," + + TRACK_COL_HDOP + "," + TRACK_COL_DATE + " FROM " + TRACK_NAME + " WHERE " + TRACK_COL_DATE + + " BETWEEN " + start + " AND " + end + " ORDER BY " + TRACK_COL_USER_ID + " ASC, " + + TRACK_COL_CHAT_ID + " ASC, " + TRACK_COL_DATE + " ASC ", null); + + long previousTime = 0; + long previousChatId = 0; + int previousUserId = 0; + TrkSegment segment = null; + Track track = null; + GPXFile gpx = new GPXFile(); + if (query.moveToFirst()) { + do { + int userId = query.getInt(0); + if (ignoredUsersIds.contains(userId)) { + continue; + } + int chatId = query.getInt(1); + long time = query.getLong(7); + if (previousUserId != userId || previousChatId != chatId) { + gpx = new GPXFile(); + gpx.chatId = chatId; + gpx.userId = userId; + previousTime = 0; + track = null; + segment = null; + dataTracks.add(gpx); + } + + WptPt pt = new WptPt(); + pt.userId = userId; + pt.chatId = chatId; + pt.lat = query.getDouble(2); + pt.lon = query.getDouble(3); + pt.ele = query.getDouble(4); + pt.speed = query.getDouble(5); + pt.hdop = query.getDouble(6); + pt.time = time; + long currentInterval = Math.abs(time - previousTime); + if (track != null) { + if (currentInterval < 30 * 60 * 1000) { + // 30 minute - same segment + segment.points.add(pt); + } else { + segment = new TrkSegment(); + segment.points.add(pt); + track.segments.add(segment); + } + } else { + track = new Track(); + segment = new TrkSegment(); + track.segments.add(segment); + segment.points.add(pt); + + gpx.tracks.add(track); + } + previousTime = time; + previousUserId = userId; + previousChatId = chatId; + } while (query.moveToNext()); + } + query.close(); + } + private static class SaveGPXTrackToFileTask extends AsyncTask> { private TelegramApplication app; - private WeakReference fragmentRef; + private SaveGpxListener listener; private final GPXFile gpxFile; private File dir; private int userId; - SaveGPXTrackToFileTask(LiveNowTabFragment fragment, GPXFile gpxFile, File dir, int userId) { + SaveGPXTrackToFileTask(TelegramApplication app, SaveGpxListener listener, GPXFile gpxFile, File dir, int userId) { this.gpxFile = gpxFile; - this.fragmentRef = new WeakReference<>(fragment); - this.app = (TelegramApplication) fragment.getActivity().getApplication(); + this.listener = listener; + this.app = app; this.dir = dir; this.userId = userId; } @@ -300,12 +454,21 @@ public class SavingTracksDbHelper extends SQLiteOpenHelper { @Override protected void onPostExecute(List warnings) { - if (warnings != null && warnings.isEmpty()) { - LiveNowTabFragment fragment = fragmentRef.get(); - if (fragment != null && fragment.isResumed()) { - fragment.shareGpx(gpxFile.path); + if (listener != null) { + if (warnings != null && warnings.isEmpty()) { + listener.onSavingGpxFinish(gpxFile.path); + } else { + listener.onSavingGpxError(warnings); } } } } + + + public interface SaveGpxListener { + + void onSavingGpxFinish(String path); + + void onSavingGpxError(List warnings); + } } \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt index af654a081c..be9c2886a4 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramHelper.kt @@ -391,6 +391,15 @@ class TelegramHelper private constructor() { fun isBot(userId: Int) = users[userId]?.type is TdApi.UserTypeBot + fun getSenderMessageId(message: TdApi.Message): Int { + val forwardInfo = message.forwardInfo + return if (forwardInfo != null && forwardInfo is TdApi.MessageForwardedFromUser) { + forwardInfo.senderUserId + } else { + message.senderUserId + } + } + fun startLiveMessagesUpdates(interval: Long) { stopLiveMessagesUpdates() @@ -738,10 +747,13 @@ class TelegramHelper private constructor() { } } else { removeOldMessages(message, fromBot, viaBot) - val oldMessage = usersLocationMessages.values.firstOrNull { it.senderUserId == message.senderUserId && !fromBot && !viaBot } - if (oldMessage == null || (Math.max(message.editDate, message.date) > Math.max(oldMessage.editDate, oldMessage.date))) { + val oldMessage = usersLocationMessages.values.firstOrNull { getSenderMessageId(it) == getSenderMessageId(message) && !fromBot && !viaBot } + val hasNewerMessage = oldMessage != null && (Math.max(message.editDate, message.date) < Math.max(oldMessage.editDate, oldMessage.date)) + if (!hasNewerMessage) { usersLocationMessages[message.id] = message - incomingMessagesListeners.forEach { + } + incomingMessagesListeners.forEach { + if (!hasNewerMessage || it is SavingTracksDbHelper) { it.onReceiveChatLocationMessages(message.chatId, message) } } @@ -754,7 +766,7 @@ class TelegramHelper private constructor() { while (iterator.hasNext()) { val message = iterator.next().value if (newMessage.chatId == message.chatId) { - val sameSender = newMessage.senderUserId == message.senderUserId + val sameSender = getSenderMessageId(newMessage) == getSenderMessageId(message) val viaSameBot = newMessage.viaBotUserId == message.viaBotUserId if (fromBot || viaBot) { if ((fromBot && sameSender) || (viaBot && viaSameBot)) { @@ -1075,23 +1087,6 @@ class TelegramHelper private constructor() { return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true) } - /** - * @chatId Id of the chat - * @message Text of the message - */ - fun sendTextMessage(chatId: Long, message: String): Boolean { - // initialize reply markup just for testing - //val row = arrayOf(TdApi.InlineKeyboardButton("https://telegram.org?1", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?2", TdApi.InlineKeyboardButtonTypeUrl()), TdApi.InlineKeyboardButton("https://telegram.org?3", TdApi.InlineKeyboardButtonTypeUrl())) - //val replyMarkup = TdApi.ReplyMarkupInlineKeyboard(arrayOf(row, row, row)) - - if (haveAuthorization) { - val content = TdApi.InputMessageText(TdApi.FormattedText(message, null), false, true) - client?.send(TdApi.SendMessage(chatId, 0, false, true, null, content), defaultHandler) - return true - } - return false - } - fun logout(): Boolean { return if (libraryLoaded) { haveAuthorization = false @@ -1769,4 +1764,4 @@ class TelegramHelper private constructor() { }// result is already received through UpdateAuthorizationState, nothing to do } } -} +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt index a1670f1912..2a4f1b1436 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/helpers/TelegramUiHelper.kt @@ -8,6 +8,7 @@ import net.osmand.telegram.R import net.osmand.telegram.TelegramApplication import net.osmand.telegram.helpers.TelegramHelper.MessageOsmAndBotLocation import net.osmand.telegram.helpers.TelegramHelper.MessageUserTextLocation +import net.osmand.telegram.utils.GPXUtilities import org.drinkless.td.libcore.telegram.TdApi object TelegramUiHelper { @@ -129,6 +130,10 @@ object TelegramUiHelper { } } + fun gpxToChatItem(helper: TelegramHelper, gpx: GPXUtilities.GPXFile, simpleUserItem: Boolean): GpxChatItem? { + return if (simpleUserItem) gpxToUserGpxChatItem(helper, gpx) else gpxToGpxChatItem(helper, gpx) + } + private fun botMessageToLocationItem( chat: TdApi.Chat, content: MessageOsmAndBotLocation @@ -224,6 +229,49 @@ object TelegramUiHelper { } } + private fun gpxToGpxChatItem( + helper: TelegramHelper, + gpx: GPXUtilities.GPXFile + ): GpxChatItem? { + val user = helper.getUser(gpx.userId) ?: return null + val chat = helper.getChat(gpx.chatId) ?: return null + return GpxChatItem().apply { + chatId = chat.id + chatTitle = chat.title + gpxFile = gpx + name = TelegramUiHelper.getUserName(user) + if (helper.isGroup(chat)) { + photoPath = helper.getUserPhotoPath(user) + groupPhotoPath = chat.photo?.small?.local?.path + } else { + photoPath = user.profilePhoto?.small?.local?.path + } + grayscalePhotoPath = helper.getUserGreyPhotoPath(user) + placeholderId = R.drawable.img_user_picture + userId = user.id + privateChat = helper.isPrivateChat(chat) || helper.isSecretChat(chat) + chatWithBot = helper.isBot(userId) + lastUpdated = (gpx.modifiedTime / 1000).toInt() + } + } + + private fun gpxToUserGpxChatItem( + helper: TelegramHelper, + gpx: GPXUtilities.GPXFile + ): GpxChatItem? { + val user = helper.getUser(gpx.userId) ?: return null + return GpxChatItem().apply { + gpxFile = gpx + name = TelegramUiHelper.getUserName(user) + photoPath = user.profilePhoto?.small?.local?.path + grayscalePhotoPath = helper.getUserGreyPhotoPath(user) + placeholderId = R.drawable.img_user_picture + userId = user.id + chatWithBot = helper.isBot(userId) + lastUpdated = (gpx.modifiedTime / 1000).toInt() + } + } + abstract class ListItem { var chatId: Long = 0 @@ -272,6 +320,24 @@ object TelegramUiHelper { override fun getVisibleName() = chatTitle } + class GpxChatItem : ListItem() { + + var gpxFile: GPXUtilities.GPXFile? = null + internal set + var groupPhotoPath: String? = null + internal set + var privateChat: Boolean = false + internal set + var chatWithBot: Boolean = false + internal set + + override fun canBeOpenedOnMap() = latLon != null + + override fun getMapPointId() = "${chatId}_$userId" + + override fun getVisibleName() = chatTitle + } + class LocationItem : ListItem() { override fun canBeOpenedOnMap() = latLon != null diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt index 42ba73d4cc..073c9633f2 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/LiveNowTabFragment.kt @@ -11,10 +11,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.LinearInterpolator -import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView +import android.widget.* import net.osmand.Location import net.osmand.data.LatLon import net.osmand.telegram.R @@ -22,6 +19,7 @@ import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramLocationProvider.TelegramCompassListener import net.osmand.telegram.TelegramLocationProvider.TelegramLocationListener import net.osmand.telegram.TelegramSettings +import net.osmand.telegram.helpers.SavingTracksDbHelper import net.osmand.telegram.helpers.TelegramHelper.* import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.telegram.helpers.TelegramUiHelper.ChatItem @@ -33,7 +31,7 @@ import net.osmand.telegram.utils.OsmandFormatter import net.osmand.telegram.utils.UiUtils.UpdateLocationViewCache import net.osmand.util.MapUtils import org.drinkless.td.libcore.telegram.TdApi -import java.io.File +import java.util.* private const val CHAT_VIEW_TYPE = 0 private const val LOCATION_ITEM_VIEW_TYPE = 1 @@ -233,15 +231,6 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage stopLocationUpdate() } - fun shareGpx(path: String) { - val fileUri = AndroidUtils.getUriForFile(app, File(path)) - val sendIntent = Intent(Intent.ACTION_SEND) - sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri) - sendIntent.type = "application/gpx+xml" - sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivity(sendIntent) - } - private fun chooseOsmAnd() { val ctx = context ?: return val installedApps = TelegramSettings.AppConnect.getInstalledApps(ctx) @@ -456,15 +445,6 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage app.showLocationHelper.showLocationOnMap(item, staleLocation) } } - openOnMapView?.setOnLongClickListener { - app.savingTracksDbHelper.saveAsyncUserDataToGpx( - this@LiveNowTabFragment, - app.getExternalFilesDir(null), - item.userId, - 60 * 60 * 24 * 1000 - ) - true - } } else { openOnMapView?.setOnClickListener(null) } @@ -485,17 +465,6 @@ class LiveNowTabFragment : Fragment(), TelegramListener, TelegramIncomingMessage if (lastItem) { holder.lastTelegramUpdateTime?.visibility = View.VISIBLE holder.lastTelegramUpdateTime?.text = OsmandFormatter.getListItemLiveTimeDescr(app, telegramHelper.lastTelegramUpdateTime, lastTelegramUpdateStr) - holder.lastTelegramUpdateTime?.setOnClickListener { - val currentUserId = telegramHelper.getCurrentUser()?.id - if (currentUserId != null) { - app.savingTracksDbHelper.saveAsyncUserDataToGpx( - this@LiveNowTabFragment, - app.getExternalFilesDir(null), - currentUserId, - 60 * 60 * 24 * 1000 - ) - } - } } else { holder.lastTelegramUpdateTime?.visibility = View.GONE } diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/MainActivity.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/MainActivity.kt index 0ac6356a0b..62d50ee90d 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/MainActivity.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/MainActivity.kt @@ -27,6 +27,7 @@ import net.osmand.telegram.ui.MyLocationTabFragment.ActionButtonsListener import net.osmand.telegram.ui.views.LockableViewPager import net.osmand.telegram.utils.* import org.drinkless.td.libcore.telegram.TdApi +import java.io.File import java.lang.ref.WeakReference const val OPEN_MY_LOCATION_TAB_KEY = "open_my_location_tab" @@ -35,6 +36,7 @@ private const val PERMISSION_REQUEST_LOCATION = 1 private const val MY_LOCATION_TAB_POS = 0 private const val LIVE_NOW_TAB_POS = 1 +private const val TIMELINE_TAB_POS = 2 class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListener, TelegramIncomingMessagesListener { @@ -54,6 +56,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene private var myLocationTabFragment: MyLocationTabFragment? = null private var liveNowTabFragment: LiveNowTabFragment? = null + private var timelineTabFragment: TimelineTabFragment? = null private lateinit var buttonsBar: LinearLayout private lateinit var bottomNav: BottomNavigationView @@ -72,7 +75,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene val viewPager = findViewById(R.id.view_pager).apply { swipeLocked = true - offscreenPageLimit = 2 + offscreenPageLimit = 3 adapter = ViewPagerAdapter(supportFragmentManager) } @@ -82,11 +85,13 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene when (it.itemId) { R.id.action_my_location -> pos = MY_LOCATION_TAB_POS R.id.action_live_now -> pos = LIVE_NOW_TAB_POS + R.id.action_timeline -> pos = TIMELINE_TAB_POS } if (pos != -1 && pos != viewPager.currentItem) { when (pos) { MY_LOCATION_TAB_POS -> liveNowTabFragment?.tabClosed() LIVE_NOW_TAB_POS -> liveNowTabFragment?.tabOpened() + TIMELINE_TAB_POS -> liveNowTabFragment?.tabClosed() } viewPager.currentItem = pos return@setOnNavigationItemSelectedListener true @@ -139,10 +144,10 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene if (fragment is TelegramListener) { listeners.add(WeakReference(fragment)) } - if (fragment is MyLocationTabFragment) { - myLocationTabFragment = fragment - } else if (fragment is LiveNowTabFragment) { - liveNowTabFragment = fragment + when (fragment) { + is MyLocationTabFragment -> myLocationTabFragment = fragment + is LiveNowTabFragment -> liveNowTabFragment = fragment + is TimelineTabFragment -> timelineTabFragment = fragment } } @@ -317,6 +322,15 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene android.os.Process.killProcess(android.os.Process.myPid()) } + fun shareGpx(path: String) { + val fileUri = AndroidUtils.getUriForFile(app, File(path)) + val sendIntent = Intent(Intent.ACTION_SEND) + sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri) + sendIntent.type = "application/gpx+xml" + sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + startActivity(sendIntent) + } + fun loginTelegram() { if (telegramHelper.getTelegramAuthorizationState() != TelegramAuthorizationState.CLOSED) { telegramHelper.logout() @@ -463,7 +477,7 @@ class MainActivity : AppCompatActivity(), TelegramListener, ActionButtonsListene class ViewPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) { - private val fragments = listOf(MyLocationTabFragment(), LiveNowTabFragment()) + private val fragments = listOf(MyLocationTabFragment(), LiveNowTabFragment(), TimelineTabFragment()) override fun getItem(position: Int) = fragments[position] diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt index 8ce2e7a4f5..149f6c7cc1 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/MyLocationTabFragment.kt @@ -599,12 +599,6 @@ class MyLocationTabFragment : Fragment(), TelegramListener { } holder.title?.text = title - holder.icon?.setOnClickListener { - app.forceUpdateMyLocation() - val curUser = telegramHelper.getCurrentUser() - val text = "${curUser?.id} ${curUser?.firstName} ${curUser?.lastName}" - Toast.makeText(app, text, Toast.LENGTH_LONG).show() - } if (holder is ChatViewHolder) { holder.description?.visibility = View.GONE if (live) { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt new file mode 100644 index 0000000000..6ddd564e8d --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/TimelineTabFragment.kt @@ -0,0 +1,261 @@ +package net.osmand.telegram.ui + +import android.app.DatePickerDialog +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Bundle +import android.support.annotation.DrawableRes +import android.support.v4.app.Fragment +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.Switch +import android.widget.TextView +import net.osmand.PlatformUtil +import net.osmand.telegram.R +import net.osmand.telegram.TelegramApplication +import net.osmand.telegram.helpers.TelegramUiHelper +import net.osmand.telegram.helpers.TelegramUiHelper.ListItem +import net.osmand.telegram.ui.TimelineTabFragment.LiveNowListAdapter.BaseViewHolder +import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.OsmandFormatter +import java.util.* + + +class TimelineTabFragment : Fragment() { + + private val log = PlatformUtil.getLog(TimelineTabFragment::class.java) + + private val app: TelegramApplication + get() = activity?.application as TelegramApplication + + private val telegramHelper get() = app.telegramHelper + private val settings get() = app.settings + + private lateinit var adapter: LiveNowListAdapter + + private lateinit var dateStartBtn: TextView + private lateinit var dateEndBtn: TextView + private lateinit var mainView: View + + private var start = 0L + private var end = 0L + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + mainView = inflater.inflate(R.layout.fragment_timeline_tab, container, false) + val appBarLayout = mainView.findViewById(R.id.app_bar_layout) + + start = System.currentTimeMillis() + end = System.currentTimeMillis() + + AndroidUtils.addStatusBarPadding19v(context!!, appBarLayout) + adapter = LiveNowListAdapter() + mainView.findViewById(R.id.recycler_view).apply { + layoutManager = LinearLayoutManager(context) + adapter = this@TimelineTabFragment.adapter + } + + val switcher = mainView.findViewById(R.id.monitoring_switcher) + val monitoringTv = mainView.findViewById(R.id.monitoring_title) + monitoringTv.setText(if (settings.monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled) + + mainView.findViewById(R.id.monitoring_container).setOnClickListener { + val monitoringEnabled = !settings.monitoringEnabled + settings.monitoringEnabled = monitoringEnabled + switcher.isChecked = monitoringEnabled + monitoringTv.setText(if (monitoringEnabled) R.string.monitoring_is_enabled else R.string.monitoring_is_disabled) + } + + dateStartBtn = mainView.findViewById(R.id.date_start_btn).apply { + setOnClickListener { + selectStartDate() + } + setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_start), null, null, null) + } + dateEndBtn = mainView.findViewById(R.id.date_end_btn).apply { + setOnClickListener { + selectEndDate() + } + setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_add), null, null, null) + } + + setupBtnTextColor(dateStartBtn) + setupBtnTextColor(dateEndBtn) + + return mainView + } + + private fun setupBtnTextColor(textView: TextView) { + textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) + } + + private fun selectStartDate() { + val dateFromDialog = + DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + val from = Calendar.getInstance() + from.set(Calendar.YEAR, year) + from.set(Calendar.MONTH, monthOfYear) + from.set(Calendar.DAY_OF_MONTH, dayOfMonth) + start = from.timeInMillis + updateList() + updateDateButtons() + } + val startCalendar = Calendar.getInstance() + startCalendar.timeInMillis = start + DatePickerDialog(context, dateFromDialog, + startCalendar.get(Calendar.YEAR), + startCalendar.get(Calendar.MONTH), + startCalendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + + private fun selectEndDate() { + val dateFromDialog = + DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + val from = Calendar.getInstance() + from.set(Calendar.YEAR, year) + from.set(Calendar.MONTH, monthOfYear) + from.set(Calendar.DAY_OF_MONTH, dayOfMonth) + end = from.timeInMillis + updateList() + updateDateButtons() + } + val endCalendar = Calendar.getInstance() + endCalendar.timeInMillis = end + DatePickerDialog(context, dateFromDialog, + endCalendar.get(Calendar.YEAR), + endCalendar.get(Calendar.MONTH), + endCalendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + + private fun updateDateButtons() { + dateStartBtn.text = OsmandFormatter.getFormattedDate(start / 1000) + dateEndBtn.text = OsmandFormatter.getFormattedDate(end / 1000) + dateEndBtn.setCompoundDrawablesWithIntrinsicBounds(getPressedStateIcon(R.drawable.ic_action_date_end), null, null, null) + } + + private fun getPressedStateIcon(@DrawableRes iconId: Int): Drawable? { + val normal = app.uiUtils.getActiveIcon(iconId) + if (Build.VERSION.SDK_INT >= 21) { + val active = app.uiUtils.getIcon(iconId, R.color.ctrl_light) + if (normal != null && active != null) { + return AndroidUtils.createPressedStateListDrawable(normal, active) + } + } + return normal + } + + private fun updateList() { + val res = mutableListOf() + val s = System.currentTimeMillis() + log.debug("updateList $start") + val ignoredUsersIds = ArrayList() + val currentUserId = telegramHelper.getCurrentUser()?.id + if (currentUserId != null) { + val currentUserGpx = app.savingTracksDbHelper.collectRecordedDataForUser(currentUserId, 0, start, end) + TelegramUiHelper.gpxToChatItem(telegramHelper, currentUserGpx, true)?.also { + res.add(it) + } + ignoredUsersIds.add(currentUserId) + } + val gpxFiles = app.savingTracksDbHelper.collectRecordedDataForUsers(start, end, ignoredUsersIds) + val e = System.currentTimeMillis() + + gpxFiles.forEach { + TelegramUiHelper.gpxToChatItem(telegramHelper, it,false)?.also { chatItem -> + res.add(chatItem) + } + } + + adapter.items = sortAdapterItems(res) + log.debug("updateList $s dif: ${e - s}") + } + + private fun sortAdapterItems(list: MutableList): MutableList { + val currentUserId = telegramHelper.getCurrentUser()?.id ?: 0 + list.sortWith(java.util.Comparator { lhs, rhs -> + when (currentUserId) { + lhs.userId -> return@Comparator 1 + rhs.userId -> return@Comparator 1 + else -> return@Comparator lhs.name.compareTo(rhs.name) + } + }) + return list + } + + inner class LiveNowListAdapter : RecyclerView.Adapter() { + + var items: List = emptyList() + set(value) { + field = value + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val inflater = LayoutInflater.from(parent.context) + return BaseViewHolder(inflater.inflate(R.layout.live_now_chat_card, parent, false)) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val lastItem = position == itemCount - 1 + val item = items[position] + val currentUserId = telegramHelper.getCurrentUser()?.id ?: 0 + + TelegramUiHelper.setupPhoto(app, holder.icon, item.photoPath, R.drawable.img_user_picture_active, false) + holder.title?.text = item.name + holder.bottomShadow?.visibility = if (lastItem) View.VISIBLE else View.GONE + holder.lastTelegramUpdateTime?.visibility = View.GONE + + if (item is TelegramUiHelper.GpxChatItem) { + val gpx = item.gpxFile + val groupDescrRowVisible = (!item.privateChat || item.chatWithBot) && item.userId != currentUserId + if (groupDescrRowVisible) { + holder.groupDescrContainer?.visibility = View.VISIBLE + holder.groupTitle?.text = item.getVisibleName() + TelegramUiHelper.setupPhoto(app, holder.groupImage, item.groupPhotoPath, item.placeholderId, false) + } else { + holder.groupDescrContainer?.visibility = View.GONE + } + holder.userRow?.setOnClickListener { + if (gpx != null) { + childFragmentManager.also { + UserGpxInfoFragment.showInstance(it, gpx, start, end) + } + } + } + + holder.imageButton?.visibility = View.GONE + holder.showOnMapRow?.visibility = View.GONE + holder.bottomDivider?.visibility = if (lastItem) View.GONE else View.VISIBLE + holder.topDivider?.visibility = if (position != 0) View.GONE else View.VISIBLE + } + } + + override fun getItemCount() = items.size + + inner class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val icon: ImageView? = view.findViewById(R.id.icon) + val title: TextView? = view.findViewById(R.id.title) + val description: TextView? = view.findViewById(R.id.description) + val bottomShadow: View? = view.findViewById(R.id.bottom_shadow) + val lastTelegramUpdateTime: TextView? = view.findViewById(R.id.last_telegram_update_time) + + val userRow: View? = view.findViewById(R.id.user_row) + val groupDescrContainer: View? = view.findViewById(R.id.group_container) + val groupImage: ImageView? = view.findViewById(R.id.group_icon) + val groupTitle: TextView? = view.findViewById(R.id.group_title) + val imageButton: ImageView? = view.findViewById(R.id.image_button) + val showOnMapRow: View? = view.findViewById(R.id.show_on_map_row) + val topDivider: View? = view.findViewById(R.id.top_divider) + val bottomDivider: View? = view.findViewById(R.id.bottom_divider) + } + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt new file mode 100644 index 0000000000..a411d80b7d --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -0,0 +1,322 @@ +package net.osmand.telegram.ui + +import android.app.DatePickerDialog +import android.app.TimePickerDialog +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.drawable.BitmapDrawable +import android.os.Bundle +import android.support.v4.app.FragmentManager +import android.util.DisplayMetrics +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import android.widget.Toast +import net.osmand.PlatformUtil +import net.osmand.aidl.gpx.AGpxBitmap +import net.osmand.telegram.R +import net.osmand.telegram.helpers.OsmandAidlHelper +import net.osmand.telegram.helpers.SavingTracksDbHelper +import net.osmand.telegram.helpers.TelegramUiHelper +import net.osmand.telegram.utils.AndroidUtils +import net.osmand.telegram.utils.GPXUtilities +import net.osmand.telegram.utils.OsmandFormatter +import net.osmand.util.Algorithms +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +class UserGpxInfoFragment : BaseDialogFragment() { + + private val log = PlatformUtil.getLog(UserGpxInfoFragment::class.java) + + private val uiUtils get() = app.uiUtils + + private lateinit var gpxFile: GPXUtilities.GPXFile + + private lateinit var dateStartBtn: TextView + private lateinit var timeStartBtn: TextView + private lateinit var dateEndBtn: TextView + private lateinit var timeEndBtn: TextView + + private lateinit var avgElevationTv: TextView + private lateinit var avgSpeedTv: TextView + private lateinit var totalDistanceTv: TextView + private lateinit var timeSpanTv: TextView + + private var startCalendar = Calendar.getInstance() + private var endCalendar = Calendar.getInstance() + + override fun onCreateView( + inflater: LayoutInflater, + parent: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val mainView = inflater.inflate(R.layout.fragment_user_gpx_info, parent) + AndroidUtils.addStatusBarPadding19v(context!!, mainView) + + readFromBundle(savedInstanceState ?: arguments) + + val userId = gpxFile.userId + val chatId = gpxFile.chatId + + val user = app.telegramHelper.getUser(userId) + if (user != null) { + mainView.findViewById(R.id.title).text = TelegramUiHelper.getUserName(user) + TelegramUiHelper.setupPhoto(app, mainView.findViewById(R.id.user_icon), + telegramHelper.getUserPhotoPath(user), R.drawable.img_user_placeholder, false) + } + app.osmandAidlHelper.setGpxBitmapCreatedListener( + object : OsmandAidlHelper.GpxBitmapCreatedListener { + override fun onGpxBitmapCreated(bitmap: AGpxBitmap) { + activity?.runOnUiThread { + mainView.findViewById(R.id.gpx_map).setImageDrawable(BitmapDrawable(app.resources, bitmap.bitmap)) + } + } + }) + + updateGPXMap() + + val backBtn = mainView.findViewById(R.id.back_button) + backBtn.setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_arrow_back)) + backBtn.setOnClickListener { + dismiss() + } + + dateStartBtn = mainView.findViewById(R.id.date_start_btn) + timeStartBtn = mainView.findViewById(R.id.time_start_btn) + dateEndBtn = mainView.findViewById(R.id.date_end_btn) + timeEndBtn = mainView.findViewById(R.id.time_end_btn) + + dateStartBtn.setOnClickListener { selectStartDate() } + timeStartBtn.setOnClickListener { selectStartTime() } + dateEndBtn.setOnClickListener { selectEndDate() } + timeEndBtn.setOnClickListener { selectEndTime() } + + setupBtnTextColor(dateStartBtn) + setupBtnTextColor(timeStartBtn) + setupBtnTextColor(dateEndBtn) + setupBtnTextColor(timeEndBtn) + + updateDateAndTimeButtons() + + avgElevationTv = mainView.findViewById(R.id.average_altitude_text) + avgSpeedTv = mainView.findViewById(R.id.average_speed_text) + totalDistanceTv = mainView.findViewById(R.id.distance_text) + timeSpanTv = mainView.findViewById(R.id.duration_text) + + mainView.findViewById(R.id.average_altitude_icon).apply { + setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range)) + } + mainView.findViewById(R.id.average_speed_icon).apply { + setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_speed_average)) + } + mainView.findViewById(R.id.distance_icon).apply { + setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range)) + } + mainView.findViewById(R.id.duration_icon).apply { + setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range)) + } + + updateGPXStatisticRow() + + mainView.findViewById(R.id.open_in_osmand_icon).setImageResource(R.drawable.ic_logo_osmand_free) + + mainView.findViewById(R.id.open_in_osmand_btn).apply { + setOnClickListener { + val gpx = gpxFile + if (gpx.path.isNotEmpty()) { + openGpx(gpx.path) + } else { + saveCurrentGpxToFile(object : + SavingTracksDbHelper.SaveGpxListener { + override fun onSavingGpxFinish(path: String) { + openGpx(path) + } + + override fun onSavingGpxError(warnings: MutableList?) { + Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show() + } + }) + } + } + } + + mainView.findViewById(R.id.share_gpx_icon).setImageDrawable(uiUtils.getActiveIcon(R.drawable.ic_action_share)) + + mainView.findViewById(R.id.share_gpx_btn).apply { + setOnClickListener { + val gpx = gpxFile + if (gpx.path.isNotEmpty()) { + (activity as MainActivity).shareGpx(gpx.path) + } else { + saveCurrentGpxToFile(object : + SavingTracksDbHelper.SaveGpxListener { + override fun onSavingGpxFinish(path: String) { + (activity as MainActivity).shareGpx(path) + } + + override fun onSavingGpxError(warnings: MutableList?) { + Toast.makeText(app, warnings?.firstOrNull(), Toast.LENGTH_LONG).show() + } + }) + } + } + } + + return mainView + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putLong(START_KEY, startCalendar.timeInMillis) + outState.putLong(END_KEY, endCalendar.timeInMillis) + } + + private fun openGpx(path: String) { + val fileUri = AndroidUtils.getUriForFile(app, File(path)) + val openGpxIntent = Intent(Intent.ACTION_VIEW) + openGpxIntent.setDataAndType(fileUri, "application/gpx+xml") +// openGpxIntent.type = "application/gpx+xml" + openGpxIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + val resolved = activity?.packageManager?.resolveActivity(openGpxIntent, PackageManager.MATCH_DEFAULT_ONLY) + if (resolved != null) { + startActivity(openGpxIntent) + } + } + + private fun saveCurrentGpxToFile(listener: SavingTracksDbHelper.SaveGpxListener) { + app.savingTracksDbHelper.saveGpx(listener, app.getExternalFilesDir(null), gpxFile) + } + + private fun readFromBundle(bundle: Bundle?) { + bundle?.also { + startCalendar.timeInMillis = it.getLong(START_KEY) + endCalendar.timeInMillis = it.getLong(END_KEY) + } + } + + private fun setupBtnTextColor(textView: TextView) { + textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) + } + + private fun updateGpxInfo() { + gpxFile = app.savingTracksDbHelper.collectRecordedDataForUser(gpxFile.userId, gpxFile.chatId, startCalendar.timeInMillis, endCalendar.timeInMillis) + updateGPXStatisticRow() + updateDateAndTimeButtons() + updateGPXMap() + } + + private fun updateDateAndTimeButtons() { + dateStartBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(startCalendar.timeInMillis) + dateEndBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(endCalendar.timeInMillis) + + timeStartBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(startCalendar.timeInMillis) + timeEndBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(endCalendar.timeInMillis) + } + + private fun updateGPXStatisticRow() { + val analysis: GPXUtilities.GPXTrackAnalysis = gpxFile.getAnalysis(0) + avgElevationTv.text = OsmandFormatter.getFormattedAlt(analysis.avgElevation, app) + avgSpeedTv.text = if (analysis.isSpeedSpecified) OsmandFormatter.getFormattedSpeed(analysis.avgSpeed, app) else "" + totalDistanceTv.text = OsmandFormatter.getFormattedDistance(analysis.totalDistance, app) + timeSpanTv.text = Algorithms.formatDuration((analysis.timeSpan / 1000).toInt(), true) + } + + private fun updateGPXMap() { + saveCurrentGpxToFile(object : + SavingTracksDbHelper.SaveGpxListener { + override fun onSavingGpxFinish(path: String) { + val mgr = activity?.getSystemService(Context.WINDOW_SERVICE) + if (mgr != null) { + val dm = DisplayMetrics() + (mgr as WindowManager).defaultDisplay.getMetrics(dm) + val widthPixels = dm.widthPixels - (2 * app.resources.getDimensionPixelSize(R.dimen.content_padding_standard)) + val heightPixels = AndroidUtils.dpToPx(app, 152f) + val gpxUri = AndroidUtils.getUriForFile(app, File(path)) + app.osmandAidlHelper.getBitmapForGpx(gpxUri, dm.density , widthPixels, heightPixels, GPX_TRACK_COLOR) + } + } + + override fun onSavingGpxError(warnings: MutableList?) { + log.debug("onSavingGpxError ${warnings?.firstOrNull()}") + } + }) + } + + private fun selectStartDate() { + val dateFromDialog = + DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + startCalendar.set(Calendar.YEAR, year) + startCalendar.set(Calendar.MONTH, monthOfYear) + startCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + updateGpxInfo() + } + DatePickerDialog(context, dateFromDialog, + startCalendar.get(Calendar.YEAR), + startCalendar.get(Calendar.MONTH), + startCalendar.get(Calendar.DAY_OF_MONTH)).show() + } + + private fun selectStartTime() { + TimePickerDialog(context, + TimePickerDialog.OnTimeSetListener { _, hours, minutes -> + startCalendar.set(Calendar.HOUR_OF_DAY, hours) + startCalendar.set(Calendar.MINUTE, minutes) + updateGpxInfo() + }, 0, 0, true).show() + } + + private fun selectEndDate() { + val dateFromDialog = + DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + endCalendar.set(Calendar.YEAR, year) + endCalendar.set(Calendar.MONTH, monthOfYear) + endCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + updateGpxInfo() + } + DatePickerDialog(context, dateFromDialog, + endCalendar.get(Calendar.YEAR), + endCalendar.get(Calendar.MONTH), + endCalendar.get(Calendar.DAY_OF_MONTH)).show() + } + + private fun selectEndTime() { + TimePickerDialog(context, + TimePickerDialog.OnTimeSetListener { _, hours, minutes -> + endCalendar.set(Calendar.HOUR_OF_DAY, hours) + endCalendar.set(Calendar.MINUTE, minutes) + updateGpxInfo() + }, 0, 0, true).show() + } + + companion object { + + private const val TAG = "UserGpxInfoFragment" + private const val START_KEY = "start_key" + private const val END_KEY = "end_key" + + private const val GPX_TRACK_COLOR = -65536 + + fun showInstance(fm: FragmentManager, gpxFile: GPXUtilities.GPXFile, start: Long, end: Long): Boolean { + return try { + val fragment = UserGpxInfoFragment().apply { + arguments = Bundle().apply { + putLong(START_KEY, start) + putLong(END_KEY, end) + } + } + fragment.gpxFile = gpxFile + fragment.show(fm, TAG) + true + } catch (e: RuntimeException) { + false + } + } + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java b/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java index 5d2ba74d8f..b27293c40b 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/GPXUtilities.java @@ -10,6 +10,7 @@ import android.text.TextUtils; import net.osmand.Location; import net.osmand.PlatformUtil; +import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; import net.osmand.telegram.TelegramApplication; import net.osmand.util.Algorithms; @@ -49,7 +50,7 @@ import java.util.Set; import java.util.Stack; import java.util.TimeZone; -// copy from net.osmand.plus.GPXUtilities and changes done to WptPt (userId,chatId) +// copy from net.osmand.plus.GPXUtilities and changes done to WptPt and GPXFile (userId,chatId) public class GPXUtilities { public final static Log log = PlatformUtil.getLog(GPXUtilities.class); @@ -815,6 +816,8 @@ public class GPXUtilities { public String path = ""; public boolean showCurrentTrack; public long modifiedTime = 0; + public int userId; + public long chatId; private Track generalTrack; private TrkSegment generalSegment; @@ -1248,6 +1251,57 @@ public class GPXUtilities { } return categories; } + + public QuadRect getRect() { + double left = 0, right = 0; + double top = 0, bottom = 0; + for (Track track : tracks) { + for (TrkSegment segment : track.segments) { + for (WptPt p : segment.points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + } + } + for (WptPt p : points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + for (GPXUtilities.Route route : routes) { + for (WptPt p : route.points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + } + return new QuadRect(left, top, right, bottom); + } } public static String asString(GPXFile file, TelegramApplication ctx) { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt index 7e8e4d9b32..3a9719848a 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt @@ -23,6 +23,7 @@ object OsmandFormatter { private const val SHORT_TIME_FORMAT = "%02d:%02d" private const val SIMPLE_TIME_OF_DAY_FORMAT = "HH:mm" private const val SIMPLE_DATE_FORMAT = "dd MMM HH:mm:ss" + private const val SHORT_DATE_FORMAT = "dd MMM yyyy" private const val MIN_DURATION_FOR_DATE_FORMAT = 48 * 60 * 60 @@ -77,8 +78,13 @@ object OsmandFormatter { } } - fun getFormattedDate(seconds: Long): String = - SimpleDateFormat(SIMPLE_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L) + fun getFormattedDate(seconds: Long, shortFormat: Boolean = false): String { + return if (shortFormat) { + SimpleDateFormat(SIMPLE_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L) + } else { + SimpleDateFormat(SHORT_DATE_FORMAT, Locale.getDefault()).format(seconds * 1000L) + } + } fun getListItemLiveTimeDescr(ctx: TelegramApplication, lastUpdated: Int, prefix: String = ""): String { return if (lastUpdated > 0) { diff --git a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl index 6baa3578a7..6f0cb8f65b 100644 --- a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl +++ b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlCallback.aidl @@ -1,6 +1,7 @@ package net.osmand.aidl; import net.osmand.aidl.search.SearchResult; +import net.osmand.aidl.gpx.AGpxBitmap; interface IOsmAndAidlCallback { void onSearchComplete(in List resultSet); @@ -8,4 +9,6 @@ interface IOsmAndAidlCallback { void onUpdate(); void onAppInitialized(); + + void onGpxBitmapCreated(in AGpxBitmap bitmap); } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl index b7ed9b1cef..af2423cd8b 100644 --- a/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl +++ b/OsmAnd/src/net/osmand/aidl/IOsmAndAidlInterface.aidl @@ -74,6 +74,8 @@ import net.osmand.aidl.customization.OsmandSettingsParams; import net.osmand.aidl.gpx.AGpxFile; import net.osmand.aidl.gpx.AGpxFileDetails; +import net.osmand.aidl.gpx.CreateGpxBitmapParams; + import net.osmand.aidl.tiles.ASqliteDbFile; import net.osmand.aidl.plugins.PluginParams; @@ -175,4 +177,6 @@ interface IOsmAndAidlInterface { boolean changePluginState(in PluginParams params); boolean registerForOsmandInitListener(in IOsmAndAidlCallback callback); + + boolean getBitmapForGpx(in CreateGpxBitmapParams file, IOsmAndAidlCallback callback); } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index 8ec5eb8317..5f833bd475 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -10,6 +10,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; @@ -24,10 +25,12 @@ import android.text.TextUtils; import android.view.View; import android.widget.ArrayAdapter; +import net.osmand.CallbackWithObject; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.aidl.favorite.AFavorite; import net.osmand.aidl.favorite.group.AFavoriteGroup; +import net.osmand.aidl.gpx.AGpxBitmap; import net.osmand.aidl.gpx.AGpxFile; import net.osmand.aidl.gpx.AGpxFileDetails; import net.osmand.aidl.gpx.ASelectedGpxFile; @@ -69,6 +72,7 @@ import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.helpers.ColorDialogs; import net.osmand.plus.helpers.ExternalApiHelper; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; +import net.osmand.plus.myplaces.TrackBitmapDrawer; import net.osmand.plus.rastermaps.OsmandRasterMapsPlugin; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.views.AidlMapLayer; @@ -1907,7 +1911,96 @@ public class OsmandAidlApi { return app.getAppCustomization().changePluginStatus(params); } + boolean getBitmapForGpx(final Uri gpxUri, final float density, final int widthPixels, final int heightPixels, final int color, final GpxBitmapCreatedCallback callback) { + if (gpxUri == null || callback == null) { + return false; + } + final TrackBitmapDrawer.TrackBitmapDrawerListener drawerListener = new TrackBitmapDrawer.TrackBitmapDrawerListener() { + @Override + public void onTrackBitmapDrawing() { + } + @Override + public void onTrackBitmapDrawn() { + } + + @Override + public boolean isTrackBitmapSelectionSupported() { + return false; + } + + @Override + public void drawTrackBitmap(Bitmap bitmap) { + callback.onGpxBitmapCreatedComplete(new AGpxBitmap(bitmap)); + } + }; + + if (app.isApplicationInitializing()) { + app.getAppInitializer().addListener(new AppInitializer.AppInitializeListener() { + @Override + public void onProgress(AppInitializer init, AppInitializer.InitEvents event) { + } + + @Override + public void onFinish(AppInitializer init) { + createGpxBitmapFromUri(gpxUri, density, widthPixels, heightPixels, color, drawerListener); + } + }); + } else { + createGpxBitmapFromUri(gpxUri, density, widthPixels, heightPixels, color, drawerListener); + } + return true; + } + + private void createGpxBitmapFromUri(final Uri gpxUri, final float density, final int widthPixels, final int heightPixels, final int color, final TrackBitmapDrawer.TrackBitmapDrawerListener drawerListener) { + GpxAsyncLoaderTask gpxAsyncLoaderTask = new GpxAsyncLoaderTask(app, gpxUri, new CallbackWithObject() { + @Override + public boolean processResult(GPXFile result) { + TrackBitmapDrawer trackBitmapDrawer = new TrackBitmapDrawer(app, result, null, result.getRect(), density, widthPixels, heightPixels); + trackBitmapDrawer.addListener(drawerListener); + trackBitmapDrawer.setDrawEnabled(true); + trackBitmapDrawer.setTrackColor(color); + trackBitmapDrawer.initAndDraw(); + return false; + } + }); + gpxAsyncLoaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private static class GpxAsyncLoaderTask extends AsyncTask { + + private final OsmandApplication app; + private final CallbackWithObject callback; + private final Uri gpxUri; + + GpxAsyncLoaderTask(@NonNull OsmandApplication app, @NonNull Uri gpxUri, final CallbackWithObject callback) { + this.app = app; + this.gpxUri = gpxUri; + this.callback = callback; + } + + @Override + protected void onPostExecute(GPXFile gpxFile) { + if (gpxFile.warning == null && callback != null) { + callback.processResult(gpxFile); + } + } + + @Override + protected GPXFile doInBackground(Void... voids) { + ParcelFileDescriptor gpxParcelDescriptor = null; + try { + gpxParcelDescriptor = app.getContentResolver().openFileDescriptor(gpxUri, "r"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + if (gpxParcelDescriptor != null) { + final FileDescriptor fileDescriptor = gpxParcelDescriptor.getFileDescriptor(); + return GPXUtilities.loadGPXFile(app, new FileInputStream(fileDescriptor)); + } + return null; + } + } private static AGpxFileDetails createGpxFileDetails(@NonNull GPXTrackAnalysis a) { return new AGpxFileDetails(a.totalDistance, a.totalTracks, a.startTime, a.endTime, @@ -1980,6 +2073,10 @@ public class OsmandAidlApi { void onSearchComplete(List resultSet); } + public interface GpxBitmapCreatedCallback { + void onGpxBitmapCreatedComplete(AGpxBitmap aGpxBitmap); + } + public interface OsmandAppInitCallback { void onAppInitialized(); } diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java index f8d2c0d30b..949b589fe5 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlService.java @@ -11,6 +11,7 @@ import android.os.RemoteException; import android.support.annotation.Nullable; import net.osmand.PlatformUtil; +import net.osmand.aidl.OsmandAidlApi.GpxBitmapCreatedCallback; import net.osmand.aidl.OsmandAidlApi.OsmandAppInitCallback; import net.osmand.aidl.OsmandAidlApi.SearchCompleteCallback; import net.osmand.aidl.calculateroute.CalculateRouteParams; @@ -22,8 +23,10 @@ import net.osmand.aidl.favorite.UpdateFavoriteParams; import net.osmand.aidl.favorite.group.AddFavoriteGroupParams; import net.osmand.aidl.favorite.group.RemoveFavoriteGroupParams; import net.osmand.aidl.favorite.group.UpdateFavoriteGroupParams; +import net.osmand.aidl.gpx.AGpxBitmap; import net.osmand.aidl.gpx.AGpxFile; import net.osmand.aidl.gpx.ASelectedGpxFile; +import net.osmand.aidl.gpx.CreateGpxBitmapParams; import net.osmand.aidl.gpx.HideGpxParams; import net.osmand.aidl.gpx.ImportGpxParams; import net.osmand.aidl.gpx.RemoveGpxParams; @@ -822,5 +825,25 @@ public class OsmandAidlService extends Service { return false; } } + + @Override + public boolean getBitmapForGpx(CreateGpxBitmapParams params, final IOsmAndAidlCallback callback) throws RemoteException { + try { + OsmandAidlApi api = getApi("getBitmapForGpx"); + return params != null && api != null && api.getBitmapForGpx(params.getGpxUri(), params.getDensity(), params.getWidthPixels(), params.getHeightPixels(), params.getColor(), new GpxBitmapCreatedCallback() { + @Override + public void onGpxBitmapCreatedComplete(AGpxBitmap aGpxBitmap) { + try { + callback.onGpxBitmapCreated(aGpxBitmap); + } catch (RemoteException e) { + handleException(e); + } + } + }); + } catch (Exception e) { + handleException(e); + return false; + } + } }; } diff --git a/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.aidl b/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.aidl new file mode 100644 index 0000000000..ca9f623833 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable AGpxBitmap; \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.java b/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.java new file mode 100644 index 0000000000..f4bdef433d --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/gpx/AGpxBitmap.java @@ -0,0 +1,46 @@ +package net.osmand.aidl.gpx; + +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +public class AGpxBitmap implements Parcelable { + + private Bitmap bitmap; + + public AGpxBitmap(@NonNull Bitmap bitmap) { + this.bitmap = bitmap; + } + + public AGpxBitmap(Parcel in) { + readFromParcel(in); + } + + public Bitmap getBitmap() { + return bitmap; + } + + public static final Creator CREATOR = new + Creator() { + public AGpxBitmap createFromParcel(Parcel in) { + return new AGpxBitmap(in); + } + + public AGpxBitmap[] newArray(int size) { + return new AGpxBitmap[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(bitmap, flags); + } + + private void readFromParcel(Parcel in) { + bitmap = in.readParcelable(Bitmap.class.getClassLoader()); + } + + public int describeContents() { + return 0; + } +} diff --git a/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl b/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl new file mode 100644 index 0000000000..b02d5c6e70 --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.aidl @@ -0,0 +1,3 @@ +package net.osmand.aidl.gpx; + +parcelable CreateGpxBitmapParams; \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java b/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java new file mode 100644 index 0000000000..b6af1d354c --- /dev/null +++ b/OsmAnd/src/net/osmand/aidl/gpx/CreateGpxBitmapParams.java @@ -0,0 +1,101 @@ +package net.osmand.aidl.gpx; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; + +public class CreateGpxBitmapParams implements Parcelable { + + private File gpxFile; + private Uri gpxUri; + private float density; + private int widthPixels; + private int heightPixels; + private int color; //ARGB color int + + public CreateGpxBitmapParams(File gpxFile, float density, int widthPixels, int heightPixels, int color) { + this.gpxFile = gpxFile; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Uri gpxUri, float density, int widthPixels, int heightPixels, int color) { + this.gpxUri = gpxUri; + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; + this.color = color; + } + + public CreateGpxBitmapParams(Parcel in) { + readFromParcel(in); + } + + public static final Creator CREATOR = new + Creator() { + public CreateGpxBitmapParams createFromParcel(Parcel in) { + return new CreateGpxBitmapParams(in); + } + + public CreateGpxBitmapParams[] newArray(int size) { + return new CreateGpxBitmapParams[size]; + } + }; + + public File getGpxFile() { + return gpxFile; + } + + public Uri getGpxUri() { + return gpxUri; + } + + public int getWidthPixels() { + return widthPixels; + } + + public int getHeightPixels() { + return heightPixels; + } + + public float getDensity() { + return density; + } + + public int getColor() { + return color; + } + + public void writeToParcel(Parcel out, int flags) { + if (gpxFile != null) { + out.writeString(gpxFile.getAbsolutePath()); + } else { + out.writeString(null); + } + out.writeParcelable(gpxUri, flags); + out.writeFloat(density); + out.writeInt(widthPixels); + out.writeInt(heightPixels); + out.writeInt(color); + } + + private void readFromParcel(Parcel in) { + String gpxAbsolutePath = in.readString(); + if (gpxAbsolutePath != null) { + gpxFile = new File(gpxAbsolutePath); + } + gpxUri = in.readParcelable(Uri.class.getClassLoader()); + density = in.readFloat(); + widthPixels = in.readInt(); + heightPixels = in.readInt(); + color = in.readInt(); + } + + public int describeContents() { + return 0; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/GPXUtilities.java b/OsmAnd/src/net/osmand/plus/GPXUtilities.java index 166aa89fce..ccc1c3b33f 100644 --- a/OsmAnd/src/net/osmand/plus/GPXUtilities.java +++ b/OsmAnd/src/net/osmand/plus/GPXUtilities.java @@ -13,6 +13,7 @@ import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.data.LocationPoint; import net.osmand.data.PointDescription; +import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; import net.osmand.plus.views.Renderable; import net.osmand.util.Algorithms; @@ -1247,6 +1248,57 @@ public class GPXUtilities { } return categories; } + + public QuadRect getRect() { + double left = 0, right = 0; + double top = 0, bottom = 0; + for (Track track : tracks) { + for (TrkSegment segment : track.segments) { + for (WptPt p : segment.points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + } + } + for (WptPt p : points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + for (GPXUtilities.Route route : routes) { + for (WptPt p : route.points) { + if (left == 0 && right == 0) { + left = p.getLongitude(); + right = p.getLongitude(); + top = p.getLatitude(); + bottom = p.getLatitude(); + } else { + left = Math.min(left, p.getLongitude()); + right = Math.max(right, p.getLongitude()); + top = Math.max(top, p.getLatitude()); + bottom = Math.min(bottom, p.getLatitude()); + } + } + } + return new QuadRect(left, top, right, bottom); + } } public static String asString(GPXFile file, OsmandApplication ctx) { diff --git a/OsmAnd/src/net/osmand/plus/activities/TrackActivity.java b/OsmAnd/src/net/osmand/plus/activities/TrackActivity.java index 564fc109ba..3b01d00303 100644 --- a/OsmAnd/src/net/osmand/plus/activities/TrackActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/TrackActivity.java @@ -1,5 +1,6 @@ package net.osmand.plus.activities; +import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; import android.os.AsyncTask; @@ -9,9 +10,11 @@ import android.support.annotation.Nullable; import android.support.design.widget.BottomNavigationView; import android.support.v4.app.Fragment; import android.support.v7.app.ActionBar; +import android.util.DisplayMetrics; import android.util.Log; import android.view.MenuItem; import android.view.View; +import android.view.WindowManager; import net.osmand.AndroidUtils; import net.osmand.data.LatLon; @@ -20,7 +23,6 @@ import net.osmand.data.QuadRect; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXFile; -import net.osmand.plus.GPXUtilities.Track; import net.osmand.plus.GPXUtilities.TrkSegment; import net.osmand.plus.GPXUtilities.WptPt; import net.osmand.plus.GpxSelectionHelper; @@ -35,7 +37,6 @@ import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment; import net.osmand.plus.measurementtool.NewGpxData; import net.osmand.plus.myplaces.FavoritesActivity; import net.osmand.plus.myplaces.SplitSegmentDialogFragment; -import net.osmand.plus.myplaces.TrackActivityFragmentAdapter; import net.osmand.plus.myplaces.TrackBitmapDrawer; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackPointFragment; @@ -156,56 +157,11 @@ public class TrackActivity extends TabActivity { } public QuadRect getRect() { - double left = 0, right = 0; - double top = 0, bottom = 0; if (getGpx() != null) { - for (Track track : getGpx().tracks) { - for (TrkSegment segment : track.segments) { - for (WptPt p : segment.points) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - } - } - for (WptPt p : getGpx().getPoints()) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - for (GPXUtilities.Route route : getGpx().routes) { - for (WptPt p : route.points) { - if (left == 0 && right == 0) { - left = p.getLongitude(); - right = p.getLongitude(); - top = p.getLatitude(); - bottom = p.getLatitude(); - } else { - left = Math.min(left, p.getLongitude()); - right = Math.max(right, p.getLongitude()); - top = Math.max(top, p.getLatitude()); - bottom = Math.min(bottom, p.getLatitude()); - } - } - } + return getGpx().getRect(); + } else { + return new QuadRect(0, 0, 0, 0); } - return new QuadRect(left, top, right, bottom); } protected void setGpxDataItem(GpxDataItem gpxDataItem) { @@ -383,8 +339,11 @@ public class TrackActivity extends TabActivity { setGpx(gpxFile); setGpxDataItem(file != null ? app.getGpxDatabase().getItem(file) : null); - if (gpxFile != null) { - trackBitmapDrawer = new TrackBitmapDrawer(this, gpxFile, getGpxDataItem(), getRect()); + WindowManager mgr = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + if (gpxFile != null && mgr != null) { + DisplayMetrics dm = new DisplayMetrics(); + mgr.getDefaultDisplay().getMetrics(dm); + trackBitmapDrawer = new TrackBitmapDrawer(app, gpxFile, getGpxDataItem(), getRect(), dm.density, dm.widthPixels, AndroidUtils.dpToPx(app, 152f)); } for (WeakReference f : fragList) { diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackBitmapDrawer.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackBitmapDrawer.java index de4df5d6b3..df3a5fa23e 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackBitmapDrawer.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackBitmapDrawer.java @@ -1,7 +1,5 @@ package net.osmand.plus.myplaces; -import android.app.Activity; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -12,8 +10,6 @@ import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; -import android.util.DisplayMetrics; -import android.view.WindowManager; import net.osmand.AndroidUtils; import net.osmand.data.LatLon; @@ -32,15 +28,16 @@ import net.osmand.plus.resources.ResourceManager; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.Renderable; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; public class TrackBitmapDrawer { private OsmandApplication app; - private WeakReference activityRef; private QuadRect rect; + private float density; + private int widthPixels; + private int heightPixels; private boolean drawEnabled; private RotatedTileBox rotatedTileBox; @@ -69,11 +66,13 @@ public class TrackBitmapDrawer { void drawTrackBitmap(Bitmap bitmap); } - public TrackBitmapDrawer(@NonNull Activity activity, @NonNull GPXFile gpxFile, - @Nullable GpxDataItem gpxDataItem, @NonNull QuadRect rect) { - this.activityRef = new WeakReference<>(activity); + public TrackBitmapDrawer(@NonNull OsmandApplication app, @NonNull GPXFile gpxFile, + @Nullable GpxDataItem gpxDataItem, @NonNull QuadRect rect, float density, int widthPixels, int heightPixels) { + this.density = density; + this.widthPixels = widthPixels; + this.heightPixels = heightPixels; this.rect = rect; - this.app = (OsmandApplication) activity.getApplication(); + this.app = app; this.gpxFile = gpxFile; this.gpxDataItem = gpxDataItem; @@ -83,8 +82,8 @@ public class TrackBitmapDrawer { paint.setStrokeWidth(AndroidUtils.dpToPx(app, 4f)); defPointColor = ContextCompat.getColor(app, R.color.gpx_color_point); paintIcon = new Paint(); - pointSmall = BitmapFactory.decodeResource(activity.getResources(), R.drawable.map_white_shield_small); - selectedPoint = BitmapFactory.decodeResource(activity.getResources(), R.drawable.map_default_location); + pointSmall = BitmapFactory.decodeResource(app.getResources(), R.drawable.map_white_shield_small); + selectedPoint = BitmapFactory.decodeResource(app.getResources(), R.drawable.map_default_location); } public void addListener(TrackBitmapDrawerListener l) { @@ -150,54 +149,48 @@ public class TrackBitmapDrawer { } public boolean initAndDraw() { - Activity activity = activityRef.get(); - if (activity != null) { - WindowManager mgr = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); - if (mgr != null && rect != null && rect.left != 0 && rect.top != 0) { - notifyDrawing(); + if (rect != null && rect.left != 0 && rect.top != 0) { + notifyDrawing(); - double clat = rect.bottom / 2 + rect.top / 2; - double clon = rect.left / 2 + rect.right / 2; - DisplayMetrics dm = new DisplayMetrics(); - mgr.getDefaultDisplay().getMetrics(dm); - RotatedTileBox.RotatedTileBoxBuilder boxBuilder = new RotatedTileBox.RotatedTileBoxBuilder() - .setLocation(clat, clon) - .setZoom(15) - .density(dm.density) - .setPixelDimensions(dm.widthPixels, AndroidUtils.dpToPx(app, 152f), 0.5f, 0.5f); + double clat = rect.bottom / 2 + rect.top / 2; + double clon = rect.left / 2 + rect.right / 2; + RotatedTileBox.RotatedTileBoxBuilder boxBuilder = new RotatedTileBox.RotatedTileBoxBuilder() + .setLocation(clat, clon) + .setZoom(15) + .density(density) + .setPixelDimensions(widthPixels, heightPixels, 0.5f, 0.5f); - rotatedTileBox = boxBuilder.build(); - while (rotatedTileBox.getZoom() < 17 && rotatedTileBox.containsLatLon(rect.top, rect.left) && rotatedTileBox.containsLatLon(rect.bottom, rect.right)) { - rotatedTileBox.setZoom(rotatedTileBox.getZoom() + 1); - } - while (rotatedTileBox.getZoom() >= 7 && (!rotatedTileBox.containsLatLon(rect.top, rect.left) || !rotatedTileBox.containsLatLon(rect.bottom, rect.right))) { - rotatedTileBox.setZoom(rotatedTileBox.getZoom() - 1); - } + rotatedTileBox = boxBuilder.build(); + while (rotatedTileBox.getZoom() < 17 && rotatedTileBox.containsLatLon(rect.top, rect.left) && rotatedTileBox.containsLatLon(rect.bottom, rect.right)) { + rotatedTileBox.setZoom(rotatedTileBox.getZoom() + 1); + } + while (rotatedTileBox.getZoom() >= 7 && (!rotatedTileBox.containsLatLon(rect.top, rect.left) || !rotatedTileBox.containsLatLon(rect.bottom, rect.right))) { + rotatedTileBox.setZoom(rotatedTileBox.getZoom() - 1); + } - final DrawSettings drawSettings = new DrawSettings(!app.getSettings().isLightContent(), true); - final ResourceManager resourceManager = app.getResourceManager(); - final MapRenderRepositories renderer = resourceManager.getRenderer(); - if (resourceManager.updateRenderedMapNeeded(rotatedTileBox, drawSettings)) { - resourceManager.updateRendererMap(rotatedTileBox, new OnMapLoadedListener() { - @Override - public void onMapLoaded(boolean interrupted) { - app.runInUIThread(new Runnable() { - @Override - public void run() { - if (isDrawEnabled()) { - mapBitmap = renderer.getBitmap(); - if (mapBitmap != null) { - notifyDrawn(); - refreshTrackBitmap(); - } + final DrawSettings drawSettings = new DrawSettings(!app.getSettings().isLightContent(), true); + final ResourceManager resourceManager = app.getResourceManager(); + final MapRenderRepositories renderer = resourceManager.getRenderer(); + if (resourceManager.updateRenderedMapNeeded(rotatedTileBox, drawSettings)) { + resourceManager.updateRendererMap(rotatedTileBox, new OnMapLoadedListener() { + @Override + public void onMapLoaded(boolean interrupted) { + app.runInUIThread(new Runnable() { + @Override + public void run() { + if (isDrawEnabled()) { + mapBitmap = renderer.getBitmap(); + if (mapBitmap != null) { + notifyDrawn(); + refreshTrackBitmap(); } } - }); - } - }); - } - return true; + } + }); + } + }); } + return true; } return false; }