Initial mapbox based maps implementation

This commit is contained in:
Marvin W 2019-04-22 19:53:55 +02:00
parent 2a01e6cb74
commit 6174997cb7
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
21 changed files with 1949 additions and 3 deletions

View File

@ -9,7 +9,7 @@ microG GmsCore is a FLOSS (Free/Libre Open Source Software) framework to allow a
License License
------- -------
Copyright 2014-2016 microG Project Team Copyright 2013-2019 microG Project Team
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -16,6 +16,12 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
def useMapbox() {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
return properties.getProperty("mapbox.enabled", "false") == "true"
}
dependencies { dependencies {
implementation "com.android.support:support-v4:$supportLibraryVersion" implementation "com.android.support:support-v4:$supportLibraryVersion"
implementation "com.android.support:appcompat-v7:$supportLibraryVersion" implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
@ -39,8 +45,12 @@ dependencies {
implementation project(':wearable-lib') implementation project(':wearable-lib')
implementation project(':remote-droid-guard-lib') implementation project(':remote-droid-guard-lib')
if (useMapbox()) {
implementation project(':play-services-maps-core-mapbox')
} else {
implementation project(':play-services-maps-core-vtm') implementation project(':play-services-maps-core-vtm')
} }
}
def execResult(...args) { def execResult(...args) {
def stdout = new ByteArrayOutputStream() def stdout = new ByteArrayOutputStream()
@ -60,7 +70,7 @@ def gitDirty = execResult('git', 'status', '--porcelain').size() > 0
def ourVersionBase = gitVersionBase.substring(0, gitVersionBase.lastIndexOf('.')) def ourVersionBase = gitVersionBase.substring(0, gitVersionBase.lastIndexOf('.'))
def ourVersionMinor = Integer.parseInt(ourVersionBase.substring(ourVersionBase.lastIndexOf('.') + 1)) def ourVersionMinor = Integer.parseInt(ourVersionBase.substring(ourVersionBase.lastIndexOf('.') + 1))
def ourVersionCode = gmsVersionCode * 1000 + ourVersionMinor * 2 + (gitCommitCount > 0 || gitDirty ? 1 : 0) def ourVersionCode = gmsVersionCode * 1000 + ourVersionMinor * 2 + (gitCommitCount > 0 || gitDirty ? 1 : 0)
def ourVersionName = "$ourVersionBase.$gmsVersionCode" + (gitCommitCount > 0 && !gitDirty ? "-$gitCommitCount" : "") + (gitDirty ? "-dirty" : "") + (gitCommitCount > 0 && !gitDirty ? " ($gitCommitId)" : "") def ourVersionName = "$ourVersionBase.$gmsVersionCode" + (gitCommitCount > 0 && !gitDirty ? "-$gitCommitCount" : "") + (gitDirty ? "-dirty" : "") + (useMapbox() ? "-mapbox" : "") + (gitCommitCount > 0 && !gitDirty ? " ($gitCommitId)" : "")
logger.lifecycle('Starting build for version {} ({})...', ourVersionName, ourVersionCode) logger.lifecycle('Starting build for version {} ({})...', ourVersionName, ourVersionCode)
android { android {

View File

@ -0,0 +1,83 @@
/*
* Copyright 2013-2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
dependencies {
implementation project(':play-services-api')
implementation "com.mapbox.mapboxsdk:mapbox-android-sdk:7.3.0"
implementation "com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v7:0.6.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
def execResult(...args) {
def stdout = new ByteArrayOutputStream()
exec {
commandLine args
standardOutput = stdout
}
return stdout.toString().trim()
}
def mapboxKey() {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
return properties.getProperty("mapbox.key", "invalid")
}
android {
compileSdkVersion androidCompileSdk()
buildToolsVersion "$androidBuildVersionTools"
defaultConfig {
versionName "temp"
versionCode 1
minSdkVersion androidMinSdk()
targetSdkVersion androidTargetSdk()
buildConfigField "String", "MAPBOX_KEY", "\"${mapboxKey()}\""
ndk {
abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'GradleCompatible'
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
if (file('user.gradle').exists()) {
apply from: 'user.gradle'
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2013-2019 microG Project Team
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="org.microg.gms.maps.mapbox"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application />
</manifest>

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.gms.maps.internal;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.Keep;
import android.util.Log;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
import com.google.android.gms.maps.GoogleMapOptions;
import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate;
import org.microg.gms.maps.mapbox.CameraUpdateFactoryImpl;
import org.microg.gms.maps.mapbox.MapFragmentImpl;
import org.microg.gms.maps.mapbox.MapViewImpl;
import org.microg.gms.maps.mapbox.model.BitmapDescriptorFactoryImpl;
@Keep
public class CreatorImpl extends ICreator.Stub {
private static final String TAG = "GmsMapCreator";
@Override
public void init(IObjectWrapper resources) {
initV2(resources, 0);
}
@Override
public IMapFragmentDelegate newMapFragmentDelegate(IObjectWrapper activity) {
return new MapFragmentImpl(ObjectWrapper.unwrapTyped(activity, Activity.class));
}
@Override
public IMapViewDelegate newMapViewDelegate(IObjectWrapper context, GoogleMapOptions options) {
return new MapViewImpl(ObjectWrapper.unwrapTyped(context, Context.class), options);
}
@Override
public ICameraUpdateFactoryDelegate newCameraUpdateFactoryDelegate() {
return new CameraUpdateFactoryImpl();
}
@Override
public IBitmapDescriptorFactoryDelegate newBitmapDescriptorFactoryDelegate() {
return BitmapDescriptorFactoryImpl.INSTANCE;
}
@Override
public void initV2(IObjectWrapper resources, int flags) {
BitmapDescriptorFactoryImpl.INSTANCE.initialize(ObjectWrapper.unwrapTyped(resources, Resources.class));
//ResourcesContainer.set((Resources) ObjectWrapper.unwrap(resources));
Log.d(TAG, "initV2");
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (super.onTransact(code, data, reply, flags)) return true;
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
return false;
}
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.camera.CameraUpdate
import com.mapbox.mapboxsdk.geometry.LatLngBounds
import com.mapbox.mapboxsdk.maps.MapboxMap
import java.util.*
internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: Int, val height: Int, val padding: IntArray) : CameraUpdate {
constructor(bounds: LatLngBounds, width: Int, height: Int, paddingLeft: Int, paddingTop: Int = paddingLeft, paddingRight: Int = paddingLeft, paddingBottom: Int = paddingTop) : this(bounds, width, height, intArrayOf(paddingLeft, paddingTop, paddingRight, paddingBottom)) {}
override fun getCameraPosition(map: MapboxMap): CameraPosition? {
val padding = this.padding.clone()
val widthPad = (map.padding[0] + map.padding[2])/2
val heightPad = (map.padding[1] + map.padding[3])/2
padding[0] += widthPad
padding[1] += heightPad
padding[2] += widthPad
padding[3] += heightPad
return map.getCameraForLatLngBounds(bounds, padding)
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other == null || other !is CameraBoundsWithSizeUpdate?) {
return false
}
val that = other as CameraBoundsWithSizeUpdate? ?: return false
if (bounds != that.bounds) {
return false
}
if (Arrays.equals(padding, that.padding)) {
return false
}
if (height != that.height || width != that.width) {
return false
}
return true
}
override fun hashCode(): Int {
var result = bounds.hashCode()
result = 31 * result + Arrays.hashCode(padding)
result = 31 * result + height.hashCode()
result = 31 * result + width.hashCode()
return result
}
override fun toString(): String {
return ("CameraBoundsWithSizeUpdate{"
+ "bounds=" + bounds
+ ", padding=" + Arrays.toString(padding)
+ '}'.toString())
}
companion object {
val TAG = "GmsCameraBounds"
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.graphics.Point
import android.os.Parcel
import android.util.Log
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.dynamic.ObjectWrapper
import com.google.android.gms.maps.internal.ICameraUpdateFactoryDelegate
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.mapbox.mapboxsdk.camera.CameraUpdate
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
import com.mapbox.mapboxsdk.maps.MapboxMap
import org.microg.gms.maps.mapbox.utils.toMapbox
class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() {
override fun zoomIn(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomIn())
override fun zoomOut(): IObjectWrapper = ObjectWrapper.wrap(CameraUpdateFactory.zoomOut())
override fun zoomTo(zoom: Float): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.zoomTo(zoom.toDouble() - 1.0))
override fun zoomBy(zoomDelta: Float): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta.toDouble()))
override fun zoomByWithFocus(zoomDelta: Float, x: Int, y: Int): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.zoomBy(zoomDelta.toDouble(), Point(x, y)))
override fun newCameraPosition(cameraPosition: CameraPosition): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.newCameraPosition(cameraPosition.toMapbox()))
override fun newLatLng(latLng: LatLng): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.newLatLng(latLng.toMapbox()))
override fun newLatLngZoom(latLng: LatLng, zoom: Float): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.newLatLngZoom(latLng.toMapbox(), zoom.toDouble() - 1.0))
override fun newLatLngBounds(bounds: LatLngBounds, padding: Int): IObjectWrapper =
ObjectWrapper.wrap(CameraUpdateFactory.newLatLngBounds(bounds.toMapbox(), padding))
override fun scrollBy(x: Float, y: Float): IObjectWrapper {
Log.d(TAG, "unimplemented Method: scrollBy")
return ObjectWrapper.wrap(NoCameraUpdate())
}
override fun newLatLngBoundsWithSize(bounds: LatLngBounds, width: Int, height: Int, padding: Int): IObjectWrapper {
Log.d(TAG, "unimplemented Method: newLatLngBoundsWithSize")
return ObjectWrapper.wrap(CameraBoundsWithSizeUpdate(bounds.toMapbox(), width, height, padding))
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
private inner class NoCameraUpdate : CameraUpdate {
override fun getCameraPosition(mapboxMap: MapboxMap): com.mapbox.mapboxsdk.camera.CameraPosition? =
mapboxMap.cameraPosition
}
companion object {
private val TAG = "GmsCameraUpdate"
}
}

View File

@ -0,0 +1,479 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.content.Context
import android.location.Location
import android.os.Bundle
import android.os.Parcel
import android.os.RemoteException
import android.support.annotation.IdRes
import android.support.annotation.Keep
import android.support.v4.util.LongSparseArray
import android.util.DisplayMetrics
import android.util.Log
import android.view.View
import android.widget.FrameLayout
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.gms.maps.internal.*
import com.google.android.gms.maps.model.*
import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.internal.*
import com.mapbox.mapboxsdk.LibraryLoader
import com.mapbox.mapboxsdk.Mapbox
import com.mapbox.mapboxsdk.R
import com.mapbox.mapboxsdk.camera.CameraUpdate
import com.mapbox.mapboxsdk.maps.MapView
import com.mapbox.mapboxsdk.maps.MapboxMap
import com.mapbox.mapboxsdk.maps.Style
import com.mapbox.mapboxsdk.plugins.annotation.*
import com.mapbox.mapboxsdk.plugins.annotation.Annotation
import com.mapbox.mapboxsdk.style.layers.Property.LINE_CAP_ROUND
import com.mapbox.mapboxsdk.utils.ColorUtils
import org.microg.gms.kotlin.unwrap
import org.microg.gms.maps.MapsConstants.*
import org.microg.gms.maps.mapbox.model.*
import org.microg.gms.maps.mapbox.utils.MapContext
import org.microg.gms.maps.mapbox.utils.MultiArchLoader
import org.microg.gms.maps.mapbox.utils.toGms
import org.microg.gms.maps.mapbox.utils.toMapbox
fun <T : Any> LongSparseArray<T>.values() = (0..size()).map { valueAt(it) }.mapNotNull { it }
class GoogleMapImpl(private val context: Context, private val options: GoogleMapOptions) : IGoogleMapDelegate.Stub() {
val view: FrameLayout
var map: MapboxMap? = null
private set
val dpiFactor: Float
get() = context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
private var mapView: MapView?
private var initialized = false
private val initializedCallbackList = mutableListOf<IOnMapReadyCallback>()
private val mapLock = Object()
val markers = mutableMapOf<Long, MarkerImpl>()
private var cameraChangeListener: IOnCameraChangeListener? = null
private var cameraMoveListener: IOnCameraMoveListener? = null
private var cameraMoveCanceledListener: IOnCameraMoveCanceledListener? = null
private var cameraMoveStartedListener: IOnCameraMoveStartedListener? = null
private var cameraIdleListener: IOnCameraIdleListener? = null
private var mapClickListener: IOnMapClickListener? = null
private var mapLongClickListener: IOnMapLongClickListener? = null
private var markerClickListener: IOnMarkerClickListener? = null
private var markerDragListener: IOnMarkerDragListener? = null
var circleManager: CircleManager? = null
var lineManager: LineManager? = null
var fillManager: FillManager? = null
var symbolManager: SymbolManager? = null
var storedMapType: Int = MAP_TYPE_NORMAL
init {
val mapContext = MapContext(context)
LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext, context))
Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY)
this.view = object : FrameLayout(mapContext) {
@Keep
fun <T : View> findViewTraversal(@IdRes id: Int): T? {
return null
}
}
this.mapView = MapView(mapContext)
this.view.addView(this.mapView)
}
override fun getCameraPosition(): CameraPosition? = map?.cameraPosition?.toGms()
override fun getMaxZoomLevel(): Float = map?.maxZoomLevel?.toFloat() ?: 20f
override fun getMinZoomLevel(): Float = map?.minZoomLevel?.toFloat() ?: 1f
override fun moveCamera(cameraUpdate: IObjectWrapper?) =
cameraUpdate.unwrap<CameraUpdate>()?.let { map?.moveCamera(it) } ?: Unit
override fun animateCamera(cameraUpdate: IObjectWrapper?) =
cameraUpdate.unwrap<CameraUpdate>()?.let { map?.animateCamera(it) } ?: Unit
override fun animateCameraWithCallback(cameraUpdate: IObjectWrapper?, callback: ICancelableCallback?) =
cameraUpdate.unwrap<CameraUpdate>()?.let { map?.animateCamera(it, callback?.toMapbox()) }
?: Unit
override fun animateCameraWithDurationAndCallback(cameraUpdate: IObjectWrapper?, duration: Int, callback: ICancelableCallback?) =
cameraUpdate.unwrap<CameraUpdate>()?.let { map?.animateCamera(it, duration, callback?.toMapbox()) }
?: Unit
override fun stopAnimation() = map?.cancelTransitions() ?: Unit
override fun addPolyline(options: PolylineOptions): IPolylineDelegate? {
val lineOptions = LineOptions()
.withLatLngs(options.points.map { it.toMapbox() })
.withLineWidth(options.width / dpiFactor)
.withLineColor(ColorUtils.colorToRgbaString(options.color))
.withLineOpacity(if (options.isVisible) 1f else 0f)
return lineManager?.let { PolylineImpl(this, it.create(lineOptions)) }
}
override fun addPolygon(options: PolygonOptions): IPolygonDelegate? {
Log.d(TAG, "unimplemented Method: addPolygon")
return null
}
override fun addMarker(options: MarkerOptions): IMarkerDelegate? {
var intBits = java.lang.Float.floatToIntBits(options.zIndex)
if (intBits < 0) intBits = intBits xor 0x7fffffff
val symbolOptions = SymbolOptions()
.withIconOpacity(if (options.isVisible) options.alpha else 0f)
.withIconRotate(options.rotation)
.withZIndex(intBits)
.withDraggable(options.isDraggable)
options.position?.let { symbolOptions.withLatLng(it.toMapbox()) }
options.icon?.remoteObject.unwrap<BitmapDescriptorImpl>()?.applyTo(symbolOptions, floatArrayOf(options.anchorU, options.anchorV), dpiFactor)
val symbol = symbolManager?.create(symbolOptions) ?: return null
val marker = MarkerImpl(this, symbol, floatArrayOf(options.anchorU, options.anchorV), options.icon?.remoteObject.unwrap<BitmapDescriptorImpl>(), options.alpha, options.title, options.snippet)
markers.put(symbol.id, marker)
return marker
}
override fun addGroundOverlay(options: GroundOverlayOptions): IGroundOverlayDelegate? {
Log.d(TAG, "unimplemented Method: addGroundOverlay")
return null
}
override fun addTileOverlay(options: TileOverlayOptions): ITileOverlayDelegate? {
Log.d(TAG, "unimplemented Method: addTileOverlay")
return null
}
override fun addCircle(options: CircleOptions): ICircleDelegate? {
val circleOptions = com.mapbox.mapboxsdk.plugins.annotation.CircleOptions()
.withLatLng(options.center.toMapbox())
.withCircleColor(ColorUtils.colorToRgbaString(options.fillColor))
.withCircleRadius(options.radius.toFloat())
.withCircleStrokeColor(ColorUtils.colorToRgbaString(options.strokeColor))
.withCircleStrokeWidth(options.strokeWidth / dpiFactor)
.withCircleOpacity(if (options.isVisible) 1f else 0f)
.withCircleStrokeOpacity(if (options.isVisible) 1f else 0f)
return circleManager?.let { CircleImpl(this, it.create(circleOptions)) }
}
override fun clear() {
circleManager?.let { clear(it) }
lineManager?.let { clear(it) }
fillManager?.let { clear(it) }
symbolManager?.let { clear(it) }
}
fun <T : Annotation<*>> clear(manager: AnnotationManager<*, T, *, *, *, *>) {
val annotations = manager.getAnnotations()
for (i in 0..annotations.size()) {
val key = annotations.keyAt(i)
val value = annotations[key];
if (value is T) manager.delete(value)
}
}
override fun getMapType(): Int {
return storedMapType
}
override fun setMapType(type: Int) {
storedMapType = type
applyMapType()
}
fun applyMapType() {
val circles = circleManager?.annotations?.values()
val lines = lineManager?.annotations?.values()
val fills = fillManager?.annotations?.values()
val symbols = symbolManager?.annotations?.values()
val update: (Style) -> Unit = {
circles?.let { circleManager?.update(it) }
lines?.let { lineManager?.update(it) }
fills?.let { fillManager?.update(it) }
symbols?.let { symbolManager?.update(it) }
}
when (storedMapType) {
MAP_TYPE_NORMAL -> map?.setStyle(Style.Builder().fromUrl("mapbox://styles/microg/cjui4020201oo1fmca7yuwbor"), update)
MAP_TYPE_SATELLITE -> map?.setStyle(Style.SATELLITE, update)
MAP_TYPE_TERRAIN -> map?.setStyle(Style.OUTDOORS, update)
MAP_TYPE_HYBRID -> map?.setStyle(Style.SATELLITE_STREETS, update)
else -> map?.setStyle(Style.LIGHT, update)
}
}
override fun isTrafficEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isTrafficEnabled")
return false
}
override fun setTrafficEnabled(traffic: Boolean) {
Log.d(TAG, "unimplemented Method: setTrafficEnabled")
}
override fun isIndoorEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isIndoorEnabled")
return false
}
override fun setIndoorEnabled(indoor: Boolean) {
Log.d(TAG, "unimplemented Method: setIndoorEnabled")
}
override fun isMyLocationEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isMyLocationEnabled")
return false
}
override fun setMyLocationEnabled(myLocation: Boolean) {
Log.d(TAG, "unimplemented Method: setMyLocationEnabled")
}
override fun getMyLocation(): Location? {
Log.d(TAG, "unimplemented Method: getMyLocation")
return null
}
override fun setLocationSource(locationSource: ILocationSourceDelegate) {
Log.d(TAG, "unimplemented Method: setLocationSource")
}
override fun getUiSettings(): IUiSettingsDelegate? = map?.uiSettings?.let { UiSettingsImpl(it) }
override fun getProjection(): IProjectionDelegate? = map?.projection?.let { ProjectionImpl(it) }
override fun setOnCameraChangeListener(listener: IOnCameraChangeListener?) {
cameraChangeListener = listener
}
override fun setOnMapClickListener(listener: IOnMapClickListener?) {
mapClickListener = listener
}
override fun setOnMapLongClickListener(listener: IOnMapLongClickListener?) {
mapLongClickListener = listener
}
override fun setOnMarkerClickListener(listener: IOnMarkerClickListener?) {
markerClickListener = listener
}
override fun setOnMarkerDragListener(listener: IOnMarkerDragListener?) {
markerDragListener = listener
}
override fun setOnInfoWindowClickListener(listener: IOnInfoWindowClickListener?) {
Log.d(TAG, "unimplemented Method: setOnInfoWindowClickListener")
}
override fun setInfoWindowAdapter(adapter: IInfoWindowAdapter?) {
Log.d(TAG, "unimplemented Method: setInfoWindowAdapter")
}
override fun getTestingHelper(): IObjectWrapper? {
Log.d(TAG, "unimplemented Method: getTestingHelper")
return null
}
override fun setOnMyLocationChangeListener(listener: IOnMyLocationChangeListener?) {
Log.d(TAG, "unimplemented Method: setOnMyLocationChangeListener")
}
override fun setOnMyLocationButtonClickListener(listener: IOnMyLocationButtonClickListener?) {
Log.d(TAG, "unimplemented Method: setOnMyLocationButtonClickListener")
}
override fun snapshot(callback: ISnapshotReadyCallback, bitmap: IObjectWrapper) {
Log.d(TAG, "unimplemented Method: snapshot")
}
override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
Log.d(TAG, "padding: $left, $top, $right, $bottom")
map?.setPadding(left, top, right, bottom)
val fourDp = mapView?.context?.resources?.getDimension(R.dimen.mapbox_four_dp)?.toInt() ?: 0
val ninetyTwoDp = mapView?.context?.resources?.getDimension(R.dimen.mapbox_ninety_two_dp)?.toInt()
?: 0
map?.uiSettings?.setLogoMargins(left + fourDp, top + fourDp, right + fourDp, bottom + fourDp)
map?.uiSettings?.setCompassMargins(left + fourDp, top + fourDp, right + fourDp, bottom + fourDp)
map?.uiSettings?.setAttributionMargins(left + ninetyTwoDp, top + fourDp, right + fourDp, bottom + fourDp)
}
override fun isBuildingsEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isBuildingsEnabled")
return false
}
override fun setBuildingsEnabled(buildings: Boolean) {
Log.d(TAG, "unimplemented Method: setBuildingsEnabled")
}
override fun setOnMapLoadedCallback(callback: IOnMapLoadedCallback?) {
Log.d(TAG, "unimplemented Method: setOnMapLoadedCallback")
}
override fun setCameraMoveStartedListener(listener: IOnCameraMoveStartedListener?) {
cameraMoveStartedListener = listener
}
override fun setCameraMoveListener(listener: IOnCameraMoveListener?) {
cameraMoveListener = listener
}
override fun setCameraMoveCanceledListener(listener: IOnCameraMoveCanceledListener?) {
cameraMoveCanceledListener = listener
}
override fun setCameraIdleListener(listener: IOnCameraIdleListener?) {
cameraIdleListener = listener
}
fun onCreate(savedInstanceState: Bundle) {
mapView?.onCreate(savedInstanceState.toMapbox())
mapView?.getMapAsync(this::initMap)
}
private fun hasSymbolAt(latlng: com.mapbox.mapboxsdk.geometry.LatLng): Boolean {
val point = map?.projection?.toScreenLocation(latlng) ?: return false
val features = map?.queryRenderedFeatures(point, SymbolManager.ID_GEOJSON_LAYER)
?: return false
return !features.isEmpty()
}
private fun initMap(map: MapboxMap) {
if (this.map != null) return
this.map = map
applyMapType()
map.getStyle {
mapView?.let { view ->
BitmapDescriptorFactoryImpl.registerMap(map)
circleManager = CircleManager(view, map, it)
lineManager = LineManager(view, map, it)
lineManager?.lineCap = LINE_CAP_ROUND
fillManager = FillManager(view, map, it)
symbolManager = SymbolManager(view, map, it)
symbolManager?.iconAllowOverlap = true
symbolManager?.addClickListener { markers[it.id]?.let { markerClickListener?.onMarkerClick(it) } }
symbolManager?.addDragListener(object : OnSymbolDragListener {
override fun onAnnotationDragStarted(annotation: Symbol?) {
markers[annotation?.id]?.let { markerDragListener?.onMarkerDragStart(it) }
}
override fun onAnnotationDrag(annotation: Symbol?) {
markers[annotation?.id]?.let { markerDragListener?.onMarkerDrag(it) }
}
override fun onAnnotationDragFinished(annotation: Symbol?) {
markers[annotation?.id]?.let { markerDragListener?.onMarkerDragEnd(it) }
}
})
map.addOnCameraIdleListener { cameraChangeListener?.onCameraChange(map.cameraPosition.toGms()) }
map.addOnCameraIdleListener { cameraIdleListener?.onCameraIdle() }
map.addOnCameraMoveListener { cameraMoveListener?.onCameraMove() }
map.addOnCameraMoveStartedListener { cameraMoveStartedListener?.onCameraMoveStarted(it) }
map.addOnCameraMoveCancelListener { cameraMoveCanceledListener?.onCameraMoveCanceled() }
map.addOnMapClickListener {
val latlng = it
mapClickListener?.let { if (!hasSymbolAt(latlng)) it.onMapClick(latlng.toGms()); }
false
}
map.addOnMapLongClickListener {
val latlng = it
mapLongClickListener?.let { if (!hasSymbolAt(latlng)) it.onMapLongClick(latlng.toGms()); }
false
}
synchronized(mapLock) {
for (callback in initializedCallbackList) {
try {
callback.onMapReady(this)
} catch (e: RemoteException) {
Log.w(TAG, e)
}
}
initialized = true
}
}
}
}
fun onResume() = mapView?.onResume()
fun onPause() = mapView?.onPause()
fun onDestroy() {
circleManager?.onDestroy()
circleManager = null
lineManager?.onDestroy()
lineManager = null
fillManager?.onDestroy()
fillManager = null
symbolManager?.onDestroy()
symbolManager = null
BitmapDescriptorFactoryImpl.unregisterMap(map)
view.removeView(mapView)
// TODO can crash?
mapView?.onDestroy()
mapView = null
}
fun onLowMemory() = mapView?.onLowMemory()
fun onSaveInstanceState(outState: Bundle) {
val newBundle = Bundle()
mapView?.onSaveInstanceState(newBundle)
outState.putAll(newBundle.toGms())
}
fun getMapAsync(callback: IOnMapReadyCallback) {
synchronized(mapLock) {
if (initialized) {
callback.onMapReady(this)
} else {
initializedCallbackList.add(callback)
}
}
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
private val TAG = "GmsMap"
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.app.Activity
import android.os.Bundle
import android.os.Parcel
import android.util.Log
import android.view.ViewGroup
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.dynamic.ObjectWrapper
import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.gms.maps.internal.IGoogleMapDelegate
import com.google.android.gms.maps.internal.IMapFragmentDelegate
import com.google.android.gms.maps.internal.IOnMapReadyCallback
class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stub() {
private var map: GoogleMapImpl? = null
private var options: GoogleMapOptions? = null
override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle) {
this.options = options
}
override fun onCreate(savedInstanceState: Bundle) {
if (options == null) {
options = savedInstanceState.getParcelable("MapOptions")
}
if (options == null) {
options = GoogleMapOptions()
}
}
override fun onCreateView(layoutInflater: IObjectWrapper, container: IObjectWrapper, savedInstanceState: Bundle): IObjectWrapper {
if (map == null) {
map = GoogleMapImpl(activity, options ?: GoogleMapOptions())
map!!.onCreate(savedInstanceState)
return ObjectWrapper.wrap(map!!.view)
} else {
val view = map!!.view
val parent = view?.parent as ViewGroup
parent.removeView(view)
return ObjectWrapper.wrap(view)
}
}
override fun getMap(): IGoogleMapDelegate? = map
override fun onResume() = map?.onResume() ?: Unit
override fun onPause() = map?.onPause() ?: Unit
override fun onLowMemory() = map?.onLowMemory() ?: Unit
override fun isReady(): Boolean = this.map != null
override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit
override fun onDestroyView() {
map?.onDestroy()
map = null
}
override fun onDestroy() {
map?.onDestroy()
map = null
options = null
}
override fun onSaveInstanceState(outState: Bundle) {
if (options != null) {
outState.putParcelable("MapOptions", options)
}
map?.onSaveInstanceState(outState)
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
private val TAG = "GmsMapFragment"
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.content.Context
import android.os.Bundle
import android.os.Parcel
import android.util.Log
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.dynamic.ObjectWrapper
import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.gms.maps.internal.IGoogleMapDelegate
import com.google.android.gms.maps.internal.IMapViewDelegate
import com.google.android.gms.maps.internal.IOnMapReadyCallback
class MapViewImpl(private val context: Context, options: GoogleMapOptions?) : IMapViewDelegate.Stub() {
private val options: GoogleMapOptions
private var map: GoogleMapImpl? = null
init {
this.options = options ?: GoogleMapOptions()
}
override fun onCreate(savedInstanceState: Bundle) {
map = GoogleMapImpl(context, options)
map?.onCreate(savedInstanceState)
}
override fun getMap(): IGoogleMapDelegate? = map
override fun onResume() = map?.onResume() ?: Unit
override fun onPause() = map?.onPause() ?: Unit
override fun onDestroy() {
map?.onDestroy()
map = null
}
override fun onLowMemory() = map?.onLowMemory() ?: Unit
override fun onSaveInstanceState(outState: Bundle) = map?.onSaveInstanceState(outState) ?: Unit
override fun getView(): IObjectWrapper = ObjectWrapper.wrap(map?.view)
override fun getMapAsync(callback: IOnMapReadyCallback) = map?.getMapAsync(callback) ?: Unit
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
private val TAG = "GmsMapView"
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.graphics.Point
import android.graphics.PointF
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.dynamic.ObjectWrapper
import com.google.android.gms.maps.internal.IProjectionDelegate
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.android.gms.maps.model.VisibleRegion
import com.mapbox.mapboxsdk.maps.Projection
import org.microg.gms.kotlin.unwrap
import org.microg.gms.maps.mapbox.utils.toGms
import org.microg.gms.maps.mapbox.utils.toMapbox
class ProjectionImpl(private val projection: Projection) : IProjectionDelegate.Stub() {
override fun fromScreenLocation(obj: IObjectWrapper?): LatLng? =
obj.unwrap<Point>()?.let { projection.fromScreenLocation(PointF(it)) }?.toGms()
override fun toScreenLocation(latLng: LatLng?): IObjectWrapper =
ObjectWrapper.wrap(latLng?.toMapbox()?.let { projection.toScreenLocation(it) }?.let { Point(it.x.toInt(), it.y.toInt()) })
override fun getVisibleRegion(): VisibleRegion = try {
projection.visibleRegion.toGms()
} catch (e: Exception) {
VisibleRegion(LatLngBounds(LatLng(0.0, 0.0), LatLng(0.0, 0.0)))
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox
import android.os.Parcel
import android.os.RemoteException
import android.util.Log
import com.google.android.gms.maps.internal.IUiSettingsDelegate
import com.mapbox.mapboxsdk.maps.UiSettings
class UiSettingsImpl(private val uiSettings: UiSettings) : IUiSettingsDelegate.Stub() {
override fun setZoomControlsEnabled(zoom: Boolean) {
Log.d(TAG, "unimplemented Method: setZoomControlsEnabled")
}
override fun setCompassEnabled(compass: Boolean) {
uiSettings.isCompassEnabled = compass
}
override fun setMyLocationButtonEnabled(locationButton: Boolean) {
Log.d(TAG, "unimplemented Method: setMyLocationButtonEnabled")
}
override fun setScrollGesturesEnabled(scrollGestures: Boolean) {
uiSettings.isScrollGesturesEnabled = scrollGestures
}
override fun setZoomGesturesEnabled(zoomGestures: Boolean) {
uiSettings.isZoomGesturesEnabled = zoomGestures
}
override fun setTiltGesturesEnabled(tiltGestures: Boolean) {
uiSettings.isTiltGesturesEnabled = tiltGestures
}
override fun setRotateGesturesEnabled(rotateGestures: Boolean) {
uiSettings.isRotateGesturesEnabled = rotateGestures
}
override fun setAllGesturesEnabled(gestures: Boolean) {
uiSettings.setAllGesturesEnabled(gestures)
}
override fun isZoomControlsEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isZoomControlsEnabled")
return false
}
override fun isCompassEnabled(): Boolean = uiSettings.isCompassEnabled
override fun isMyLocationButtonEnabled(): Boolean {
Log.d(TAG, "unimplemented Method: isMyLocationButtonEnabled")
return false
}
override fun isScrollGesturesEnabled(): Boolean = uiSettings.isScrollGesturesEnabled
override fun isZoomGesturesEnabled(): Boolean = uiSettings.isZoomGesturesEnabled
override fun isTiltGesturesEnabled(): Boolean = uiSettings.isTiltGesturesEnabled
override fun isRotateGesturesEnabled(): Boolean = uiSettings.isRotateGesturesEnabled
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
private val TAG = "GmsMapsUi"
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.model
import android.graphics.Color
import android.graphics.PointF
import android.util.Log
import com.mapbox.mapboxsdk.plugins.annotation.Symbol
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
import com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_TOP_LEFT
import com.mapbox.mapboxsdk.utils.ColorUtils
open class BitmapDescriptorImpl(private val id: String, private val size: FloatArray) {
open fun applyTo(options: SymbolOptions, anchor: FloatArray, dpiFactor: Float): SymbolOptions {
return options.withIconImage(id).withIconAnchor(ICON_ANCHOR_TOP_LEFT).withIconOffset(arrayOf(-anchor[0] * size[0] / dpiFactor, -anchor[1] * size[1] / dpiFactor))
}
open fun applyTo(symbol: Symbol, anchor: FloatArray, dpiFactor: Float) {
symbol.iconAnchor = ICON_ANCHOR_TOP_LEFT
symbol.iconOffset = PointF(-anchor[0] * size[0] / dpiFactor, -anchor[1] * size[1] / dpiFactor)
symbol.iconImage = id
}
}
class ColorBitmapDescriptorImpl(id: String, size: FloatArray, val hue: Float) : BitmapDescriptorImpl(id, size) {
override fun applyTo(options: SymbolOptions, anchor: FloatArray, dpiFactor: Float): SymbolOptions = super.applyTo(options, anchor, dpiFactor).withIconColor(ColorUtils.colorToRgbaString(Color.HSVToColor(floatArrayOf(hue, 1.0f, 0.5f))))
override fun applyTo(symbol: Symbol, anchor: FloatArray, dpiFactor: Float) {
super.applyTo(symbol, anchor, dpiFactor)
symbol.setIconColor(Color.HSVToColor(floatArrayOf(hue, 1.0f, 0.5f)))
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.model
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Parcel
import android.util.Log
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.dynamic.ObjectWrapper
import com.google.android.gms.maps.model.internal.IBitmapDescriptorFactoryDelegate
import com.mapbox.mapboxsdk.maps.MapboxMap
object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() {
private val TAG = "GmsMapBitmap"
private var resources: Resources? = null
private val maps = hashSetOf<MapboxMap>()
private val bitmaps = hashMapOf<String, Bitmap>()
fun initialize(resources: Resources) {
BitmapDescriptorFactoryImpl.resources = resources
}
fun registerMap(map: MapboxMap) {
map.getStyle {
it.addImages(bitmaps)
maps.add(map)
}
}
fun unregisterMap(map: MapboxMap?) {
maps.remove(map)
// TODO: cleanup bitmaps?
}
fun bitmapSize(id: String): FloatArray =
bitmaps[id]?.let { floatArrayOf(it.width.toFloat(), it.height.toFloat()) }
?: floatArrayOf(0f, 0f)
private fun registerBitmap(id: String, bitmapCreator: () -> Bitmap?) {
if (bitmaps.contains(id)) return
val bitmap = bitmapCreator() ?: return
bitmaps[id] = bitmap
for (map in maps) {
map.getStyle { it.addImage(id, bitmap) }
}
}
override fun fromResource(resourceId: Int): IObjectWrapper? {
val id = "resource-$resourceId"
registerBitmap(id) { BitmapFactory.decodeResource(resources, resourceId) }
return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id)))
}
override fun fromAsset(assetName: String): IObjectWrapper? {
val id = "asset-$assetName"
registerBitmap(id) { resources?.assets?.open(assetName)?.let { BitmapFactory.decodeStream(it) } }
return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id)))
}
override fun fromFile(fileName: String): IObjectWrapper? {
val id = "file-$fileName"
registerBitmap(id) { BitmapFactory.decodeFile(fileName) }
return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id)))
}
override fun defaultMarker(): IObjectWrapper? {
Log.d(TAG, "unimplemented Method: defaultMarker")
val id = "marker"
return ObjectWrapper.wrap(BitmapDescriptorImpl("marker", bitmapSize(id)))
}
override fun defaultMarkerWithHue(hue: Float): IObjectWrapper? {
val id = "marker"
Log.d(TAG, "unimplemented Method: defaultMarkerWithHue")
return ObjectWrapper.wrap(ColorBitmapDescriptorImpl("marker", bitmapSize(id), hue))
}
override fun fromBitmap(bitmap: Bitmap): IObjectWrapper? {
val id = "bitmap-${bitmap.hashCode()}"
registerBitmap(id) { bitmap }
return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id)))
}
override fun fromPath(absolutePath: String): IObjectWrapper? {
val id = "path-$absolutePath"
registerBitmap(id) { BitmapFactory.decodeFile(absolutePath) }
return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id)))
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.model
import android.os.Parcel
import android.util.Log
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.internal.ICircleDelegate
import com.mapbox.mapboxsdk.plugins.annotation.Circle
import org.microg.gms.maps.mapbox.GoogleMapImpl
import org.microg.gms.maps.mapbox.utils.toGms
import org.microg.gms.maps.mapbox.utils.toMapbox
class CircleImpl(private val map: GoogleMapImpl, private val circle: Circle) : ICircleDelegate.Stub() {
override fun remove() {
map.circleManager?.delete(circle)
}
override fun getId(): String = "c" + circle.id.toString()
override fun setCenter(center: LatLng) {
circle.latLng = center.toMapbox()
map.circleManager?.update(circle)
}
override fun getCenter(): LatLng = circle.latLng.toGms()
override fun setRadius(radius: Double) {
circle.circleRadius = radius.toFloat()
map.circleManager?.update(circle)
}
override fun getRadius(): Double = circle.circleRadius.toDouble()
override fun setStrokeWidth(width: Float) {
circle.circleStrokeWidth = width / map.dpiFactor
map.circleManager?.update(circle)
}
override fun getStrokeWidth(): Float = circle.circleStrokeWidth * map.dpiFactor
override fun setStrokeColor(color: Int) {
circle.setCircleStrokeColor(color)
map.circleManager?.update(circle)
}
override fun getStrokeColor(): Int = circle.circleStrokeColorAsInt
override fun setFillColor(color: Int) {
circle.setCircleColor(color)
map.circleManager?.update(circle)
}
override fun getFillColor(): Int = circle.circleColorAsInt
override fun setZIndex(zIndex: Float) {
Log.d(TAG, "unimplemented Method: setZIndex")
}
override fun getZIndex(): Float {
Log.d(TAG, "unimplemented Method: getZIndex")
return 0f
}
override fun setVisible(visible: Boolean) {
circle.circleOpacity = if (visible) 1f else 0f
circle.circleStrokeOpacity = if (visible) 1f else 0f
map.circleManager?.update(circle)
}
override fun isVisible(): Boolean = circle.circleOpacity != 0f || circle.circleStrokeOpacity != 0f
override fun equalsRemote(other: ICircleDelegate?): Boolean = equals(other)
override fun hashCodeRemote(): Int = hashCode()
override fun equals(other: Any?): Boolean {
if (other is CircleImpl) {
return other.circle == circle
}
return false
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
val TAG = "GmsMapCircle"
}
}

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.model
import android.util.Log
import com.google.android.gms.dynamic.IObjectWrapper
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.internal.IMarkerDelegate
import com.mapbox.mapboxsdk.plugins.annotation.Symbol
import org.microg.gms.kotlin.unwrap
import org.microg.gms.maps.mapbox.GoogleMapImpl
import org.microg.gms.maps.mapbox.utils.toGms
import org.microg.gms.maps.mapbox.utils.toMapbox
class MarkerImpl(private val map: GoogleMapImpl,
private val symbol: Symbol,
private var anchor: FloatArray,
private var icon: BitmapDescriptorImpl?,
private var alpha: Float = symbol.iconOpacity,
private var title: String? = null,
private var snippet: String? = null) : IMarkerDelegate.Stub() {
private var tag: IObjectWrapper? = null
override fun remove() {
map.symbolManager?.delete(symbol)
map.markers.remove(symbol.id)
}
override fun getId(): String = "m" + symbol.id.toString()
override fun setPosition(pos: LatLng?) {
pos?.let { symbol.latLng = it.toMapbox() }
map.symbolManager?.update(symbol)
}
override fun getPosition(): LatLng = symbol.latLng.toGms()
override fun setTitle(title: String?) {
this.title = title
}
override fun getTitle(): String? = title
override fun setSnippet(snippet: String?) {
this.snippet = snippet
}
override fun getSnippet(): String? = snippet
override fun setDraggable(drag: Boolean) {
symbol.isDraggable = drag
map.symbolManager?.update(symbol)
}
override fun isDraggable(): Boolean = symbol.isDraggable
override fun showInfoWindow() {
Log.d(TAG, "unimplemented Method: showInfoWindow")
}
override fun hideInfoWindow() {
Log.d(TAG, "unimplemented Method: hideInfoWindow")
}
override fun isInfoWindowShown(): Boolean {
Log.d(TAG, "unimplemented Method: isInfoWindowShow")
return false
}
override fun setVisible(visible: Boolean) {
symbol.iconOpacity = if (visible) 0f else alpha
map.symbolManager?.update(symbol)
}
override fun isVisible(): Boolean = symbol.iconOpacity != 0f
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is IMarkerDelegate) return other.id == id
return false
}
override fun equalsRemote(other: IMarkerDelegate?): Boolean = equals(other)
override fun hashCode(): Int {
return id.hashCode()
}
override fun toString(): String {
return "$id ($title)"
}
override fun hashCodeRemote(): Int = hashCode()
override fun setIcon(obj: IObjectWrapper?) {
obj.unwrap<BitmapDescriptorImpl>()?.let { icon = it }
icon?.applyTo(symbol, anchor, map.dpiFactor)
map.symbolManager?.update(symbol)
}
override fun setAnchor(x: Float, y: Float) {
anchor = floatArrayOf(x, y)
icon?.applyTo(symbol, anchor, map.dpiFactor)
map.symbolManager?.update(symbol)
}
override fun setFlat(flat: Boolean) {
Log.d(TAG, "unimplemented Method: setFlat")
}
override fun isFlat(): Boolean {
Log.d(TAG, "unimplemented Method: isFlat")
return false
}
override fun setRotation(rotation: Float) {
symbol.iconRotate = rotation
map.symbolManager?.update(symbol)
}
override fun getRotation(): Float = symbol.iconRotate
override fun setInfoWindowAnchor(x: Float, y: Float) {
Log.d(TAG, "unimplemented Method: setInfoWindowAnchor")
}
override fun setAlpha(alpha: Float) {
this.alpha = alpha
symbol.iconOpacity = alpha
map.symbolManager?.update(symbol)
}
override fun getAlpha(): Float = alpha
override fun setZIndex(zIndex: Float) {
var intBits = java.lang.Float.floatToIntBits(zIndex)
if (intBits < 0) intBits = intBits xor 0x7fffffff
symbol.zIndex = intBits
map.symbolManager?.update(symbol)
}
override fun getZIndex(): Float {
var intBits = symbol.zIndex
if (intBits < 0) intBits = intBits xor 0x7fffffff
return java.lang.Float.intBitsToFloat(intBits)
}
override fun setTag(obj: IObjectWrapper?) {
this.tag = obj
}
override fun getTag(): IObjectWrapper? = tag
companion object {
private val TAG = "GmsMapMarker"
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.model
import android.os.Parcel
import android.util.Log
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.internal.IPolylineDelegate
import com.mapbox.mapboxsdk.plugins.annotation.Line
import org.microg.gms.maps.mapbox.GoogleMapImpl
import org.microg.gms.maps.mapbox.utils.toGms
import org.microg.gms.maps.mapbox.utils.toMapbox
class PolylineImpl(private val map: GoogleMapImpl, private val line: Line) : IPolylineDelegate.Stub() {
override fun remove() {
map.lineManager?.delete(line)
}
override fun getId(): String = "l" + line.id.toString()
override fun setPoints(points: MutableList<LatLng>) {
line.latLngs = points.map { it.toMapbox() }
map.lineManager?.update(line)
}
override fun getPoints(): List<LatLng> = line.latLngs.map { it.toGms() }
override fun setWidth(width: Float) {
line.lineWidth = width / map.dpiFactor
map.lineManager?.update(line)
}
override fun getWidth(): Float = line.lineWidth * map.dpiFactor
override fun setColor(color: Int) {
line.setLineColor(color)
map.lineManager?.update(line)
}
override fun getColor(): Int = line.lineColorAsInt
override fun setZIndex(zIndex: Float) {
Log.d(TAG, "unimplemented Method: setZIndex")
}
override fun getZIndex(): Float {
Log.d(TAG, "unimplemented Method: getZIndex")
return 0f
}
override fun setVisible(visible: Boolean) {
line.lineOpacity = if (visible) 1f else 0f
map.lineManager?.update(line)
}
override fun isVisible(): Boolean = line.lineOpacity != 0f
override fun setGeodesic(geod: Boolean) {
Log.d(TAG, "unimplemented Method: setGeodesic")
}
override fun isGeodesic(): Boolean {
Log.d(TAG, "unimplemented Method: isGeodesic")
return false
}
override fun equalsRemote(other: IPolylineDelegate?): Boolean = equals(other)
override fun hashCodeRemote(): Int = hashCode()
override fun equals(other: Any?): Boolean {
if (other is PolylineImpl) {
return other.line == line
}
return false
}
override fun onTransact(code: Int, data: Parcel?, reply: Parcel?, flags: Int): Boolean =
if (super.onTransact(code, data, reply, flags)) {
true
} else {
Log.d(TAG, "onTransact [unknown]: $code, $data, $flags"); false
}
companion object {
private val TAG = "GmsMapPolyline"
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.utils
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.SharedPreferences
import android.view.LayoutInflater
import org.microg.gms.common.Constants
import java.io.File
class MapContext(private val context: Context) : ContextWrapper(context.createPackageContext(Constants.GMS_PACKAGE_NAME, Context.CONTEXT_INCLUDE_CODE and Context.CONTEXT_IGNORE_SECURITY)) {
private var layoutInflater: LayoutInflater? = null
private val appContext: Context
get() = context.applicationContext ?: context
override fun getApplicationContext(): Context {
return this
}
override fun getCacheDir(): File {
val cacheDir = File(appContext.cacheDir, "com.google.android.gms")
cacheDir.mkdirs()
return cacheDir
}
override fun getFilesDir(): File {
val filesDir = File(appContext.filesDir, "com.google.android.gms")
filesDir.mkdirs()
return filesDir
}
override fun getPackageName(): String {
return appContext.packageName
}
override fun getClassLoader(): ClassLoader {
return MapContext::class.java.classLoader
}
override fun getSharedPreferences(name: String?, mode: Int): SharedPreferences {
return appContext.getSharedPreferences("com.google.android.gms_$name", mode)
}
override fun getSystemService(name: String): Any? {
if (name == Context.LAYOUT_INFLATER_SERVICE) {
if (layoutInflater == null) {
layoutInflater = super.getSystemService(name) as LayoutInflater
layoutInflater?.cloneInContext(this)?.let { layoutInflater = it }
}
if (layoutInflater != null) {
return layoutInflater
}
}
return context.getSystemService(name)
}
override fun startActivity(intent: Intent?) {
context.startActivity(intent)
}
companion object {
val TAG = "GmsMapContext"
}
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.utils
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.util.Log
import com.mapbox.mapboxsdk.LibraryLoader
import java.io.*
import java.util.zip.ZipFile
class MultiArchLoader(private val mapContext: Context, private val appContext: Context) : LibraryLoader() {
@SuppressLint("UnsafeDynamicallyLoadedCode")
override fun load(name: String) {
try {
val otherAppInfo = mapContext.packageManager.getApplicationInfo(appContext.packageName, 0)
val primaryCpuAbi = ApplicationInfo::class.java.getField("primaryCpuAbi").get(otherAppInfo) as String?
if (primaryCpuAbi != null) {
val path = "lib/$primaryCpuAbi/lib$name.so"
val cacheFile = File("${appContext.cacheDir.absolutePath}/.gmscore/$path")
cacheFile.parentFile.mkdirs()
val apkFile = File(mapContext.packageCodePath)
if (!cacheFile.exists() || cacheFile.lastModified() < apkFile.lastModified()) {
val zipFile = ZipFile(apkFile)
val entry = zipFile.getEntry(path)
if (entry != null) {
copyInputStream(zipFile.getInputStream(entry), FileOutputStream(cacheFile))
} else {
Log.d(TAG, "Can't load native library: $path does not exist in $apkFile")
val entries = zipFile.entries()
while (entries.hasMoreElements()) {
Log.d(TAG, "but: ${entries.nextElement()}")
}
}
}
Log.d(TAG, "Loading $name from ${cacheFile.getPath()}")
System.load(cacheFile.absolutePath)
return
}
} catch (e: Exception) {
Log.w(TAG, e)
}
Log.d(TAG, "Loading native $name")
System.loadLibrary(name)
}
@Throws(IOException::class)
private fun copyInputStream(inp: InputStream, out: OutputStream) {
val buffer = ByteArray(1024)
var len: Int = inp.read(buffer)
while (len >= 0) {
out.write(buffer, 0, len)
len = inp.read(buffer)
}
inp.close()
out.close()
}
companion object {
private val TAG = "GmsMultiArchLoader"
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2019 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.maps.mapbox.utils
import android.os.Bundle
import com.google.android.gms.maps.internal.ICancelableCallback
import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.PolylineOptions
import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.geometry.LatLng
import com.mapbox.mapboxsdk.geometry.LatLngBounds
import com.mapbox.mapboxsdk.geometry.VisibleRegion
import com.mapbox.mapboxsdk.maps.MapboxMap
import com.mapbox.mapboxsdk.plugins.annotation.LineOptions
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
import com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_TOP_LEFT
import com.mapbox.mapboxsdk.utils.ColorUtils
import org.microg.gms.kotlin.unwrap
import org.microg.gms.maps.mapbox.model.BitmapDescriptorImpl
fun com.google.android.gms.maps.model.LatLng.toMapbox(): LatLng =
LatLng(latitude, longitude)
fun com.google.android.gms.maps.model.LatLngBounds.toMapbox(): LatLngBounds =
LatLngBounds.from(this.northeast.latitude, this.northeast.longitude, this.southwest.latitude, this.southwest.longitude)
fun com.google.android.gms.maps.model.CameraPosition.toMapbox(): CameraPosition =
CameraPosition.Builder()
.target(target.toMapbox())
.zoom(zoom.toDouble() - 1.0)
.tilt(tilt.toDouble())
.bearing(bearing.toDouble())
.build()
fun ICancelableCallback.toMapbox(): MapboxMap.CancelableCallback =
object : MapboxMap.CancelableCallback {
override fun onFinish() = this@toMapbox.onFinish()
override fun onCancel() = this@toMapbox.onCancel()
}
fun Bundle.toMapbox(): Bundle {
val newBundle = Bundle(this)
for (key in newBundle.keySet()) {
val value = newBundle.get(key)
when (value) {
is com.google.android.gms.maps.model.CameraPosition -> newBundle.putParcelable(key, value.toMapbox())
is com.google.android.gms.maps.model.LatLng -> newBundle.putParcelable(key, value.toMapbox())
is com.google.android.gms.maps.model.LatLngBounds -> newBundle.putParcelable(key, value.toMapbox())
is Bundle -> newBundle.putBundle(key, value.toMapbox())
}
}
return newBundle
}
fun LatLng.toGms(): com.google.android.gms.maps.model.LatLng =
com.google.android.gms.maps.model.LatLng(latitude, longitude)
fun LatLngBounds.toGms(): com.google.android.gms.maps.model.LatLngBounds =
com.google.android.gms.maps.model.LatLngBounds(southWest.toGms(), northEast.toGms())
fun CameraPosition.toGms(): com.google.android.gms.maps.model.CameraPosition =
com.google.android.gms.maps.model.CameraPosition(target.toGms(), zoom.toFloat() + 1.0f, tilt.toFloat(), bearing.toFloat())
fun Bundle.toGms(): Bundle {
val newBundle = Bundle(this)
for (key in newBundle.keySet()) {
val value = newBundle.get(key)
when (value) {
is CameraPosition -> newBundle.putParcelable(key, value.toGms())
is LatLng -> newBundle.putParcelable(key, value.toGms())
is LatLngBounds -> newBundle.putParcelable(key, value.toGms())
is Bundle -> newBundle.putBundle(key, value.toGms())
}
}
return newBundle
}
fun VisibleRegion.toGms(): com.google.android.gms.maps.model.VisibleRegion =
com.google.android.gms.maps.model.VisibleRegion(nearLeft.toGms(), nearRight.toGms(), farLeft.toGms(), farRight.toGms(), latLngBounds.toGms())

View File

@ -17,6 +17,7 @@ include ':play-services-base'
include ':play-services-tasks' include ':play-services-tasks'
include ':play-services-wearable' include ':play-services-wearable'
include ':play-services-maps-core-mapbox'
include ':play-services-maps-core-vtm' include ':play-services-maps-core-vtm'
include ':play-services-core' include ':play-services-core'