Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ protected void onCreate(Bundle savedInstanceState) {
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
// 根据用户设置创建地图提供商
String mapProviderType = this.userSettings.getMapProvider();
this.mapProvider = MapProviderFactory.create(mapProviderType);
this.mapProvider = MapProviderFactory.create(MapProviderFactory.effectiveProvider(this, mapProviderType));

// 初始化地图
this.mapProvider.initialize(this, R.id.history_map, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@

import android.bluetooth.le.ScanSettings;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.app.Dialog;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.location.Address;
import android.location.Geocoder;
Expand Down Expand Up @@ -502,7 +500,6 @@ protected void onCreate(Bundle savedInstanceState) {
app.setupTheme();

WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
this.checkApiKey();

this.binding = ActivityMapsBinding.inflate(getLayoutInflater());
setContentView(this.binding.getRoot());
Expand Down Expand Up @@ -560,6 +557,9 @@ protected void onCreate(Bundle savedInstanceState) {

// 根据用户设置创建地图提供商
String mapProviderType = this.userSettings.getMapProvider();
// A build without a Google Maps key (secrets.properties) still has a map: OpenStreetMap
// needs no key, so it stands in for Google rather than the app refusing to start.
mapProviderType = MapProviderFactory.effectiveProvider(this, mapProviderType);

// **Before the new provider touches it.** Changing provider calls recreate(), and the
// FragmentManager restores Google's map fragment into this container while a plain view
Expand Down Expand Up @@ -1751,26 +1751,6 @@ public void onClickMoreForDevice(View view) {
deviceInfoActivityLauncher.launch(deviceInfoIntent);
}

private void checkApiKey() {
try {
ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = Objects.requireNonNull(appInfo.metaData);

String apiKey = bundle.getString("com.google.android.geo.API_KEY");

if (apiKey == null || apiKey.isBlank() || apiKey.equals("DEFAULT_API_KEY")) {
Toast.makeText(this, "API Key was not set in secrets.properties", LENGTH_SHORT).show();
throw new RuntimeException("API Key was not set in secrets.properties");
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Package name not found.", e);
throw new RuntimeException("Error getting package info", e);
} catch (NullPointerException e) {
Log.e(TAG, "Error accessing meta-data.", e); // Handle the case where meta-data is completely missing
throw new RuntimeException("Error accessing meta-data in manifest", e);
}
}

private void handleAuthAndShowDevices() {
var async = userAuthRepo.getUserAuth()
.subscribe(this::getAppleUser,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package dev.wander.android.opentagviewer.ui.maps;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import java.util.Set;
import java.util.function.Supplier;

/**
Expand Down Expand Up @@ -47,6 +51,43 @@ public static void reset() {
replacement = null;
}

/**
* Values the Maps key takes in a build that has no real one, so Google would draw an empty
* grey map. {@code DEFAULT_API_KEY} comes from {@code local.defaults.properties} when there is
* no {@code secrets.properties}; {@code maps_key_default_value} is what
* {@code build-debug.yml} writes when the repository secret is unavailable, as on fork PRs.
*/
private static final Set<String> PLACEHOLDER_KEYS = Set.of("DEFAULT_API_KEY", "maps_key_default_value");

/**
* The provider to actually use. A build without a usable Google Maps key gets OpenStreetMap,
* which needs none, instead of a map that cannot load.
*/
public static String effectiveProvider(final Context context, final String providerType) {
String key = null;
try {
final Bundle meta = context.getPackageManager().getApplicationInfo(
context.getPackageName(), PackageManager.GET_META_DATA).metaData;
key = meta == null ? null : meta.getString("com.google.android.geo.API_KEY");
} catch (final Exception e) {
Log.w(TAG, "Could not read the Maps API key; treating it as absent", e);
}
return providerFor(providerType, key);
}

/** {@link #effectiveProvider(Context, String)} once the key has been read. */
static String providerFor(final String providerType, final String googleMapsKey) {
if (PROVIDER_AMAP.equals(providerType) || PROVIDER_OSM.equals(providerType)) {
return providerType;
}
// Google, unset or unknown - all of which create() turns into Google Maps.
if (googleMapsKey == null || googleMapsKey.isBlank() || PLACEHOLDER_KEYS.contains(googleMapsKey)) {
Log.w(TAG, "No usable Google Maps key in this build; using OpenStreetMap");
return PROVIDER_OSM;
}
return providerType;
}

public static IMapProvider create(String providerType) {
if (replacement != null) {
Log.d(TAG, "Creating a substituted map provider");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package dev.wander.android.opentagviewer.ui.maps;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class MapProviderFactoryTest {

private static final String A_REAL_KEY = "AIzaSyExampleExampleExampleExample123";

@Test
public void googleWithARealKeyStaysGoogle() {
assertEquals(MapProviderFactory.PROVIDER_GOOGLE,
MapProviderFactory.providerFor(MapProviderFactory.PROVIDER_GOOGLE, A_REAL_KEY));
}

@Test
public void googleWithoutAUsableKeyFallsBackToOpenStreetMap() {
for (final String key : new String[]{null, "", " ", "DEFAULT_API_KEY", "maps_key_default_value"}) {
assertEquals("key: " + key, MapProviderFactory.PROVIDER_OSM,
MapProviderFactory.providerFor(MapProviderFactory.PROVIDER_GOOGLE, key));
}
}

/** create() turns an unset or unknown provider into Google, so those need the key too. */
@Test
public void anUnsetOrUnknownProviderIsTreatedAsGoogle() {
assertEquals(MapProviderFactory.PROVIDER_OSM,
MapProviderFactory.providerFor(null, "DEFAULT_API_KEY"));
assertEquals(MapProviderFactory.PROVIDER_OSM,
MapProviderFactory.providerFor("bing", null));
assertEquals("bing", MapProviderFactory.providerFor("bing", A_REAL_KEY));
}

@Test
public void otherProvidersNeverDependOnTheGoogleKey() {
assertEquals(MapProviderFactory.PROVIDER_AMAP,
MapProviderFactory.providerFor(MapProviderFactory.PROVIDER_AMAP, null));
assertEquals(MapProviderFactory.PROVIDER_OSM,
MapProviderFactory.providerFor(MapProviderFactory.PROVIDER_OSM, null));
}
}
Loading