Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offline region feature #336

Merged
merged 57 commits into from
Dec 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
307d136
Update MapboxMapBuilder.java
kleeb Jan 9, 2020
6fbde64
Models for offline structure.
jrolinskifdt Apr 22, 2020
423bed2
Extract function for getting mapbox.
jrolinskifdt Apr 23, 2020
3e580b8
Download region
jrolinskifdt Apr 23, 2020
c6c6794
Handle downloading region stream.
jrolinskifdt Apr 24, 2020
0fa5cb3
Download regions data.
jrolinskifdt Apr 24, 2020
f512e84
Delete region
jrolinskifdt Apr 24, 2020
c8e5a54
Fix:
jrolinskifdt Apr 27, 2020
3473e19
WIP example
jrolinskifdt Apr 27, 2020
a554cd9
Rename region.
jrolinskifdt Apr 27, 2020
27044f2
WIP
jrolinskifdt Apr 27, 2020
0fb41b2
Show offline map.
jrolinskifdt Apr 27, 2020
fcd2923
refactor
jrolinskifdt Apr 27, 2020
206bd4a
Update android dependencies.
jrolinskifdt Jun 3, 2020
bccf419
Fixes
jrolinskifdt Jun 3, 2020
9963f57
Working or iOS offline maps implementation
patrykfdt Jun 3, 2020
caf2790
Merge branch 'feature/offline' of https://github.com/Asasello225/flut…
patrykfdt Jun 3, 2020
2af6e9d
Finished iOS offline maps implementation
patrykfdt Jun 8, 2020
69f091f
Merge branch 'master' into feature/offline
patrykfdt Jun 8, 2020
c5cc0df
test dependencies from git
andrea689 Jun 9, 2020
a276ba5
Merge pull request #5 from Asasello225/feature/offline
kleeb Jun 9, 2020
95aa106
add git dependencies
andrea689 Jun 9, 2020
71406f6
Merge branch 'master' of https://github.com/tobrun/flutter-mapbox-gl …
Jun 9, 2020
4ec5104
Merge branch 'tobrun-master' into feature/offline
Jun 9, 2020
0d7d5aa
change git urls to https
andrea689 Jun 9, 2020
f14408a
Merge pull request #7 from andrea689/dependencies-from-git
kleeb Jun 9, 2020
16456e7
Merge branch 'master' of https://github.com/tobrun/flutter-mapbox-gl …
Jun 22, 2020
7d9d5f2
Update MapboxMapController.java
kleeb Jun 22, 2020
8b2b8a0
Update MapboxMapController.java
kleeb Jun 22, 2020
2a82c18
Delete MapBoxUtils.java
kleeb Jun 22, 2020
a796290
Update MapboxMapController.java
kleeb Jun 22, 2020
31e1abb
Update MapboxMapBuilder.java
kleeb Jun 22, 2020
45a9c1c
Delete Flutter.podspec
kleeb Jun 22, 2020
11128ce
Update README.md
kleeb Jun 22, 2020
fda5a92
Update README.md
kleeb Jun 22, 2020
a90f189
lint fix
Jun 22, 2020
1909e6a
added utils
Jun 22, 2020
6a1bd26
downgraded gradle plugin
Jun 22, 2020
62ca51e
Merge pull request #11 from tobrun/master
kleeb Jun 26, 2020
0197c21
Merge pull request #13 from tobrun/master
kleeb Jun 30, 2020
ef34e6e
Allow user to provide accessToken by flutter side.
jrolinskifdt Jun 30, 2020
7c77759
Fix
jrolinskifdt Jul 6, 2020
c741f52
Merge branch 'feature/offlinesync' into feature/offline
jrolinskifdt Jul 6, 2020
30c2307
Merge branch 'feature/offline' into accessToken
jrolinskifdt Jul 6, 2020
a76e981
Merge pull request #14 from Asasello225/accessToken
kleeb Jul 6, 2020
3424367
Merge branch 'feature/offlinesync' into feature/offline
jrolinskifdt Aug 10, 2020
cb8a7fb
rename downloadListOfRegions method
damian-molinski Aug 10, 2020
81be71e
fix json mapping progress
damian-molinski Aug 10, 2020
5ca3cd5
revert method name changes
damian-molinski Aug 10, 2020
ba07e85
rename getDownloadListOfRegions to getXYZ
damian-molinski Aug 10, 2020
f3b425f
Use default accessToken from main()
damian-molinski Aug 10, 2020
acdf387
Merge pull request #15 from Asasello225/hotfix/pr_fixes
Aug 12, 2020
3d696a2
Corrections from review and merge to master (0.9.0)
Nov 12, 2020
7b61a8a
correcao
Nov 12, 2020
7fb27ee
CORRECTION
Nov 13, 2020
d69105e
Merge branch 'feature/offlinesync' into master
lfofelipe Nov 13, 2020
4a139ee
Merge pull request #16 from lfofelipe/master
Nov 14, 2020
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ app.*.symbols
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!/dev/ci/**/Gemfile.lock
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,41 @@ Support for offline maps is available by *"side loading"* the required map tiles
}
```

## Downloading Offline Regions

An offline region is a defined region of a map that is available for use in conditions with limited or no network connection. Tiles for selected region, style and precision are downloaded from Mapbox using proper SDK methods and stored in application's cache.

* Beware of selecting big regions, as size might be significant. Here is an online estimator https://docs.mapbox.com/playground/offline-estimator/.

* Call `downloadOfflineRegionStream` with predefined `OfflineRegion` and optionally track progress in the callback function.

```
final Function(DownloadRegionStatus event) onEvent = (DownloadRegionStatus status) {
if (status.runtimeType == Success) {
// ...
} else if (status.runtimeType == InProgress) {
int progress = (status as InProgress).progress.round();
// ...
} else if (status.runtimeType == Error) {
// ...
}
};

final OfflineRegion offlineRegion = OfflineRegion(
bounds: LatLngBounds(
northeast: LatLng(52.5050648, 13.3915634),
southwest: LatLng(52.4943073, 13.4055383),
),
id: 1,
minZoom: 6,
maxZoom: 18,
mapStyleUrl: 'mapbox://styles/mapbox/streets-v11',
);

downloadOfflineRegionStream(offlineRegion, onEvent);
```


## Location features
To enable location features in an **Android** application:

Expand Down
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.5.0'
}
}

Expand Down Expand Up @@ -40,6 +40,7 @@ android {
implementation "com.mapbox.mapboxsdk:mapbox-android-sdk:9.2.0"
implementation "com.mapbox.mapboxsdk:mapbox-android-plugin-annotation-v9:0.9.0"
implementation "com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v9:0.12.0"
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-offline-v9:0.7.0'
}
compileOptions {
sourceCompatibility 1.8
Expand Down
29 changes: 26 additions & 3 deletions android/src/main/java/com/mapbox/mapboxgl/GlobalMethodHandler.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package com.mapbox.mapboxgl;

import android.content.Context;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mapbox.mapboxgl.models.OfflineRegionData;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
Expand All @@ -36,6 +37,20 @@ public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
installOfflineMapTiles(tilesDb);
result.success(null);
break;
case "downloadOfflineRegion":
//Get download region arguments from caller
Gson gson = new Gson();
OfflineRegionData args = gson.fromJson(methodCall.arguments.toString(), OfflineRegionData.class);

//Start downloading
OfflineManagerUtils.downloadRegion(args, result, registrar, gson.fromJson(methodCall.arguments.toString(), JsonObject.class).get("accessToken").getAsString());
break;
case "getListOfRegions":
OfflineManagerUtils.regionsList(result, registrar.context(), new Gson().fromJson(methodCall.arguments.toString(), JsonObject.class).get("accessToken").getAsString());
break;
case "deleteOfflineRegion":
OfflineManagerUtils.deleteRegion(result, registrar.context(), (int) methodCall.argument("id"), new Gson().fromJson(methodCall.arguments.toString(), JsonObject.class).get("accessToken").getAsString());
break;
default:
result.notImplemented();
break;
Expand All @@ -61,6 +76,14 @@ private InputStream openTilesDbFile(String tilesDb) throws IOException {
}
}

private String extractAccessToken(MethodCall methodCall, String fallbackValue) {
if (methodCall.hasArgument("accessToken")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kleeb I'm getting a ClassCastException here every time I try to download an offline region. The reason seems to be that flutter's methods like methodCall.hasArgument() only work if you pass a flutter Map or JSONObject in flutter. Because you pass an already json-encoded String in global.dart:50 this fails. I got it to work by replacing all calls to extractAccessToken() with
gson.fromJson(methodCall.arguments.toString(), JsonObject.class).get("accessToken").getAsString()
or new Gson().fromJson(methodCall.arguments.toString(), JsonObject.class).get("accessToken").getAsString()

After that downloading definitely works. After I delete a region I can still view it offline but I guess that is just because of caching.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you look into patching this @kleeb ?

return methodCall.argument("accessToken");
}

return fallbackValue;
}

private static int copy(InputStream input, OutputStream output) throws IOException {
final byte[] buffer = new byte[BUFFER_SIZE];
final BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
Expand Down
37 changes: 37 additions & 0 deletions android/src/main/java/com/mapbox/mapboxgl/MapBoxUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.mapbox.mapboxgl;

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

import androidx.annotation.NonNull;

import com.mapbox.mapboxsdk.Mapbox;

abstract class MapBoxUtils {
private static final String TAG = "MapboxMapController";

static Mapbox getMapbox(Context context, String accessToken) {
return Mapbox.getInstance(context, accessToken == null ? getAccessToken(context) : accessToken);
}

private static String getAccessToken(@NonNull Context context) {
try {
ApplicationInfo ai = context.getPackageManager()
.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String token = bundle.getString("com.mapbox.token");
if (token == null || token.isEmpty()) {
throw new NullPointerException();
}
return token;
} catch (Exception e) {
Log.e(TAG, "Failed to find an Access Token in the Application meta-data. Maps may not load correctly. " +
"Please refer to the installation guide at https://github.com/tobrun/flutter-mapbox-gl#mapbox-access-token " +
"for troubleshooting advice." + e.getMessage());
}
return null;
}
}
22 changes: 2 additions & 20 deletions android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
Expand Down Expand Up @@ -150,7 +149,7 @@ final class MapboxMapController
MapboxMapOptions options,
String accessToken,
String styleStringInitial) {
Mapbox.getInstance(context, accessToken!=null ? accessToken : getAccessToken(context));
MapBoxUtils.getMapbox(context, accessToken);
this.id = id;
this.context = context;
this.activityState = activityState;
Expand All @@ -168,23 +167,6 @@ final class MapboxMapController
this.registrarActivityHashCode = registrar.activity().hashCode();
}

private static String getAccessToken(@NonNull Context context) {
try {
ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = ai.metaData;
String token = bundle.getString("com.mapbox.token");
if (token == null || token.isEmpty()) {
throw new NullPointerException();
}
return token;
} catch (Exception e) {
Log.e(TAG, "Failed to find an Access Token in the Application meta-data. Maps may not load correctly. " +
"Please refer to the installation guide at https://github.com/tobrun/flutter-mapbox-gl#mapbox-access-token " +
"for troubleshooting advice." + e.getMessage());
}
return null;
}

@Override
public View getView() {
return mapView;
Expand Down Expand Up @@ -1351,4 +1333,4 @@ public void onFinish() {
public void onCancel() {
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mapbox.mapboxgl;

import androidx.annotation.Nullable;

import com.google.gson.Gson;

import java.util.HashMap;
import java.util.Map;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;

public class OfflineChannelHandlerImpl implements EventChannel.StreamHandler {
private EventChannel.EventSink sink;
private Gson gson = new Gson();

OfflineChannelHandlerImpl(BinaryMessenger messenger, String channelName) {
EventChannel eventChannel = new EventChannel(messenger, channelName);
eventChannel.setStreamHandler(this);
}

@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
sink = events;
}

@Override
public void onCancel(Object arguments) {
sink = null;
}

void onError(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
if (sink == null) return;
sink.error(errorCode, errorMessage, errorDetails);
}

void onSuccess() {
if (sink == null) return;
Map<String, Object> body = new HashMap<>();
body.put("status", "success");
sink.success(gson.toJson(body));
}

void onStart() {
if (sink == null) return;
Map<String, Object> body = new HashMap<>();
body.put("status", "start");
sink.success(gson.toJson(body));
}

void onProgress(double progress) {
if (sink == null) return;
Map<String, Object> body = new HashMap<>();
body.put("status", "progress");
body.put("progress", progress);
sink.success(gson.toJson(body));
}
}
Loading