diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3e12b08d23dc95fffa7561e167b94f953673c450..3ea467079a1c8b3d3b783ee68fe35c9517b92173 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,11 +25,11 @@ build: script: - git submodule update --recursive --init - echo sdk.dir $ANDROID_HOME > local.properties + - echo mapbox.key $MAPBOX_KEY >> local.properties + - echo mapbox.enabled true >> local.properties - export TERM=dumb - export JAVA_OPTS="-XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -Xmx2048m" - - ./gradlew build - only: - - tags + - ./gradlew assemble artifacts: paths: - play-services-core/build/outputs/apk/ diff --git a/.travis.yml b/.travis.yml index 822e0de02263154feb093faf0e4787e9ab6bd026..87ad538a658853850efc446ab60a46d025d475b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ android: components: - tools - platform-tools - - build-tools-28.0.3 + - build-tools-29.0.2 - android-27 - android-28 - extra-android-m2repository diff --git a/build.gradle b/build.gradle index 76056a023c79903aac26a2a7b9ba16b747e7d1ee..b6ffdcde07818031b191fa61a4fb217bfc22c7a5 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' + classpath 'com.android.tools.build:gradle:3.5.1' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } @@ -29,7 +29,7 @@ buildscript { allprojects { apply plugin: 'idea' - ext.androidBuildVersionTools = "28.0.3" + ext.androidBuildVersionTools = "29.0.2" ext.supportLibraryVersion = "28.0.0" ext.isReleaseVersion = false ext.slf4jVersion = "1.7.25" @@ -37,7 +37,7 @@ allprojects { def androidCompileSdk() { return 28 } -def androidTargetSdk() { return 28 } +def androidTargetSdk() { return 29 } def androidMinSdk() { return 14 } diff --git a/extern/GmsApi b/extern/GmsApi index 51509859ae51097ad5789ba4ca2e9ffb9658f80e..2a43448e49dc0aec0d6c53c8a27dd58245fdaba6 160000 --- a/extern/GmsApi +++ b/extern/GmsApi @@ -1 +1 @@ -Subproject commit 51509859ae51097ad5789ba4ca2e9ffb9658f80e +Subproject commit 2a43448e49dc0aec0d6c53c8a27dd58245fdaba6 diff --git a/extern/GmsLib b/extern/GmsLib index 39d20ec1f28bcc4b134d76f43296bad587362c69..15cd4491bcca57d627796b35b69bdf8c97564792 160000 --- a/extern/GmsLib +++ b/extern/GmsLib @@ -1 +1 @@ -Subproject commit 39d20ec1f28bcc4b134d76f43296bad587362c69 +Subproject commit 15cd4491bcca57d627796b35b69bdf8c97564792 diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index dfbe478748b40312f4a377f1c3890f6c74d93027..0000000000000000000000000000000000000000 --- a/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -android.useDeprecatedNdk=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7919ae6e6bc1c4aceb8a6689658a1ca95e83d53d..d757f3d33fcc6837851eb73a6963eebcee23c282 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle index c40c2c7dd262618dbede2077c89a4360ba1a8c30..1c0de9b520c45d621786c8b43e5cc12b2154d71c 100644 --- a/play-services-core/build.gradle +++ b/play-services-core/build.gradle @@ -30,7 +30,7 @@ dependencies { implementation "com.squareup.wire:wire-runtime:1.6.1" implementation "com.takisoft.fix:preference-v7:$supportLibraryVersion.0" implementation "de.hdodenhof:circleimageview:1.3.0" - implementation "org.conscrypt:conscrypt-android:2.0.0" + implementation "org.conscrypt:conscrypt-android:2.1.0" // TODO: Switch to upstream once raw requests are merged // https://github.com/vitalidze/chromecast-java-api-v2/pull/99 // implementation "su.litvak.chromecast:api-v2:0.10.4" @@ -65,7 +65,7 @@ def execResult(...args) { return stdout.toString().trim() } -def gmsVersion = "17.7.85" +def gmsVersion = "19.4.20" def gmsVersionCode = Integer.parseInt(gmsVersion.replaceAll('\\.', '')) def gitVersionBase = execResult('git', 'describe', '--tags', '--abbrev=0', '--match=v[0-9]*').substring(1) def gitCommitCount = Integer.parseInt(execResult('git', 'rev-list', '--count', "v$gitVersionBase..HEAD")) @@ -74,7 +74,7 @@ def gitDirty = execResult('git', 'status', '--porcelain').size() > 0 def ourVersionBase = gitVersionBase.substring(0, gitVersionBase.lastIndexOf('.')) def ourVersionMinor = Integer.parseInt(ourVersionBase.substring(ourVersionBase.lastIndexOf('.') + 1)) def ourVersionCode = gmsVersionCode * 1000 + ourVersionMinor * 2 + (gitCommitCount > 0 || gitDirty ? 1 : 0) -def ourVersionName = "$ourVersionBase.$gmsVersionCode" + (gitCommitCount > 0 && !gitDirty ? "-$gitCommitCount" : "") + (gitDirty ? "-dirty" : "") + (useMapbox() ? "-mapbox" : "") + (gitCommitCount > 0 && !gitDirty ? " ($gitCommitId)" : "") +def ourVersionName = "$ourVersionBase.$gmsVersionCode" + (gitCommitCount > 0 && !gitDirty ? "-$gitCommitCount" : "") + (gitDirty ? "-dirty" : "") + (useMapbox() ? "" : "-vtm") + (gitCommitCount > 0 && !gitDirty ? " ($gitCommitId)" : "") logger.lifecycle('Starting build for version {} ({})...', ourVersionName, ourVersionCode) android { diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml index 535180fe62e8afee561b504c64fff6ff7361225b..823085688d462ba9965ff1c409061a0c46952d4e 100644 --- a/play-services-core/src/main/AndroidManifest.xml +++ b/play-services-core/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ - - - + + + + + + + + + diff --git a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java index 43bd5cafed9704b3320423a62da76023db72f587..a841984c7642dc8a65196c3e43a104b7d2b72694 100644 --- a/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java +++ b/play-services-core/src/main/java/com/google/android/gms/chimera/container/DynamiteLoaderImpl.java @@ -66,6 +66,10 @@ public class DynamiteLoaderImpl extends IDynamiteLoader.Stub { Log.d(TAG, "returning temp fix module version for " + moduleId + ". Cast API wil not be functional!"); return 1; } + if (moduleId.equals("com.google.android.gms.maps_dynamite")) { + Log.d(TAG, "returning v1 for maps"); + return 1; + } Log.d(TAG, "unimplemented Method: getModuleVersion for " + moduleId); return 0; } diff --git a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java b/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java index bb9c6df2ffb96424b2b72b0e0b86247d1c5dcb7f..06a8a7cce1eb0519c63262629eafc97a1c2d84e4 100644 --- a/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java +++ b/play-services-core/src/main/java/com/google/android/gms/common/GoogleCertificatesImpl.java @@ -16,6 +16,7 @@ package com.google.android.gms.common; +import android.content.pm.PackageManager; import android.os.RemoteException; import android.support.annotation.Keep; import android.util.Log; @@ -55,6 +56,14 @@ public class GoogleCertificatesImpl extends IGoogleCertificatesApi.Stub { @Override public boolean isGoogleOrPlatformSigned(GoogleCertificatesQuery query, IObjectWrapper packageManager) throws RemoteException { - return PackageUtils.isGooglePackage(query.getPackageName(), query.getCertData().getBytes()); + PackageManager pm = ObjectWrapper.unwrapTyped(packageManager, PackageManager.class); + if (query == null || query.getPackageName() == null) { + return false; + } else if (query.getCertData() == null) { + if (pm == null) return false; + return PackageUtils.isGooglePackage(pm, query.getPackageName()); + } else { + return PackageUtils.isGooglePackage(query.getPackageName(), query.getCertData().getBytes()); + } } } diff --git a/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java b/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java index fa72252da8fd3f47f0306bc0358f975a05797ed2..6d702279f1e4874b909bdd0df05485166f232e25 100644 --- a/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java +++ b/play-services-core/src/main/java/com/google/android/gms/common/security/ProviderInstallerImpl.java @@ -34,9 +34,10 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.Security; +import java.util.Arrays; import java.util.Collections; -import java.util.Enumeration; import java.util.List; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -45,7 +46,7 @@ import javax.net.ssl.SSLContext; public class ProviderInstallerImpl { private static final String TAG = "GmsProviderInstaller"; - private static final List DISABLED = Collections.singletonList("com.discord"); + private static final List DISABLED = Collections.unmodifiableList(Arrays.asList("com.discord", "com.bankid.bus")); public static void insertProvider(Context context) { String packageName = PackageUtils.packageFromProcessId(context, Process.myPid()); @@ -86,13 +87,57 @@ public class ProviderInstallerImpl { System.load(cacheFile.getAbsolutePath()); Class clazz = NativeCrypto.class; + Field loadError = clazz.getDeclaredField("loadError"); loadError.setAccessible(true); loadError.set(null, null); - Method clinit =clazz.getDeclaredMethod("clinit"); + + Method clinit = clazz.getDeclaredMethod("clinit"); clinit.setAccessible(true); + + Method get_cipher_names = clazz.getDeclaredMethod("get_cipher_names", String.class); + get_cipher_names.setAccessible(true); + + Method cipherSuiteToJava = clazz.getDeclaredMethod("cipherSuiteToJava", String.class); + cipherSuiteToJava.setAccessible(true); + + Method EVP_has_aes_hardware = clazz.getDeclaredMethod("EVP_has_aes_hardware"); + EVP_has_aes_hardware.setAccessible(true); + + Field f = clazz.getDeclaredField("SUPPORTED_TLS_1_2_CIPHER_SUITES_SET"); + f.setAccessible(true); + + Set SUPPORTED_TLS_1_2_CIPHER_SUITES_SET = (Set) f.get(null); + f = clazz.getDeclaredField("SUPPORTED_LEGACY_CIPHER_SUITES_SET"); + f.setAccessible(true); + + Set SUPPORTED_LEGACY_CIPHER_SUITES_SET = (Set) f.get(null); + f = clazz.getDeclaredField("SUPPORTED_TLS_1_2_CIPHER_SUITES"); + f.setAccessible(true); + try { clinit.invoke(null); + + String[] allCipherSuites = (String[]) get_cipher_names.invoke(null, "ALL:!DHE"); + int size = allCipherSuites.length; + + String[] SUPPORTED_TLS_1_2_CIPHER_SUITES = new String[size / 2 + 2]; + for (int i = 0; i < size; i += 2) { + String cipherSuite = (String) cipherSuiteToJava.invoke(null, allCipherSuites[i]); + + SUPPORTED_TLS_1_2_CIPHER_SUITES[i / 2] = cipherSuite; + SUPPORTED_TLS_1_2_CIPHER_SUITES_SET.add(cipherSuite); + + SUPPORTED_LEGACY_CIPHER_SUITES_SET.add(allCipherSuites[i + 1]); + } + SUPPORTED_TLS_1_2_CIPHER_SUITES[size / 2] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"; + SUPPORTED_TLS_1_2_CIPHER_SUITES[size / 2 + 1] = "TLS_FALLBACK_SCSV"; + f.set(null, SUPPORTED_TLS_1_2_CIPHER_SUITES); + + f = clazz.getDeclaredField("HAS_AES_HARDWARE"); + f.setAccessible(true); + f.set(null, (int) EVP_has_aes_hardware.invoke(null) == 1); + provider = new OpenSSLProvider("GmsCore_OpenSSL"); } catch (InvocationTargetException inner) { if (inner.getTargetException() instanceof UnsatisfiedLinkError) { diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java index 1f5c26a796a8e6bf71c0ad0241cb4354ca14d239..0e6faf89ef998697129f20e48f381ae45c8168f9 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthManagerServiceImpl.java @@ -17,6 +17,7 @@ package org.microg.gms.auth; import android.accounts.Account; +import android.accounts.AccountManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -167,6 +168,7 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub { packageName = PackageUtils.getAndCheckCallingPackage(context, packageName, extras.getInt(KEY_CALLER_UID, 0), extras.getInt(KEY_CALLER_PID, 0)); Log.d(TAG, "clearToken: token:" + token + " extras:" + extras); + AccountManager.get(context).invalidateAuthToken(AuthConstants.DEFAULT_ACCOUNT_TYPE, token); return null; } diff --git a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java index d37a99c63ddefd69535a0a5ff29f6ca9af62b943..a538c55ffa3c8cd9d2b23537c023d11d219e24a0 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/login/LoginActivity.java @@ -55,6 +55,7 @@ import org.microg.gms.checkin.CheckinManager; import org.microg.gms.checkin.LastCheckinInfo; import org.microg.gms.common.HttpFormClient; import org.microg.gms.common.Utils; +import org.microg.gms.gcm.McsService; import org.microg.gms.people.PeopleManager; import java.io.IOException; @@ -308,7 +309,12 @@ public class LoginActivity extends AssistantActivity { @Override public void onException(Exception exception) { - Log.w(TAG, "onException: " + exception); + Log.w(TAG, "onException", exception); + runOnUiThread(() -> { + showError(R.string.auth_general_error_desc); + setNextButtonText(android.R.string.ok); + }); + state = -2; } }); } @@ -339,7 +345,12 @@ public class LoginActivity extends AssistantActivity { @Override public void onException(Exception exception) { - Log.w(TAG, "onException: " + exception); + Log.w(TAG, "onException", exception); + runOnUiThread(() -> { + showError(R.string.auth_general_error_desc); + setNextButtonText(android.R.string.ok); + }); + state = -2; } }); } @@ -347,6 +358,7 @@ public class LoginActivity extends AssistantActivity { private boolean checkin(boolean force) { try { CheckinManager.checkin(LoginActivity.this, force); + McsService.scheduleReconnect(this); return true; } catch (IOException e) { Log.w(TAG, "Checkin failed", e); diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java index 8660dcdbe58a296e172747502c97496ab7009507..a2b65de1976e27161f8e355c92a8f11c50938e80 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java +++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java @@ -62,6 +62,7 @@ public class PackageUtils { KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.wellbeing", "4ebdd02380f1fa0b6741491f0af35625dba76e9f"); KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.village.boond", "48e7985b8f901df335b5d5223579c81618431c7b"); KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.subscriptions.red", "de8304ace744ae4c4e05887a27a790815e610ff0"); + KNOWN_GOOGLE_PACKAGES.put("com.google.android.apps.meetings", "47a6936b733dbdb45d71997fbe1d610eca36b8bf"); } public static boolean isGooglePackage(Context context, String packageName) { @@ -69,6 +70,11 @@ public class PackageUtils { return isGooglePackage(packageName, signatureDigest); } + public static boolean isGooglePackage(PackageManager packageManager, String packageName) { + String signatureDigest = firstSignatureDigest(packageManager, packageName); + return isGooglePackage(packageName, signatureDigest); + } + public static boolean isGooglePackage(String packageName, byte[] bytes) { return isGooglePackage(packageName, sha1sum(bytes)); } @@ -102,7 +108,11 @@ public class PackageUtils { @Nullable public static String firstSignatureDigest(Context context, String packageName) { - PackageManager packageManager = context.getPackageManager(); + return firstSignatureDigest(context.getPackageManager(), packageName); + } + + @Nullable + public static String firstSignatureDigest(PackageManager packageManager, String packageName) { final PackageInfo info; try { info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); diff --git a/play-services-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java b/play-services-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java index 1e2fa5ead6bea789f646634060bdf48a3ec5cbbd..46ebca3b9013e24281733958a5b1725652e0cc72 100644 --- a/play-services-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java +++ b/play-services-core/src/main/java/org/microg/gms/common/RemoteListenerProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 microG Project Team + * 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. @@ -66,7 +66,6 @@ public class RemoteListenerProxy implements ServiceConnect if (!connecting) Log.d(TAG, "Could not connect to: " + intent); return connecting; } - Log.d(TAG, "Unable to resolve: " + searchIntent); return false; } catch (Exception e) { Log.w(TAG, e); @@ -91,11 +90,17 @@ public class RemoteListenerProxy implements ServiceConnect synchronized (this) { remote = service; if (!waiting.isEmpty()) { - for (Runnable runnable : waiting) { - runnable.run(); + try { + for (Runnable runnable : waiting) { + runnable.run(); + } + } catch (Exception e) { } waiting.clear(); - context.unbindService(RemoteListenerProxy.this); + try { + context.unbindService(RemoteListenerProxy.this); + } catch (Exception e) { + } connecting = false; remote = null; } diff --git a/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerServiceImpl.java index 97b0357053de282debadb68f7ef34294f690cde1..c66c0be436ea471ce731196f2cea9e077c8f76e7 100644 --- a/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerServiceImpl.java @@ -307,7 +307,7 @@ public class GoogleLocationManagerServiceImpl extends IGoogleLocationManagerServ public void requestLocationSettingsDialog(LocationSettingsRequest settingsRequest, ISettingsCallbacks callback, String packageName) throws RemoteException { Log.d(TAG, "requestLocationSettingsDialog: " + settingsRequest); PackageUtils.getAndCheckCallingPackage(context, packageName); - callback.onLocationSettingsResult(new LocationSettingsResult(new LocationSettingsStates(true, true, false, true, true, false), Status.CANCELED)); + callback.onLocationSettingsResult(new LocationSettingsResult(new LocationSettingsStates(true, true, false, true, true, false), Status.SUCCESS)); } @Override diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java index 9d9af368c2dc7ef3f31efadbbfeaabfa26c5d7d9..a2f1001581e8eb7c545fe336505cd6d0f81269d5 100644 --- a/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleServiceImpl.java @@ -16,9 +16,11 @@ package org.microg.gms.people; +import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; import android.os.Parcel; @@ -26,7 +28,6 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; -import com.google.android.gms.R; import com.google.android.gms.common.data.DataHolder; import com.google.android.gms.common.internal.ICancelToken; import com.google.android.gms.people.internal.IPeopleCallbacks; @@ -51,7 +52,9 @@ public class PeopleServiceImpl extends IPeopleService.Stub { @Override public void loadOwners(final IPeopleCallbacks callbacks, boolean var2, boolean var3, final String accountName, String var5, int sortOrder) { Log.d(TAG, "loadOwners: " + var2 + ", " + var3 + ", " + accountName + ", " + var5 + ", " + sortOrder); - PackageUtils.assertExtendedAccess(context); + if (context.checkCallingPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { + PackageUtils.assertExtendedAccess(context); + } AccountManager accountManager = AccountManager.get(context); Bundle accountMetadata = new Bundle(); String accountType = AuthConstants.DEFAULT_ACCOUNT_TYPE; diff --git a/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayImpl.java b/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..406326503ff769707c32611c96b4234dcc94164a --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayImpl.java @@ -0,0 +1,33 @@ +/* + * 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.tapandpay; + +import com.google.android.gms.tapandpay.internal.ITapAndPayService; +import android.os.Parcel; +import android.os.RemoteException; +import android.util.Log; + +public class TapAndPayImpl extends ITapAndPayService.Stub { + private static final String TAG = "GmsTapAndPayImpl"; + + @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; + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayService.java b/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayService.java new file mode 100644 index 0000000000000000000000000000000000000000..082234e643bdc741cfa87af6e17efced1838b244 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/tapandpay/TapAndPayService.java @@ -0,0 +1,38 @@ +/* + * 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.tapandpay; + +import android.os.RemoteException; + +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.internal.GetServiceRequest; +import com.google.android.gms.common.internal.IGmsCallbacks; + +import org.microg.gms.BaseService; +import org.microg.gms.common.GmsService; + +public class TapAndPayService extends BaseService { + public TapAndPayService() { + super("GmsTapAndPaySvc", GmsService.TAP_AND_PAY); + } + + @Override + public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException { + callback.onPostInitComplete(CommonStatusCodes.SUCCESS, new TapAndPayImpl(), null); + + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/CapabilityManager.java b/play-services-core/src/main/java/org/microg/gms/wearable/CapabilityManager.java new file mode 100644 index 0000000000000000000000000000000000000000..0c8f59ffc676c1e999f0c1e261cfe4b3bfb0a128 --- /dev/null +++ b/play-services-core/src/main/java/org/microg/gms/wearable/CapabilityManager.java @@ -0,0 +1,85 @@ +/* + * 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.wearable; + +import android.content.Context; +import android.net.Uri; + +import com.google.android.gms.common.api.CommonStatusCodes; +import com.google.android.gms.common.data.DataHolder; +import com.google.android.gms.wearable.Node; +import com.google.android.gms.wearable.WearableStatusCodes; +import com.google.android.gms.wearable.internal.NodeParcelable; + +import org.microg.gms.common.PackageUtils; + +import java.util.HashSet; +import java.util.Set; + +public class CapabilityManager { + private static final Uri ROOT = Uri.parse("wear:/capabilities/"); + private final Context context; + private final WearableImpl wearable; + private final String packageName; + + private Set capabilities = new HashSet(); + + public CapabilityManager(Context context, WearableImpl wearable, String packageName) { + this.context = context; + this.wearable = wearable; + this.packageName = packageName; + } + + private Uri buildCapabilityUri(String capability, boolean withAuthority) { + Uri.Builder builder = ROOT.buildUpon(); + if (withAuthority) builder.authority(wearable.getLocalNodeId()); + builder.appendPath(packageName); + builder.appendPath(PackageUtils.firstSignatureDigest(context, packageName)); + builder.appendPath(Uri.encode(capability)); + return builder.build(); + } + + public Set getNodesForCapability(String capability) { + DataHolder dataHolder = wearable.getDataItemsByUriAsHolder(buildCapabilityUri(capability, false), packageName); + Set nodes = new HashSet<>(); + for (int i = 0; i < dataHolder.getCount(); i++) { + nodes.add(dataHolder.getString("host", i, 0)); + } + dataHolder.close(); + return nodes; + } + + public int add(String capability) { + if (this.capabilities.contains(capability)) { + return WearableStatusCodes.DUPLICATE_CAPABILITY; + } + DataItemInternal dataItem = new DataItemInternal(buildCapabilityUri(capability, true)); + DataItemRecord record = wearable.putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), wearable.getLocalNodeId(), dataItem); + this.capabilities.add(capability); + wearable.syncRecordToAll(record); + return CommonStatusCodes.SUCCESS; + } + + public int remove(String capability) { + if (!this.capabilities.contains(capability)) { + return WearableStatusCodes.UNKNOWN_CAPABILITY; + } + wearable.deleteDataItems(buildCapabilityUri(capability, true), packageName); + capabilities.remove(capability); + return CommonStatusCodes.SUCCESS; + } +} diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java b/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java index 852438921943450a16ecb1fafdc732a71e5c2a73..0406005defa3a0db0b32de585dc4c725be152a62 100644 --- a/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 microG Project Team + * 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. @@ -16,14 +16,15 @@ package org.microg.gms.wearable; -import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Base64; import android.util.Log; @@ -37,7 +38,6 @@ import com.google.android.gms.wearable.internal.MessageEventParcelable; import com.google.android.gms.wearable.internal.NodeParcelable; import com.google.android.gms.wearable.internal.PutDataRequest; -import org.microg.gms.common.MultiListenerProxy; import org.microg.gms.common.PackageUtils; import org.microg.gms.common.RemoteListenerProxy; import org.microg.gms.common.Utils; @@ -52,6 +52,7 @@ import org.microg.wearable.proto.FilePiece; import org.microg.wearable.proto.Request; import org.microg.wearable.proto.RootMessage; import org.microg.wearable.proto.SetAsset; +import org.microg.wearable.proto.SetDataItem; import java.io.File; import java.io.FileInputStream; @@ -70,8 +71,6 @@ import java.util.Set; import okio.ByteString; -import static android.os.Build.VERSION.SDK_INT; - public class WearableImpl { private static final String TAG = "GmsWear"; @@ -81,7 +80,7 @@ public class WearableImpl { private final Context context; private final NodeDatabaseHelper nodeDatabase; private final ConfigurationDatabaseHelper configDatabase; - private final Map> listeners = new HashMap>(); + private final Map> listeners = new HashMap>(); private final Set connectedNodes = new HashSet(); private final Map activeConnections = new HashMap(); private RpcHelper rpcHelper; @@ -130,12 +129,10 @@ public class WearableImpl { } } } - try { - getListener(record.packageName, "com.google.android.gms.wearable.DATA_CHANGED", record.dataItem.uri) - .onDataChanged(record.toEventDataHolder()); - } catch (RemoteException e) { - Log.w(TAG, e); - } + Intent intent = new Intent("com.google.android.gms.wearable.DATA_CHANGED"); + intent.setPackage(record.packageName); + intent.setData(record.dataItem.uri); + invokeListeners(intent, listener -> listener.onDataChanged(record.toEventDataHolder())); return record; } @@ -247,8 +244,7 @@ public class WearableImpl { } - private void syncRecordToAll(DataItemRecord record) { - Log.d(TAG, "Syncing record " + record + " over " + activeConnections.size() + " connections."); + void syncRecordToAll(DataItemRecord record) { for (String nodeId : new ArrayList(activeConnections.keySet())) { syncRecordToPeer(nodeId, record); } @@ -256,47 +252,45 @@ public class WearableImpl { private boolean syncRecordToPeer(String nodeId, DataItemRecord record) { for (Asset asset : record.dataItem.getAssets().values()) { - syncAssetToPeer(nodeId, record, asset); + try { + syncAssetToPeer(nodeId, record, asset); + } catch (Exception e) { + Log.w(TAG, "Could not sync asset " + asset + " for " + nodeId + " and " + record, e); + closeConnection(nodeId); + return false; + } } - Log.d(TAG, "Sync over to " + nodeId + ": " + record); try { - activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(record.toSetDataItem()).build()); - } catch (IOException e) { - closeConnection(nodeId); + SetDataItem item = record.toSetDataItem(); + activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(item).build()); + } catch (Exception e) { Log.w(TAG, e); + closeConnection(nodeId); return false; } return true; } - private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) { - try { - Log.d(TAG, "Sync over to " + nodeId + ": " + asset); - RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder() - .digest(asset.getDigest()) - .appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest)))) - .build()).hasAsset(true).build(); - activeConnections.get(nodeId).writeMessage(announceMessage); - File assetFile = createAssetFile(asset.getDigest()); - String fileName = calculateDigest(announceMessage.toByteArray()); - FileInputStream fis = new FileInputStream(assetFile); - byte[] arr = new byte[12215]; - ByteString lastPiece = null; - int c = 0; - while ((c = fis.read(arr)) > 0) { - if (lastPiece != null) { - Log.d(TAG, "Sync over to " + nodeId + ": Asset piece for fileName " + fileName + ": " + lastPiece); - activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build()); - } - lastPiece = ByteString.of(arr, 0, c); + private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) throws IOException { + RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder() + .digest(asset.getDigest()) + .appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest)))) + .build()).hasAsset(true).build(); + activeConnections.get(nodeId).writeMessage(announceMessage); + File assetFile = createAssetFile(asset.getDigest()); + String fileName = calculateDigest(announceMessage.toByteArray()); + FileInputStream fis = new FileInputStream(assetFile); + byte[] arr = new byte[12215]; + ByteString lastPiece = null; + int c = 0; + while ((c = fis.read(arr)) > 0) { + if (lastPiece != null) { + activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build()); } - Log.d(TAG, "Sync over to " + nodeId + ": Last asset piece for fileName " + fileName + ": " + lastPiece); - activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build()); - } catch (IOException e) { - Log.w(TAG, e); - closeConnection(nodeId); + lastPiece = ByteString.of(arr, 0, c); } + activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build()); } public void addAssetToDatabase(Asset asset, List appKeys) { @@ -394,37 +388,59 @@ public class WearableImpl { return nodes; } - public IWearableListener getAllListeners() { - return MultiListenerProxy.get(IWearableListener.class, new MultiListenerProxy.MultiCollectionListenerPool(listeners.values())); + interface ListenerInvoker { + void invoke(IWearableListener listener) throws RemoteException; + } + + private void invokeListeners(@Nullable Intent intent, ListenerInvoker invoker) { + for (String packageName : new ArrayList<>(listeners.keySet())) { + List listeners = this.listeners.get(packageName); + if (listeners == null) continue; + for (int i = 0; i < listeners.size(); i++) { + boolean filterMatched = false; + if (intent != null) { + for (IntentFilter filter : listeners.get(i).filters) { + filterMatched |= filter.match(context.getContentResolver(), intent, false, TAG) > 0; + } + } + if (filterMatched || listeners.get(i).filters.length == 0) { + try { + invoker.invoke(listeners.get(i).listener); + } catch (RemoteException e) { + Log.w(TAG, "Registered listener at package " + packageName + " failed, removing."); + listeners.remove(i); + i--; + } + } + } + if (listeners.isEmpty()) { + this.listeners.remove(packageName); + } + } + if (intent != null) { + try { + invoker.invoke(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER")); + } catch (RemoteException e) { + Log.w(TAG, "Failed to deliver message received to " + intent, e); + } + } } public void onPeerConnected(NodeParcelable node) { Log.d(TAG, "onPeerConnected: " + node); - try { - getAllListeners().onPeerConnected(node); - } catch (RemoteException e) { - Log.w(TAG, e); - } + invokeListeners(null, listener -> listener.onPeerConnected(node)); addConnectedNode(node); } public void onPeerDisconnected(NodeParcelable node) { Log.d(TAG, "onPeerDisconnected: " + node); - try { - getAllListeners().onPeerDisconnected(node); - } catch (RemoteException e) { - Log.w(TAG, e); - } + invokeListeners(null, listener -> listener.onPeerDisconnected(node)); removeConnectedNode(node.getId()); } public void onConnectedNodes(List nodes) { Log.d(TAG, "onConnectedNodes: " + nodes); - try { - getAllListeners().onConnectedNodes(nodes); - } catch (RemoteException e) { - Log.w(TAG, e); - } + invokeListeners(null, listener -> listener.onConnectedNodes(nodes)); } public DataItemRecord putData(PutDataRequest request, String packageName) { @@ -444,11 +460,6 @@ public class WearableImpl { public DataHolder getDataItemsAsHolder(String packageName) { Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolder(packageName, PackageUtils.firstSignatureDigest(context, packageName)); - while (dataHolderItems.moveToNext()) { - Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath()); - } - dataHolderItems.moveToFirst(); - dataHolderItems.moveToPrevious(); return new DataHolder(dataHolderItems, 0, null); } @@ -467,40 +478,26 @@ public class WearableImpl { return null; } Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, fixHost(uri.getHost(), false), uri.getPath()); - maybeDebugCursor("getDataItems",dataHolderItems); - dataHolderItems.moveToFirst(); - dataHolderItems.moveToPrevious(); DataHolder dataHolder = new DataHolder(dataHolderItems, 0, null); Log.d(TAG, "Returning data holder of size " + dataHolder.getCount() + " for query " + uri); return dataHolder; } - @TargetApi(11) - private void maybeDebugCursor(String what, Cursor cursor) { - if (SDK_INT >= 11) { - int j = 0; - while (cursor.moveToNext()) { - for (int i = 0; i < cursor.getColumnCount(); i++) { - if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING) { - Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getString(i)); - } - if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER) - Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getLong(i)); - } - } - } - } - - public synchronized void addListener(String packageName, IWearableListener listener) { + public synchronized void addListener(String packageName, IWearableListener listener, IntentFilter[] filters) { if (!listeners.containsKey(packageName)) { - listeners.put(packageName, new ArrayList()); + listeners.put(packageName, new ArrayList()); } - listeners.get(packageName).add(listener); + listeners.get(packageName).add(new ListenerInfo(listener, filters)); } public void removeListener(IWearableListener listener) { - for (List list : listeners.values()) { - list.remove(listener); + for (List list : listeners.values()) { + for (int i = 0; i < list.size(); i++) { + if (list.get(i).listener.equals(listener)) { + list.remove(i); + i--; + } + } } } @@ -546,12 +543,10 @@ public class WearableImpl { public void sendMessageReceived(String packageName, MessageEventParcelable messageEvent) { Log.d(TAG, "onMessageReceived: " + messageEvent); - try { - getListener(packageName, "com.google.android.gms.wearable.MESSAGE_RECEIVED", Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath())) - .onMessageReceived(messageEvent); - } catch (RemoteException e) { - Log.w(TAG, e); - } + Intent intent = new Intent("com.google.android.gms.wearable.MESSAGE_RECEIVED"); + intent.setPackage(packageName); + intent.setData(Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath())); + invokeListeners(intent, listener -> listener.onMessageReceived(messageEvent)); } public DataItemRecord getDataItemByUri(Uri uri, String packageName) { @@ -568,17 +563,11 @@ public class WearableImpl { } private IWearableListener getListener(String packageName, String action, Uri uri) { - synchronized (this) { - List l = new ArrayList(listeners.containsKey(packageName) ? listeners.get(packageName) : Collections.emptyList()); + Intent intent = new Intent(action); + intent.setPackage(packageName); + intent.setData(uri); - Intent intent = new Intent(action); - intent.setPackage(packageName); - intent.setData(uri); - - l.add(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER")); - - return MultiListenerProxy.get(IWearableListener.class, l); - } + return RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER"); } private void closeConnection(String nodeId) { @@ -594,7 +583,7 @@ public class WearableImpl { } activeConnections.remove(nodeId); for (ConnectionConfiguration config : getConfigurations()) { - if (config.nodeId.equals(nodeId) || config.peerNodeId.equals(nodeId)) { + if (nodeId.equals(config.nodeId) || nodeId.equals(config.peerNodeId)) { config.connected = false; } } @@ -631,4 +620,14 @@ public class WearableImpl { public void stop() { this.networkHandler.getLooper().quit(); } + + private class ListenerInfo { + private IWearableListener listener; + private IntentFilter[] filters; + + private ListenerInfo(IWearableListener listener, IntentFilter[] filters) { + this.listener = listener; + this.filters = filters; + } + } } diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java index ef830d3b49d7ba74f885ce8eac4b83c2b6768891..e091ba729c732ff7a172c6251170f45e3f546eec 100644 --- a/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java +++ b/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2017 microG Project Team + * 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. @@ -29,8 +29,11 @@ import com.google.android.gms.common.api.Status; import com.google.android.gms.wearable.Asset; import com.google.android.gms.wearable.ConnectionConfiguration; import com.google.android.gms.wearable.internal.AddListenerRequest; +import com.google.android.gms.wearable.internal.AddLocalCapabilityResponse; import com.google.android.gms.wearable.internal.AncsNotificationParcelable; +import com.google.android.gms.wearable.internal.CapabilityInfoParcelable; import com.google.android.gms.wearable.internal.DeleteDataItemsResponse; +import com.google.android.gms.wearable.internal.GetCapabilityResponse; import com.google.android.gms.wearable.internal.GetCloudSyncSettingResponse; import com.google.android.gms.wearable.internal.GetConfigResponse; import com.google.android.gms.wearable.internal.GetConfigsResponse; @@ -45,9 +48,13 @@ import com.google.android.gms.wearable.internal.NodeParcelable; import com.google.android.gms.wearable.internal.PutDataRequest; import com.google.android.gms.wearable.internal.PutDataResponse; import com.google.android.gms.wearable.internal.RemoveListenerRequest; +import com.google.android.gms.wearable.internal.RemoveLocalCapabilityResponse; import com.google.android.gms.wearable.internal.SendMessageResponse; import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; public class WearableServiceImpl extends IWearableService.Stub { private static final String TAG = "GmsWearSvcImpl"; @@ -56,11 +63,13 @@ public class WearableServiceImpl extends IWearableService.Stub { private final String packageName; private final WearableImpl wearable; private final Handler mainHandler; + private final CapabilityManager capabilities; public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) { this.context = context; this.wearable = wearable; this.packageName = packageName; + this.capabilities = new CapabilityManager(context, wearable, packageName); this.mainHandler = new Handler(context.getMainLooper()); } @@ -191,8 +200,11 @@ public class WearableServiceImpl extends IWearableService.Stub { @Override public void deleteDataItemsWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException { Log.d(TAG, "deleteDataItems: " + uri); - postMain(callbacks, () -> { - callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName))); + this.wearable.networkHandler.post(new CallbackRunnable(callbacks) { + @Override + public void run(IWearableCallbacks callbacks) throws RemoteException { + callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName))); + } }); } @@ -289,29 +301,49 @@ public class WearableServiceImpl extends IWearableService.Stub { */ @Override - public void getConnectedCapability(IWearableCallbacks callbacks, String s1, int i) throws RemoteException { - Log.d(TAG, "unimplemented Method: getConnectedCapability " + s1 + ", " + i); + public void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) throws RemoteException { + Log.d(TAG, "unimplemented Method: getConnectedCapability " + capability + ", " + nodeFilter); + postMain(callbacks, () -> { + List nodes = new ArrayList<>(); + for (String host : capabilities.getNodesForCapability(capability)) { + nodes.add(new NodeParcelable(host, host)); + } + CapabilityInfoParcelable capabilityInfo = new CapabilityInfoParcelable(capability, nodes); + callbacks.onGetCapabilityResponse(new GetCapabilityResponse(0, capabilityInfo)); + }); } @Override - public void getConnectedCapaibilties(IWearableCallbacks callbacks, int i) throws RemoteException { - Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + i); + public void getConnectedCapaibilties(IWearableCallbacks callbacks, int nodeFilter) throws RemoteException { + Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + nodeFilter); } @Override - public void addLocalCapability(IWearableCallbacks callbacks, String cap) throws RemoteException { - Log.d(TAG, "unimplemented Method: addLocalCapability: " + cap); + public void addLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException { + Log.d(TAG, "unimplemented Method: addLocalCapability: " + capability); + this.wearable.networkHandler.post(new CallbackRunnable(callbacks) { + @Override + public void run(IWearableCallbacks callbacks) throws RemoteException { + callbacks.onAddLocalCapabilityResponse(new AddLocalCapabilityResponse(capabilities.add(capability))); + } + }); } @Override - public void removeLocalCapability(IWearableCallbacks callbacks, String cap) throws RemoteException { - Log.d(TAG, "unimplemented Method: removeLocalCapability: " + cap); + public void removeLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException { + Log.d(TAG, "unimplemented Method: removeLocalCapability: " + capability); + this.wearable.networkHandler.post(new CallbackRunnable(callbacks) { + @Override + public void run(IWearableCallbacks callbacks) throws RemoteException { + callbacks.onRemoveLocalCapabilityResponse(new RemoveLocalCapabilityResponse(capabilities.remove(capability))); + } + }); } @Override public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException { if (request.listener != null) { - wearable.addListener(packageName, request.listener); + wearable.addListener(packageName, request.listener, request.intentFilters); } callbacks.onStatus(Status.SUCCESS); } diff --git a/play-services-maps-core-mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java b/play-services-maps-core-mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java index df0fb3e9823197cc3ed15b5c0a803259f9c2fcb6..96a3c191bb7849514434110b278619ff80c1b493 100644 --- a/play-services-maps-core-mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java +++ b/play-services-maps-core-mapbox/src/main/java/com/google/android/gms/maps/internal/CreatorImpl.java @@ -65,9 +65,9 @@ public class CreatorImpl extends ICreator.Stub { @Override public void initV2(IObjectWrapper resources, int flags) { - BitmapDescriptorFactoryImpl.INSTANCE.initialize(ObjectWrapper.unwrapTyped(resources, Resources.class)); + BitmapDescriptorFactoryImpl.INSTANCE.initialize(ObjectWrapper.unwrapTyped(resources, Resources.class), null); //ResourcesContainer.set((Resources) ObjectWrapper.unwrap(resources)); - Log.d(TAG, "initV2"); + Log.d(TAG, "initV2 " + flags); } @Override diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt index 654feca6b5613d652047690f2ba0443849df4527..eea4304f56161ce47b83bff5400d2bea29595ab7 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraBoundsWithSizeUpdate.kt @@ -16,6 +16,7 @@ package org.microg.gms.maps.mapbox +import android.util.Log import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.camera.CameraUpdate import com.mapbox.mapboxsdk.geometry.LatLngBounds @@ -28,12 +29,13 @@ internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: I 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 + val widthPad = ((map.width + map.padding[0] + map.padding[2] - width) / 2).toInt() + val heightPad = ((map.height + map.padding[1] + map.padding[3] - height) / 2).toInt() padding[0] += widthPad padding[1] += heightPad padding[2] += widthPad padding[3] += heightPad + Log.d(TAG, "map ${map.width} ${map.height}, set $width $height -> ${padding.map { it.toString() }.reduce { a, b -> "$a,$b"}}") return map.getCameraForLatLngBounds(bounds, padding) } @@ -78,6 +80,6 @@ internal class CameraBoundsWithSizeUpdate(val bounds: LatLngBounds, val width: I } companion object { - val TAG = "GmsCameraBounds" + const val TAG = "GmsMapCameraBounds" } } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt index 03e92de2079d9eb2f018fdc74c17a8f6338137b0..150afb4bc9677575a458cb21a3d4d9e937f84f30 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/CameraUpdateFactory.kt @@ -61,11 +61,8 @@ class CameraUpdateFactoryImpl : ICameraUpdateFactoryDelegate.Stub() { 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 newLatLngBoundsWithSize(bounds: LatLngBounds, width: Int, height: Int, padding: Int): IObjectWrapper = + 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)) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt index f809b4f4a5d9c13cfe6547a018f6052be355ba70..b31472cb446c847765d7700d7ef3af5963b47739 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/GoogleMap.kt @@ -20,6 +20,7 @@ import android.annotation.SuppressLint import android.content.Context import android.location.Location import android.os.Bundle +import android.os.IBinder import android.os.Parcel import android.support.annotation.IdRes import android.support.annotation.Keep @@ -66,12 +67,14 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) val dpiFactor: Float get() = context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT - private var mapView: MapView? + private var mapView: MapView? = null private var created = false private var initialized = false - private val initializedCallbackList = mutableListOf() + private var loaded = false private val mapLock = Object() + private val initializedCallbackList = mutableListOf() + private var loadedCallback : IOnMapLoadedCallback? = null private var cameraChangeListener: IOnCameraChangeListener? = null private var cameraMoveListener: IOnCameraMoveListener? = null private var cameraMoveCanceledListener: IOnCameraMoveCanceledListener? = null @@ -83,7 +86,12 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) private var markerDragListener: IOnMarkerDragListener? = null var lineManager: LineManager? = null + val pendingLines = mutableSetOf() + var lineId = 0L + var fillManager: FillManager? = null + val pendingFills = mutableSetOf() + var fillId = 0L var circleManager: CircleManager? = null val pendingCircles = mutableSetOf() @@ -99,6 +107,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) init { val mapContext = MapContext(context) + BitmapDescriptorFactoryImpl.initialize(mapContext.resources, context.resources) LibraryLoader.setLibraryLoader(MultiArchLoader(mapContext, context)) Mapbox.getInstance(mapContext, BuildConfig.MAPBOX_KEY) @@ -137,8 +146,6 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) return null } } - this.mapView = MapView(mapContext) - this.view.addView(this.mapView) } override fun getCameraPosition(): CameraPosition? = map?.cameraPosition?.toGms() @@ -147,43 +154,57 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) override fun moveCamera(cameraUpdate: IObjectWrapper?) { val update = cameraUpdate.unwrap() ?: return - val map = this.map - if (map != null) { - map.moveCamera(update) - } else { - waitingCameraUpdates.add(update) + synchronized(mapLock) { + if (initialized) { + this.map?.moveCamera(update) + } else { + waitingCameraUpdates.add(update) + } } } override fun animateCamera(cameraUpdate: IObjectWrapper?) { val update = cameraUpdate.unwrap() ?: return - val map = this.map - if (map != null) { - map.animateCamera(update) - } else { - waitingCameraUpdates.add(update) + synchronized(mapLock) { + if (initialized) { + this.map?.animateCamera(update) + } else { + waitingCameraUpdates.add(update) + } } } + fun afterInitialized(runnable: () -> Unit) { + initializedCallbackList.add(object : IOnMapReadyCallback { + override fun onMapReady(map: IGoogleMapDelegate?) { + runnable() + } + + override fun asBinder(): IBinder? = null + }) + } + override fun animateCameraWithCallback(cameraUpdate: IObjectWrapper?, callback: ICancelableCallback?) { val update = cameraUpdate.unwrap() ?: return - val map = this.map - if (map != null) { - map.animateCamera(update, callback?.toMapbox()) - } else { - waitingCameraUpdates.add(update) - callback?.onFinish() + synchronized(mapLock) { + if (initialized) { + this.map?.animateCamera(update, callback?.toMapbox()) + } else { + waitingCameraUpdates.add(update) + afterInitialized { callback?.onFinish() } + } } } override fun animateCameraWithDurationAndCallback(cameraUpdate: IObjectWrapper?, duration: Int, callback: ICancelableCallback?) { val update = cameraUpdate.unwrap() ?: return - val map = this.map - if (map != null) { - map.animateCamera(update, duration, callback?.toMapbox()) - } else { - waitingCameraUpdates.add(update) - callback?.onFinish() + synchronized(mapLock) { + if (initialized) { + this.map?.animateCamera(update, duration, callback?.toMapbox()) + } else { + waitingCameraUpdates.add(update) + afterInitialized { callback?.onFinish() } + } } } @@ -207,21 +228,30 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } 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)) } + val line = PolylineImpl(this, "l${lineId++}", options) + synchronized(this) { + val lineManager = lineManager + if (lineManager == null) { + pendingLines.add(line) + } else { + line.update(lineManager) + } + } + return line } override fun addPolygon(options: PolygonOptions): IPolygonDelegate? { - val fillOptions = FillOptions() - .withLatLngs(listOf(options.points.map { it.toMapbox() })) - .withFillColor(ColorUtils.colorToRgbaString(options.fillColor)) - .withFillOpacity(if (options.isVisible) 1f else 0f) - return fillManager?.let { PolygonImpl(this, it.create(fillOptions)) } + val fill = PolygonImpl(this, "p${fillId++}", options) + synchronized(this) { + val fillManager = fillManager + if (fillManager == null) { + pendingFills.add(fill) + } else { + fill.update(fillManager) + } + } + return fill } override fun addMarker(options: MarkerOptions): IMarkerDelegate? { @@ -412,7 +442,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { - Log.d(TAG, "padding: $left, $top, $right, $bottom") + Log.d(TAG, "setPadding: $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() @@ -433,8 +463,23 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) } override fun setOnMapLoadedCallback(callback: IOnMapLoadedCallback?) { - Log.d(TAG, "unimplemented Method: setOnMapLoadedCallback") - + if (callback != null) { + synchronized(mapLock) { + if (loaded) { + Log.d(TAG, "Invoking callback instantly, as map is loaded") + try { + callback.onMapLoaded() + } catch (e: Exception) { + Log.w(TAG, e) + } + } else { + Log.d(TAG, "Delay callback invocation, as map is not yet loaded") + loadedCallback = callback + } + } + } else { + loadedCallback = null + } } override fun setCameraMoveStartedListener(listener: IOnCameraMoveStartedListener?) { @@ -455,8 +500,12 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) override fun onCreate(savedInstanceState: Bundle?) { if (!created) { - mapView?.onCreate(savedInstanceState?.toMapbox()) - mapView?.getMapAsync(this::initMap) + Log.d(TAG, "create"); + val mapView = MapView(MapContext(context)) + this.mapView = mapView + view.addView(mapView) + mapView.onCreate(savedInstanceState?.toMapbox()) + mapView.getMapAsync(this::initMap) created = true } } @@ -533,9 +582,11 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) options.scrollGesturesEnabled?.let { map.uiSettings.isScrollGesturesEnabled = it } options.tiltGesturesEnabled?.let { map.uiSettings.isTiltGesturesEnabled = it } options.camera?.let { map.cameraPosition = it.toMapbox() } - waitingCameraUpdates.forEach { map.moveCamera(it) } synchronized(mapLock) { + initialized = true + waitingCameraUpdates.forEach { map.moveCamera(it) } + val initializedCallbackList = ArrayList(initializedCallbackList) Log.d(TAG, "Invoking ${initializedCallbackList.size} callbacks delayed, as map is initialized") for (callback in initializedCallbackList) { try { @@ -544,30 +595,37 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) Log.w(TAG, e) } } - initialized = true } map.getStyle { mapView?.let { view -> - circleManager = CircleManager(view, map, it) - lineManager = LineManager(view, map, it) - fillManager = FillManager(view, map, it) - lineManager?.lineCap = LINE_CAP_ROUND - synchronized(this) { - val symbolManager = SymbolManager(view, map, it) - pendingMarkers.forEach { it.update(symbolManager) } - pendingMarkers.clear() + if (loaded) return@let + val symbolManager: SymbolManager + val lineManager: LineManager + val circleManager: CircleManager + val fillManager: FillManager + + synchronized(mapLock) { + circleManager = CircleManager(view, map, it) + fillManager = FillManager(view, map, it) + symbolManager = SymbolManager(view, map, it) + lineManager = LineManager(view, map, it) + lineManager.lineCap = LINE_CAP_ROUND + this.symbolManager = symbolManager + this.lineManager = lineManager + this.circleManager = circleManager + this.fillManager = fillManager } - symbolManager?.iconAllowOverlap = true - symbolManager?.addClickListener { + symbolManager.iconAllowOverlap = true + symbolManager.addClickListener { try { markers[it.id]?.let { markerClickListener?.onMarkerClick(it) } } catch (e: Exception) { Log.w(TAG, e) } } - symbolManager?.addDragListener(object : OnSymbolDragListener { + symbolManager.addDragListener(object : OnSymbolDragListener { override fun onAnnotationDragStarted(annotation: Symbol?) { try { markers[annotation?.id]?.let { markerDragListener?.onMarkerDragStart(it) } @@ -591,9 +649,23 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) Log.w(TAG, e) } } - }) - + pendingCircles.forEach { it.update(circleManager) } + pendingCircles.clear() + pendingFills.forEach { it.update(fillManager) } + pendingFills.clear() + pendingLines.forEach { it.update(lineManager) } + pendingLines.clear() + pendingMarkers.forEach { it.update(symbolManager) } + pendingMarkers.clear() + + synchronized(mapLock) { + loaded = true + if (loadedCallback != null) { + Log.d(TAG, "Invoking callback delayed, as map is loaded") + loadedCallback?.onMapLoaded() + } + } } } } @@ -606,6 +678,7 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) override fun onResume() = mapView?.onResume() ?: Unit override fun onPause() = mapView?.onPause() ?: Unit override fun onDestroy() { + Log.d(TAG, "destroy"); circleManager?.onDestroy() circleManager = null lineManager?.onDestroy() @@ -621,14 +694,18 @@ class GoogleMapImpl(private val context: Context, var options: GoogleMapOptions) // TODO can crash? mapView?.onDestroy() mapView = null + map = null + created = false + initialized = false + loaded = false } override fun onStart() { - Log.d(TAG, "unimplemented Method: onStart") + mapView?.onStart() } override fun onStop() { - Log.d(TAG, "unimplemented Method: onStop") + mapView?.onStop() } override fun onEnterAmbient(bundle: Bundle?) { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt index 78636e93a13f68d2342849fd72e8503f906730a2..0ef6c589ac9c715cb93633f335a3c59f6695b901 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/MapFragment.kt @@ -35,8 +35,7 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu private var map: GoogleMapImpl? = null private var options: GoogleMapOptions? = null - override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle) { - Log.d(TAG, "onInflate: ${options.camera.target}") + override fun onInflate(activity: IObjectWrapper, options: GoogleMapOptions, savedInstanceState: Bundle?) { this.options = options map?.options = options } @@ -48,7 +47,6 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu if (options == null) { options = GoogleMapOptions() } - Log.d(TAG, "onCreate: ${options?.camera?.target}") map = GoogleMapImpl(activity, options ?: GoogleMapOptions()) } @@ -80,7 +78,6 @@ class MapFragmentImpl(private val activity: Activity) : IMapFragmentDelegate.Stu override fun onDestroyView() { map?.onDestroy() - map = null } override fun onDestroy() { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt index bf5cf153d54e9a0bc93729eeb7f3a8633bc46700..32f5243438868d90cfbbf2eaebb772ed3d07cf07 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/BitmapDescriptorFactory.kt @@ -17,23 +17,26 @@ package org.microg.gms.maps.mapbox.model import android.content.res.Resources -import android.graphics.Bitmap -import android.graphics.BitmapFactory +import android.graphics.* 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 +import org.microg.gms.maps.mapbox.R + object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { private val TAG = "GmsMapBitmap" private var resources: Resources? = null + private var mapResources: Resources? = null private val maps = hashSetOf() private val bitmaps = hashMapOf() - fun initialize(resources: Resources) { - BitmapDescriptorFactoryImpl.resources = resources + fun initialize(mapResources: Resources?, resources: Resources?) { + BitmapDescriptorFactoryImpl.mapResources = mapResources ?: resources + BitmapDescriptorFactoryImpl.resources = resources ?: mapResources } fun registerMap(map: MapboxMap) { @@ -56,9 +59,13 @@ object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { ?: floatArrayOf(0f, 0f) private fun registerBitmap(id: String, bitmapCreator: () -> Bitmap?) { - val bitmap = synchronized(bitmaps) { + val bitmap: Bitmap = synchronized(bitmaps) { if (bitmaps.contains(id)) return - val bitmap = bitmapCreator() ?: return + val bitmap = bitmapCreator() + if (bitmap == null) { + Log.w(TAG, "Failed to register bitmap $id, creator returned null") + return + } bitmaps[id] = bitmap bitmap } @@ -69,7 +76,17 @@ object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { override fun fromResource(resourceId: Int): IObjectWrapper? { val id = "resource-$resourceId" - registerBitmap(id) { BitmapFactory.decodeResource(resources, resourceId) } + registerBitmap(id) { + val bitmap = BitmapFactory.decodeResource(resources, resourceId) + if (bitmap == null) { + try { + Log.d(TAG, "Resource $resourceId not found in $resources (${resources?.getResourceName(resourceId)})") + } catch (e: Resources.NotFoundException) { + Log.d(TAG, "Resource $resourceId not found in $resources") + } + } + bitmap + } return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id))) } @@ -86,15 +103,45 @@ object BitmapDescriptorFactoryImpl : IBitmapDescriptorFactoryDelegate.Stub() { } override fun defaultMarker(): IObjectWrapper? { - Log.d(TAG, "unimplemented Method: defaultMarker") val id = "marker" - return ObjectWrapper.wrap(BitmapDescriptorImpl("marker", bitmapSize(id))) + registerBitmap(id) { BitmapFactory.decodeResource(mapResources, R.drawable.maps_default_marker) } + return ObjectWrapper.wrap(BitmapDescriptorImpl(id, bitmapSize(id))) + } + + private fun adjustHue(cm: ColorMatrix, value: Float) { + var value = value + value = cleanValue(value, 180f) / 180f * Math.PI.toFloat() + if (value == 0f) { + return + } + val cosVal = Math.cos(value.toDouble()).toFloat() + val sinVal = Math.sin(value.toDouble()).toFloat() + val lumR = 0.213f + val lumG = 0.715f + val lumB = 0.072f + val mat = floatArrayOf(lumR + cosVal * (1 - lumR) + sinVal * -lumR, lumG + cosVal * -lumG + sinVal * -lumG, lumB + cosVal * -lumB + sinVal * (1 - lumB), 0f, 0f, lumR + cosVal * -lumR + sinVal * 0.143f, lumG + cosVal * (1 - lumG) + sinVal * 0.140f, lumB + cosVal * -lumB + sinVal * -0.283f, 0f, 0f, lumR + cosVal * -lumR + sinVal * -(1 - lumR), lumG + cosVal * -lumG + sinVal * lumG, lumB + cosVal * (1 - lumB) + sinVal * lumB, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f) + cm.postConcat(ColorMatrix(mat)) + } + + private fun cleanValue(p_val: Float, p_limit: Float): Float { + return Math.min(p_limit, Math.max(-p_limit, p_val)) } override fun defaultMarkerWithHue(hue: Float): IObjectWrapper? { - val id = "marker" - Log.d(TAG, "unimplemented Method: defaultMarkerWithHue") - return ObjectWrapper.wrap(ColorBitmapDescriptorImpl("marker", bitmapSize(id), hue)) + val id = "marker-${hue.toInt()}" + registerBitmap(id) { + val bitmap = BitmapFactory.decodeResource(mapResources, R.drawable.maps_default_marker).copy(Bitmap.Config.ARGB_8888, true) + val paint = Paint() + val matrix = ColorMatrix() + val huex = hue % 360f + adjustHue(matrix, if (huex > 180f) huex - 360f else huex) + paint.setColorFilter(ColorMatrixColorFilter(matrix)) + + val canvas = Canvas(bitmap) + canvas.drawBitmap(bitmap, 0f, 0f, paint) + bitmap + } + return ObjectWrapper.wrap(ColorBitmapDescriptorImpl(id, bitmapSize(id), hue)) } override fun fromBitmap(bitmap: Bitmap): IObjectWrapper? { diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt index 90c4309ee8fe7cad6ba4d8ca165fb4f858295450..48f465e8ad3ba278639f95dcbaad93f184f02b4f 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Circle.kt @@ -116,6 +116,14 @@ class CircleImpl(private val map: GoogleMapImpl, private val id: String, options override fun hashCodeRemote(): Int = hashCode() + override fun hashCode(): Int { + return id.hashCode() + } + + override fun toString(): String { + return id + } + override fun equals(other: Any?): Boolean { if (other is CircleImpl) { return other.id == id diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt index f0994173a51e406e41e3e2310691a679557410bb..c737afccd4af5be9ddaf052d55386d300ddc6eb1 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Markup.kt @@ -16,6 +16,7 @@ package org.microg.gms.maps.mapbox.model +import android.util.Log import com.mapbox.mapboxsdk.plugins.annotation.Annotation import com.mapbox.mapboxsdk.plugins.annotation.AnnotationManager import com.mapbox.mapboxsdk.plugins.annotation.Options @@ -37,4 +38,8 @@ interface Markup, S : Options> { } } } + + companion object { + private val TAG = "GmsMapMarkup" + } } \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt index 9e5ca9f54f38522e21e14691775ca2bcafa9c252..1790e2b10fa0faed4b740cc16307baf66e5703b9 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polygon.kt @@ -19,57 +19,93 @@ 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.PolygonOptions +import com.google.android.gms.maps.model.PolylineOptions import com.google.android.gms.maps.model.internal.IPolygonDelegate +import com.mapbox.mapboxsdk.plugins.annotation.AnnotationManager import com.mapbox.mapboxsdk.plugins.annotation.Fill +import com.mapbox.mapboxsdk.plugins.annotation.FillOptions +import com.mapbox.mapboxsdk.utils.ColorUtils import org.microg.gms.maps.mapbox.GoogleMapImpl -import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox -class PolygonImpl(private val map: GoogleMapImpl, private val fill: Fill) : IPolygonDelegate.Stub() { +class PolygonImpl(private val map: GoogleMapImpl, private val id: String, options: PolygonOptions) : IPolygonDelegate.Stub(), Markup { + private var points = ArrayList(options.points) + private var holes: List> = ArrayList(options.holes.map { ArrayList(it) }) + private var fillColor = options.fillColor + private var strokeColor = options.strokeColor + private var strokeWidth = options.strokeWidth + private var visible: Boolean = options.isVisible + + private var strokes = (listOf(PolylineImpl(map, "$id-stroke-main", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(points))) + + holes.mapIndexed { idx, it -> PolylineImpl(map, "$id-stroke-hole-$idx", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it)) }).toMutableList() + + override var annotation: Fill? = null + override var removed: Boolean = false + override val annotationOptions: FillOptions + get() = FillOptions() + .withLatLngs(mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } })) + .withFillColor(ColorUtils.colorToRgbaString(fillColor)) + .withFillOpacity(if (visible) 1f else 0f) + override fun remove() { - map.fillManager?.delete(fill) + removed = true + map.fillManager?.let { update(it) } + strokes.forEach { it.remove() } + } + + override fun update(manager: AnnotationManager<*, Fill, FillOptions, *, *, *>) { + super.update(manager) + map.lineManager?.let { lineManager -> strokes.forEach { it.update(lineManager) } } } - override fun getId(): String = "p" + fill.id.toString() + override fun getId(): String = id override fun setPoints(points: List) { - fill.latLngs = listOf(points.map { it.toMapbox() }) - map.fillManager?.update(fill) + this.points = ArrayList(points) + annotation?.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(holes.map { it.map { it.toMapbox() } }) + map.fillManager?.let { update(it) } + strokes[0].points = points } - override fun getPoints(): List = fill.latLngs[0]?.map { it.toGms() } ?: emptyList() + override fun getPoints(): List = points override fun setHoles(holes: List?) { - Log.d(TAG, "unimplemented Method: setHoles") + this.holes = if (holes == null) emptyList() else ArrayList(holes.mapNotNull { if (it is List<*>) it.mapNotNull { if (it is LatLng) it else null }.let { if (it.isNotEmpty()) it else null } else null }) + annotation?.latLngs = mutableListOf(points.map { it.toMapbox() }).plus(this.holes.map { it.map { it.toMapbox() } }) + while (this.holes.size < strokes.size) { + val last = strokes.last() + last.remove() + strokes.remove(last) + } + strokes.forEachIndexed { idx, it -> it.points = this.holes[idx] } + strokes.addAll(this.holes.subList(strokes.size, this.holes.lastIndex).mapIndexed { idx, it -> PolylineImpl(map, "$id-stroke-hole-${strokes.size + idx}", PolylineOptions().color(strokeColor).width(strokeWidth).addAll(it)) }) + map.fillManager?.let { update(it) } } - override fun getHoles(): List { - Log.d(TAG, "unimplemented Method: getHoles") - return emptyList() - } + override fun getHoles(): List = holes override fun setStrokeWidth(width: Float) { - Log.d(TAG, "unimplemented Method: setStrokeWidth") + this.strokeWidth = width + strokes.forEach { it.width = width } } - override fun getStrokeWidth(): Float { - Log.d(TAG, "unimplemented Method: getStrokeWidth") - return 0f - } + override fun getStrokeWidth(): Float = strokeWidth override fun setStrokeColor(color: Int) { - fill.setFillOutlineColor(color) - map.fillManager?.update(fill) + this.strokeColor = color + strokes.forEach { it.color = color } } - override fun getStrokeColor(): Int = fill.fillOutlineColorAsInt + override fun getStrokeColor(): Int = strokeColor override fun setFillColor(color: Int) { - fill.setFillColor(color) - map.fillManager?.update(fill) + this.fillColor = color + annotation?.setFillColor(color) + map.fillManager?.let { update(it) } } - override fun getFillColor(): Int = fill.fillColorAsInt + override fun getFillColor(): Int = fillColor override fun setZIndex(zIndex: Float) { Log.d(TAG, "unimplemented Method: setZIndex") @@ -81,11 +117,12 @@ class PolygonImpl(private val map: GoogleMapImpl, private val fill: Fill) : IPol } override fun setVisible(visible: Boolean) { - fill.fillOpacity = if (visible) 1f else 0f - map.fillManager?.update(fill) + this.visible = visible + annotation?.fillOpacity = if (visible) 1f else 0f + map.fillManager?.let { update(it) } } - override fun isVisible(): Boolean = fill.fillOpacity != 0f + override fun isVisible(): Boolean = visible override fun setGeodesic(geod: Boolean) { Log.d(TAG, "unimplemented Method: setGeodesic") @@ -100,9 +137,17 @@ class PolygonImpl(private val map: GoogleMapImpl, private val fill: Fill) : IPol override fun hashCodeRemote(): Int = hashCode() + override fun hashCode(): Int { + return id.hashCode() + } + + override fun toString(): String { + return id + } + override fun equals(other: Any?): Boolean { if (other is PolygonImpl) { - return other.fill == fill + return other.id == id } return false } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt index 75ee07368aa1930b164dc1e3cda86d9cbadf43f1..5871b6a7c31f19f7bd7262cdb73198b6bc6a1ccf 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/model/Polyline.kt @@ -21,37 +21,57 @@ 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 com.mapbox.mapboxsdk.plugins.annotation.LineOptions +import com.mapbox.mapboxsdk.utils.ColorUtils import org.microg.gms.maps.mapbox.GoogleMapImpl -import org.microg.gms.maps.mapbox.utils.toGms import org.microg.gms.maps.mapbox.utils.toMapbox +import com.google.android.gms.maps.model.PolylineOptions as GmsLineOptions + +class PolylineImpl(private val map: GoogleMapImpl, private val id: String, options: GmsLineOptions) : IPolylineDelegate.Stub(), Markup { + private var points = ArrayList(options.points) + private var width = options.width + private var color = options.color + private var visible: Boolean = options.isVisible + + override var annotation: Line? = null + override var removed: Boolean = false + override val annotationOptions: LineOptions + get() = LineOptions() + .withLatLngs(points.map { it.toMapbox() }) + .withLineWidth(width / map.dpiFactor) + .withLineColor(ColorUtils.colorToRgbaString(color)) + .withLineOpacity(if (visible) 1f else 0f) -class PolylineImpl(private val map: GoogleMapImpl, private val line: Line) : IPolylineDelegate.Stub() { override fun remove() { - map.lineManager?.delete(line) + removed = true + map.lineManager?.let { update(it) } } - override fun getId(): String = "l" + line.id.toString() + override fun getId(): String = id override fun setPoints(points: List) { - line.latLngs = points.map { it.toMapbox() } - map.lineManager?.update(line) + this.points = ArrayList(points) + annotation?.latLngs = points.map { it.toMapbox() } + map.lineManager?.let { update(it) } } - override fun getPoints(): List = line.latLngs.map { it.toGms() } + override fun getPoints(): List = points override fun setWidth(width: Float) { - line.lineWidth = width / map.dpiFactor - map.lineManager?.update(line) + this.width = width + annotation?.lineWidth = width / map.dpiFactor + map.lineManager?.let { update(it) } } - override fun getWidth(): Float = line.lineWidth * map.dpiFactor + override fun getWidth(): Float = width override fun setColor(color: Int) { - line.setLineColor(color) - map.lineManager?.update(line) + this.color = color + annotation?.setLineColor(color) + map.lineManager?.let { update(it) } } - override fun getColor(): Int = line.lineColorAsInt + override fun getColor(): Int = color override fun setZIndex(zIndex: Float) { Log.d(TAG, "unimplemented Method: setZIndex") @@ -63,11 +83,12 @@ class PolylineImpl(private val map: GoogleMapImpl, private val line: Line) : IPo } override fun setVisible(visible: Boolean) { - line.lineOpacity = if (visible) 1f else 0f - map.lineManager?.update(line) + this.visible = visible + annotation?.lineOpacity = if (visible) 1f else 0f + map.lineManager?.let { update(it) } } - override fun isVisible(): Boolean = line.lineOpacity != 0f + override fun isVisible(): Boolean = visible override fun setGeodesic(geod: Boolean) { Log.d(TAG, "unimplemented Method: setGeodesic") @@ -82,9 +103,17 @@ class PolylineImpl(private val map: GoogleMapImpl, private val line: Line) : IPo override fun hashCodeRemote(): Int = hashCode() + override fun hashCode(): Int { + return id.hashCode() + } + + override fun toString(): String { + return id + } + override fun equals(other: Any?): Boolean { if (other is PolylineImpl) { - return other.line == line + return other.id == id } return false } diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt index bfcda14e0fecb345c38abe004e4340d6695634ea..598860f03268e49be5676b1ae1873ffc778e3bea 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/MultiArchLoader.kt @@ -29,7 +29,10 @@ class MultiArchLoader(private val mapContext: Context, private val appContext: C 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? + var primaryCpuAbi = ApplicationInfo::class.java.getField("primaryCpuAbi").get(otherAppInfo) as String? + if (primaryCpuAbi == "armeabi") { + primaryCpuAbi = "armeabi-v7a" + } if (primaryCpuAbi != null) { val path = "lib/$primaryCpuAbi/lib$name.so" val cacheFile = File("${appContext.cacheDir.absolutePath}/.gmscore/$path") diff --git a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt index 7cb5e5beb3a81a94b82bd86384426d8984defad4..d49cef35e92f68183eea547939b35cd9010e5c0b 100644 --- a/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt +++ b/play-services-maps-core-mapbox/src/main/kotlin/org/microg/gms/maps/mapbox/utils/typeConverter.kt @@ -17,30 +17,24 @@ package org.microg.gms.maps.mapbox.utils import android.os.Bundle -import android.util.Log 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 +import com.google.android.gms.maps.model.CameraPosition as GmsCameraPosition +import com.google.android.gms.maps.model.LatLng as GmsLatLng +import com.google.android.gms.maps.model.LatLngBounds as GmsLatLngBounds +import com.google.android.gms.maps.model.VisibleRegion as GmsVisibleRegion -fun com.google.android.gms.maps.model.LatLng.toMapbox(): LatLng = +fun GmsLatLng.toMapbox(): LatLng = LatLng(latitude, longitude) -fun com.google.android.gms.maps.model.LatLngBounds.toMapbox(): LatLngBounds = +fun GmsLatLngBounds.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 = +fun GmsCameraPosition.toMapbox(): CameraPosition = CameraPosition.Builder() .target(target.toMapbox()) .zoom(zoom.toDouble() - 1.0) @@ -60,23 +54,21 @@ fun Bundle.toMapbox(): Bundle { 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 GmsCameraPosition -> newBundle.putParcelable(key, value.toMapbox()) + is GmsLatLng -> newBundle.putParcelable(key, value.toMapbox()) + is GmsLatLngBounds -> 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 LatLng.toGms(): GmsLatLng = GmsLatLng(latitude, longitude) -fun LatLngBounds.toGms(): com.google.android.gms.maps.model.LatLngBounds = - com.google.android.gms.maps.model.LatLngBounds(southWest.toGms(), northEast.toGms()) +fun LatLngBounds.toGms(): GmsLatLngBounds = GmsLatLngBounds(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 CameraPosition.toGms(): GmsCameraPosition = + GmsCameraPosition(target.toGms(), zoom.toFloat() + 1.0f, tilt.toFloat(), bearing.toFloat()) fun Bundle.toGms(): Bundle { val newBundle = Bundle(this) @@ -92,5 +84,5 @@ fun Bundle.toGms(): Bundle { 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()) \ No newline at end of file +fun VisibleRegion.toGms(): GmsVisibleRegion = + GmsVisibleRegion(nearLeft.toGms(), nearRight.toGms(), farLeft.toGms(), farRight.toGms(), latLngBounds.toGms()) \ No newline at end of file diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-hdpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-hdpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..94c365a3e33b5785ca8d104fbda71a7181b40f38 Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-hdpi/maps_default_marker.png differ diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-ldpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-ldpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ca2bf63446acebf3ecfca236b6d528a39424c6 Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-ldpi/maps_default_marker.png differ diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-mdpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-mdpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..c79736e70791d059a56c5c9f476cfd2e3cda28ae Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-mdpi/maps_default_marker.png differ diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-xhdpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-xhdpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..0b1b1975b41e8acce97fbddd3fe1cb8700477d08 Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-xhdpi/maps_default_marker.png differ diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-xxhdpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-xxhdpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..709bf78d7b41a36aa12ef7db0a4eedb8bbfae25d Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-xxhdpi/maps_default_marker.png differ diff --git a/play-services-maps-core-mapbox/src/main/res/drawable-xxxhdpi/maps_default_marker.png b/play-services-maps-core-mapbox/src/main/res/drawable-xxxhdpi/maps_default_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..0de5d255aea9781fa48f3c13b5c3c5b661b35064 Binary files /dev/null and b/play-services-maps-core-mapbox/src/main/res/drawable-xxxhdpi/maps_default_marker.png differ diff --git a/proguard.flags b/proguard.flags index 6be46b9c23bd114ef38c3236ce452dc8e9a5c3a7..1b6700d51373bec144826bc30d75c4b30639b1be 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,9 +1,5 @@ -# We use ProGuard for optimizations, obfuscation is for those who have sth to hide --dontobfuscate --optimizations !code/allocation/variable - # We're referencing stuff that is unknown to the system --libraryjar ../unifiednlp-compat/build/classes/java/main +#-libraryjar ../unifiednlp-compat/build/classes/java/main -dontwarn java.awt.** -dontwarn javax.annotation.** @@ -15,6 +11,7 @@ -dontwarn com.caverock.androidsvg.** -dontwarn org.slf4j.** -dontwarn org.codehaus.jackson.** +-dontwarn com.android.location.provider.** # Disable ProGuard Notes, they won't help here -dontnote @@ -33,6 +30,15 @@ @org.microg.safeparcel.SafeParceled *; } +# Keep form data +-keepclassmembers class * { + @org.microg.gms.common.HttpFormClient$* *; +} + +# Keep our stuff +-keep class org.microg.** { *; } +-keep class com.google.android.gms.** { *; } + # Keep asInterface method cause it's accessed from SafeParcel -keepattributes InnerClasses -keepclassmembers interface * extends android.os.IInterface {