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

Does the property setUseDataConnection(false) work like intented? #1691

Open
WRPSoft opened this issue Feb 5, 2021 · 9 comments
Open

Does the property setUseDataConnection(false) work like intented? #1691

WRPSoft opened this issue Feb 5, 2021 · 9 comments

Comments

@WRPSoft
Copy link

WRPSoft commented Feb 5, 2021

Hello OSMDroid community,

First of all: OSMDroid is an awesome library. Thanks so much for sharing this treasure box to the community!

Now a first question I couldn't get answer myself although I've read the docs.:

Does the property setUseDataConnection(false) work like intented?
I would have thought that deactivating this property would result in no more tiles being downloaded online.
But regardless of whether I assign this property with false or true, it looks like tiles are still being downloaded over the network. I would like to implement a real offline mode for on the go, which means that only cached tiles are displayed or the map view remains empty if there aren't any tiles cached.

I'm a Windows developer, just making my first steps into Android. Therefore debugging the OSMDroid code is still a bit cumbersome for me at the moment. But I hopefully will get better :)

I'm using OSMDroid for a small and really lightweight free GPS viewer (TCX/GPX/KMZ imports) if this does matter...

Would be nice if one could explain how setUseDataConnection(false) is intented to work.

Regards Ralph

Issue Type

[X] Question
[ ] Bug
[ ] Improvement
[ ] Build system related
[ ] Performance
[ ] Documentation

Description and/or steps/code to reproduce the problem

Just setting

map.setUseDataConnection(false);

It seems that tiles will still be downloaded, maybe a little but slower compared to map.setUseDataConnection(true);

Environment

If it's a bug, version(s) of android this affects:

All

Version of osmdroid the issue relates to:

6.1.10

@monsieurtanuki
Copy link
Collaborator

Hi @WRPSoft, I'm glad you enjoy osmdroid!

My remarks:

  • if I were you I would not read the docs, but rather read the code, and more specifically the code of the sample app
  • as you mention that you've just started Android development, I strongly advise against it. My suggestion: try flutter instead.
  • if you still persist, regarding your issue, from what I read quickly in the code I don't think using setUseDataConnection(boolean) can help you. In osmdroid, basically either we use fixed data (like a zip in a subfolder), or downloadable data that we cache. I think your choice is rather fixed data, am I right?

@WRPSoft
Copy link
Author

WRPSoft commented Feb 5, 2021

Hi @monsieurtanuki

Thanks for your feedback.

  • I've first thought that setUseDataConnection(false) will act like a kind of internal emergency switch :), like the Android plane mode for example.

This could have been a good option for on the go, e.g. if you want to prevent mobile data usage on-the-fly within the OSMDroid library, but cannot disable mobile data connection for the phone. But if it doesn't work that way, that's the way to go :) Using fixed data is of course an option.

  • The sample app which I looked at and which I took a lot from, in my opinion is a bit ambiguous regarding this topic, because there is an online/offline switch whose operation mode cannot be seen in the demo, but you have to go deep to the MapTileDownloader through debugging to see what's going on in detail.

By the way going to use flutter is not an option, cause there is this old developer wisdom: If you never dare to jump into the deep and cold water, then you will probably not be able to achieve things you want to master :)

So doing it the hard way and learning Android Java is okay; programming is kind of my daily business, but at the moment I'm still struggling against Google's 'deprecated' orgy (just joking :) ) and have to get used with the GUI and workspace of Android Studio (like most of us when making their first steps in the Android world). So the Android debugging is still a little be hard for me. But that should be doable with the time :)

Again, thanks for your feedback and thanks for the good work!

@monsieurtanuki
Copy link
Collaborator

@WRPSoft The SQLite tile cache is there to prevent useless mobile data usage, and it's automatic. If you already have the tile in the database and if it's not expired (it will depend on the tile provider: one week old?), there won't be internet access.
I think you can override the tile expiration timestamp and make it much longer; that could be an option for you, couldn't it?

If your goal is to learn Android dev, learn Android dev.
If your goal is to build an Android app, learn flutter instead. There's no reason to do it "the hard way" - OK, I won't mention that again with you :)

@WRPSoft
Copy link
Author

WRPSoft commented Feb 6, 2021

Hi @monsieurtanuki,

Developer are kind of big kids: we will again and again put our hands on the hot stove (= learning things the hard way :) (

I'm aware how the tile (caching) system is working.
I'm a cyclist and even if mobility is still heavily regulated because of this Covid crap, in theory it happens that you want to reduce mobile data usage to the minimum, cause you are 'crossing' many map tiles while cycling on the road.

Downloading an offline map beforehand is of course the most smart thing to do, but sometimes the data that has already been cached is sufficient.

Anyway, I have now overwritten the MapTileProviderBase and MapTileDownloader class and take into account the UsesDataConnection property, which has been always set to true in the super class.

Perhaps this is not exactly what OSMDroid is designed to work, but it helps me in my special use case and it seems to work (at least yesterday's sleepless night while I did this approach :) (.

Again, the OSMDroid Library is a real treasure chest.

Thank you all for this great library!

ChartMaker_Offline

@monsieurtanuki
Copy link
Collaborator

If I sum up:

  • your initial need was to let the end-user disable the download of downloadable tiles in some use-cases, typically when the connection is poor - a bit like switching off the internet access, but not on your whole phone, just on the maps
  • setUseDataConnection(boolean) had a good name, but didn't work exactly as expected
  • you managed to code something to fit your needs

It would make sense to share this code with us, because you're not the only one with that need I guess.

I don't know how you did that, but I guess I would have done it that way:

  • I would have let setUseDataConnection(boolean) as it is, because I think it has a purpose, but not the one you expected, and some developers use it as it works currently
  • in MapTileDownloader, handle the case where mTileDownloader is null in TileLoader.downloadTile
  • in MapTileProviderBasic, create a new method similar to setOfflineFirst, that would switch to an explicit "downgraded mode" and would set the tile downloader to null. Maybe setTileDownloader(TileDownloader)?

@WRPSoft What do you think of that?

@WRPSoft
Copy link
Author

WRPSoft commented Feb 6, 2021

Hi @monsieurtanuki,

I will be happy to post my small adjustments.

Now it's weekend and I must do some 'work' with my family away from the PC :), but I will be back tomorrow and post the adjustments.

If it is a suggestion to others, I would be happy. I'm sure one can improve that because I'm done the changes at a late night session when I was looking for a possibe 'interface' for this specific case.

PS: The main reason for this special use case is the fact that I'm using a prepaid tariff on my mobile phone and the mobile data usage is therefore somewhat limited. Probably not the common use case, but a kind of stopp switch is sometimes not that bad :)

@WRPSoft
Copy link
Author

WRPSoft commented Feb 7, 2021

Hi @monsieurtanuki and OSDMDroid community

Here is what I did to bring a new 'blocking switcher' into the game.

By the way, this graddle thing is like a book with seven seals to me, so I've just looking in a quick way for an 'interface' to interfere some special handling without changing too much. Therefore at least I had to modify two classes (please take a look for comments called 'RW:' within the two classes at the attached zip file).

  1. MapTileDownloader class needs to be modified, cause here a new boolean will be introduced (I called it MyMapTileDownloader)

  2. MapTileProviderBasic class must be modified too due to embedding the newly MapTileDownloader class (MyMapTileProviderBasic).

I implemented a new static (!) boolean for blocking the download.
@monsieurtanuki: as you recommended I didn't touch setUseDataConnection(boolean) (I changed this and added a new boolean instead).

Basically it's just this if clause at the MapTileDownloader class that that is doing the trick:

@OverRide
public Drawable loadTile (final long pMapTileIndex) throws CantContinueException {

  OnlineTileSourceBase tileSource = mTileSource.get ();
  if (tileSource == null) {
     return null;
  }

 // RW: 02/05/2021 this attribute is a kind of (global) stop switch, therefore avoid (block) the download of a tile.
 // keep in mind: blockDownload is declared as static!
 if (getBlockDownload ()) {
    if (Configuration.getInstance (). isDebugMode ()) {
        Log.d (IMapView.LOGTAG, "Skipping" + getName () + "due to BlockDownload.");
    }
    return null;
 }

if (mNetworkAvailablityCheck! = null
    &&! mNetworkAvailablityCheck.getNetworkAvailable ()) {
        if (Configuration.getInstance (). isDebugMode ()) {
            Log.d (IMapView.LOGTAG, "Skipping" + getName () + "due to NetworkAvailabliltyCheck.");
        }
        return null;
}

As I've seen OSMDroid is able to build a tile request chain -> I've never checked for any side effects, maybe there could be any unwanted side effects!
On the other side, this 'blocking-switcher' is a kind of global emergency brake, so in my eyes using a static field should be fine(?). But please see my last comment at the end of this post.

Now our activity is taking the role:
(The code will probably look familiar to you :) )

 private void iniMap() {

// NOTE: doing the configuration stuff in the onCreate event as suggested
// Configuration.getInstance().setUserAgentValue(BuildConfig.APPLICATION_ID);
// //load/initialize the osmdroid configuration, this can be done
// Context ctx = getActivity().getApplicationContext();
// Configuration.getInstance().load(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));

        //setting this before the layout is inflated is a good idea
        //it 'should' ensure that the map has a writable location for the map cache, even without permissions
        //if no tiles are displayed, you can try overriding the cache path using Configuration.getInstance().setCachePath
        //see also StorageUtils
        //note, the load method also sets the HTTP User Agent to your application's package name, abusing osm's
        //tile servers will get you banned based on this string

        //inflate and create the map
        map = (MapView) mroot.findViewById(R.id.map);

        //////////////////////////////////////////////////////////////////////  		
        // RW: bringing the new MyMapTileProviderBasic class into the game
        //////////////////////////////////////////////////////////////////////  
	
        map.setTileProvider(new MyMapTileProviderBasic(getContext()));

        //////////////////////////////////////////////////////////////////////  		
        // RW: ...and the new myMapTileDownloader too
        //////////////////////////////////////////////////////////////////////  		

        if (myMapTileDownloader == null)
            // RW: this isn't needed, cause it will only instanced one a time, but it didn't hurt :-)
            // checking for null is always a good deal in the Android java world :-)
            myMapTileDownloader = new MyMapTileDownloader(map.getTileProvider().getTileSource());

        //////////////////////////////////////////////////////////////////////  		
        // RW: ...of course the TileDownloader class is needed as before!
        //////////////////////////////////////////////////////////////////////  		

        myMapTileDownloader.setTileDownloader(new TileDownloader());

        //////////////////////////////////////////////////////////////////////  		
        // RW: and voilà, now you can switch to blocking state
        //////////////////////////////////////////////////////////////////////  		

        myMapTileDownloader.setBlockDownload(sharedData.isOffline());
        // RW: it's probably a good practice to keep a myMapTileDownloader instance for
        // switching this attibute on the fly at any place within the activity class. 
        // -> I'm using a helper method for this task that is setting myMapTileDownloader.setBlockDownload(boolean);
        // helper.setOfflineMode(sharedData.isOffline());


        // RW: 03.02.2021 added some more map provider
        //map.setTileSource(TileSourceFactory.MAPNIK);
        helper.setMapProvider(sharedData.getMapID());

Please note: I've been working in the software development for over 20 years, but I've always been able to avoid going too deep to Java.
Java and especially Android's Java offshoots are relatively new territory for me, so please don't beat me up now if I haven't considered something important and potentially serious side effects may result.

And finally: the way OSMDroid is handling the map tiles is absolutey fine! My use case is very, very specific -> therefore please do not misunderstand this modification. It's for my internal usage, but of course, feel free to use it or better to improve.

PS: If a short sample project is needed I can make that. But I don't know if it's needed and maybe nobody will use this specific approach :).

PPS: Working with OSMDroid is so much fun, again, thanks for this great library!

OSMDroid_MapTileDownloaderEx.zip

@WRPSoft
Copy link
Author

WRPSoft commented Feb 9, 2021

Addendum:

I have now been able to find a side effect. It looks like after my changes the copyright overlay has to be assigned explicitly. So be sure to add this code in the map initialization, otherwise you will no longer see the copyright notice:

String copyrightNotice = map.getTileProvider().getTileSource().getCopyrightNotice();
CopyrightOverlay copyrightOverlay = new CopyrightOverlay(context);
copyrightOverlay.setCopyrightNotice(copyrightNotice);
copyrightOverlay.setAlignRight(true);
map.getOverlays().add(copyrightOverlay);

Otherwise, I haven't found any problems with my internal test version (but that doesn't mean anything)

@monsieurtanuki
Copy link
Collaborator

Hello @WRPSoft! My comments:

  • if you change the code, it's supposed to have no impact on the already existing apps. The example of copyright overlay is not a good sign :(
  • I cannot read your code as zip files - it would be easier to understand your changes if you created a Pull Request, where the differences will be displayed in an obvious manner
  • I don't recommend static fields in Android Java, because they have a tendency to disappear (e.g. if memory is needed on the smartphone)
  • beyond that, there could be a flaw (that was also there in my suggestion in a previous comment) - could you please check that the same tile is not being asked over and over again (like: no network => immediate answer => immediately trying again)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants