implement batch tile downloader for android client

git-svn-id: https://osmand.googlecode.com/svn/trunk@762 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-12-05 13:12:50 +00:00
parent 6605875694
commit b180b6bd70
12 changed files with 294 additions and 11 deletions

View file

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.osmand" android:versionName="0.5.1" android:versionCode="22" android:installLocation="auto"> package="net.osmand" android:versionName="0.5.1" android:versionCode="22" android:installLocation="auto">
<application android:icon="@drawable/icon" android:label="@string/app_name" <application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="false" android:name=".activities.OsmandApplication" android:description="@string/app_description"> android:debuggable="true" android:name=".activities.OsmandApplication" android:description="@string/app_description">
<activity android:name=".activities.MainMenuActivity" <activity android:name=".activities.MainMenuActivity"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>

View file

@ -8,7 +8,7 @@
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">
<LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" > <LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" >
<LinearLayout android:layout_height="wrap_content" android:layout_width="wrap_content" <LinearLayout android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="@drawable/headliner" android:orientation="horizontal" > android:background="@drawable/headliner" android:orientation="horizontal" android:id="@+id/Headliner" >
<ImageView android:src="@drawable/headline_osmand_icon" <ImageView android:src="@drawable/headline_osmand_icon"
android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginLeft="5dp"/> android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginLeft="5dp"/>
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content" <TextView android:layout_height="wrap_content" android:layout_width="wrap_content"

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<TextView android:text="@string/select_max_zoom_preload_area" android:gravity="center"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp"/>
<SeekBar android:id="@+id/ZoomToDownload" android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp"/>
<LinearLayout
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp">
<TextView android:text="10" android:gravity="left" android:layout_weight="0.5" android:id="@+id/MinZoom"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView android:text="15" android:gravity="right" android:layout_weight="0.5" android:id="@+id/MaxZoom"
android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>
<TextView android:text="@string/tiles_to_download_estimated_size" android:gravity="center" android:id="@+id/DownloadDescription"
android:layout_width="fill_parent" android:layout_height="wrap_content"/>
<ImageView android:layout_width="fill_parent" android:layout_height="fill_parent"
android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/AreaPreview"/>
</LinearLayout>

View file

@ -8,7 +8,7 @@
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">
<LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" > <LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:gravity="center_horizontal" >
<LinearLayout android:layout_height="wrap_content" android:layout_width="wrap_content" <LinearLayout android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="@drawable/headliner" android:orientation="horizontal" > android:background="@drawable/headliner" android:orientation="horizontal" android:id="@+id/Headliner" >
<ImageView android:src="@drawable/headline_osmand_icon" <ImageView android:src="@drawable/headline_osmand_icon"
android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginLeft="5dp"/> android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_marginLeft="5dp"/>
<TextView android:layout_height="wrap_content" android:layout_width="wrap_content" <TextView android:layout_height="wrap_content" android:layout_width="wrap_content"

View file

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="tiles_to_download_estimated_size">На масштабе {0} загрузить {1} тайлов ({2} Mb )</string>
<string name="context_menu_item_download_map">Загрузить карту</string>
<string name="select_max_zoom_preload_area">Выберите максимальный масштаб для загрузки видимой области</string>
<string name="maps_could_not_be_downloaded">Выбранная карта не может быть загружена</string>
<string name="continuous_rendering">Быстрый рендеринг</string> <string name="continuous_rendering">Быстрый рендеринг</string>
<string name="continuous_rendering_descr">Выберите способ как отрисовывать карту</string> <string name="continuous_rendering_descr">Выберите способ как отрисовывать карту</string>
<string name="rendering_exception">Во время отображения карты произошла непредвиденная ошибка</string> <string name="rendering_exception">Во время отображения карты произошла непредвиденная ошибка</string>

View file

@ -1,5 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<resources> <resources>
<string name="tiles_to_download_estimated_size">At zoom {0} download {1} tiles ({2} Mb )</string>
<string name="context_menu_item_download_map">Download map</string>
<string name="select_max_zoom_preload_area">Select max zoom to preload visible area</string>
<string name="maps_could_not_be_downloaded">This map could not be downloaded</string>
<string name="continuous_rendering">Continuous rendering</string> <string name="continuous_rendering">Continuous rendering</string>
<string name="continuous_rendering_descr">Choose show continuous rendering or whole image</string> <string name="continuous_rendering_descr">Choose show continuous rendering or whole image</string>
<string name="rendering_exception">Error occurred while rendering selected area</string> <string name="rendering_exception">Error occurred while rendering selected area</string>

View file

@ -656,6 +656,10 @@ public class ResourceManager {
closeTransport(); closeTransport();
} }
public synchronized void reloadTilesFromFS(){
imagesOnFS.clear();
}
/// On low memory method /// /// On low memory method ///
public void onLowMemory() { public void onLowMemory() {
log.info("On low memory : cleaning tiles - size = " + cacheOfImages.size()); //$NON-NLS-1$ log.info("On low memory : cleaning tiles - size = " + cacheOfImages.size()); //$NON-NLS-1$

View file

@ -96,7 +96,7 @@ public class TransportIndexRepositoryBinary implements TransportIndexRepository
for(int r : stop.getReferencesToRoutes()) { for(int r : stop.getReferencesToRoutes()) {
try { try {
TransportRoute route = file.getTransportRoute(r); TransportRoute route = file.getTransportRoute(r);
res.add(f.format(new String[] { route.getRef(), route.getType(), route.getName(), route.getEnName()})); res.add(f.format(new String[] { route.getRef()+"", route.getType()+"", route.getName()+"", route.getEnName()+""})); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
} catch (IOException e) { } catch (IOException e) {
log.error("Disk error ", e); //$NON-NLS-1$ log.error("Disk error ", e); //$NON-NLS-1$
} }

View file

@ -629,6 +629,7 @@ public class DownloadIndexActivity extends ListActivity {
if (lowerCase.contains("map")) { //$NON-NLS-1$ if (lowerCase.contains("map")) { //$NON-NLS-1$
if (!f) { if (!f) {
s += ", "; //$NON-NLS-1$ s += ", "; //$NON-NLS-1$
} else {
f = false; f = false;
} }
s += getString(R.string.map_index); s += getString(R.string.map_index);
@ -636,6 +637,7 @@ public class DownloadIndexActivity extends ListActivity {
if (lowerCase.contains("transport")) { //$NON-NLS-1$ if (lowerCase.contains("transport")) { //$NON-NLS-1$
if (!f) { if (!f) {
s += ", "; //$NON-NLS-1$ s += ", "; //$NON-NLS-1$
} else {
f = false; f = false;
} }
s += getString(R.string.transport); s += getString(R.string.transport);
@ -643,6 +645,7 @@ public class DownloadIndexActivity extends ListActivity {
if (lowerCase.contains("address")) { //$NON-NLS-1$ if (lowerCase.contains("address")) { //$NON-NLS-1$
if (!f) { if (!f) {
s += ", "; //$NON-NLS-1$ s += ", "; //$NON-NLS-1$
} else {
f = false; f = false;
} }
s += getString(R.string.address); s += getString(R.string.address);

View file

@ -0,0 +1,235 @@
package net.osmand.activities;
import java.text.MessageFormat;
import java.util.ArrayList;
import net.osmand.LogUtil;
import net.osmand.R;
import net.osmand.ResourceManager;
import net.osmand.data.preparation.MapTileDownloader;
import net.osmand.data.preparation.MapTileDownloader.DownloadRequest;
import net.osmand.data.preparation.MapTileDownloader.IMapDownloaderCallback;
import net.osmand.map.ITileSource;
import net.osmand.osm.MapUtils;
import net.osmand.views.OsmandMapTileView;
import org.apache.commons.logging.Log;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
public class DownloadTilesDialog {
private final static Log log = LogUtil.getLog(DownloadTilesDialog.class);
private final Context ctx;
private final OsmandApplication app;
private final OsmandMapTileView mapView;
public DownloadTilesDialog(Context ctx, OsmandApplication app, OsmandMapTileView mapView){
this.ctx = ctx;
this.app = app;
this.mapView = mapView;
}
public void openDialog(){
final ITileSource mapSource = mapView.getMap();
if(mapSource == null || !mapSource.couldBeDownloadedFromInternet()){
Toast.makeText(ctx, R.string.maps_could_not_be_downloaded, Toast.LENGTH_SHORT).show();
return;
}
final int max = mapSource.getMaximumZoomSupported();
final int zoom = mapView.getZoom();
// calculate pixel rectangle
Rect boundsRect = new Rect(0, 0, mapView.getWidth(), mapView.getHeight());
float tileX = (float) MapUtils.getTileNumberX(zoom, mapView.getLongitude());
float tileY = (float) MapUtils.getTileNumberY(zoom, mapView.getLatitude());
float w = mapView.getCenterPointX();
float h = mapView.getCenterPointY();
RectF tilesRect = new RectF();
final RectF latlonRect = new RectF();
mapView.calculateTileRectangle(boundsRect, w, h, tileX, tileY, tilesRect);
latlonRect.top = (float) MapUtils.getLatitudeFromTile(zoom, tilesRect.top);
latlonRect.left = (float) MapUtils.getLongitudeFromTile(zoom, tilesRect.left);
latlonRect.bottom = (float) MapUtils.getLatitudeFromTile(zoom, tilesRect.bottom);
latlonRect.right = (float) MapUtils.getLongitudeFromTile(zoom, tilesRect.right);
Builder builder = new AlertDialog.Builder(ctx);
LayoutInflater inflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.download_tiles, null);
((TextView)view.findViewById(R.id.MinZoom)).setText(zoom+""); //$NON-NLS-1$
((TextView)view.findViewById(R.id.MaxZoom)).setText(max+""); //$NON-NLS-1$
final SeekBar seekBar = (SeekBar) view.findViewById(R.id.ZoomToDownload);
seekBar.setMax(max - zoom);
seekBar.setProgress((max - zoom) / 2);
final TextView downloadText = ((TextView) view.findViewById(R.id.DownloadDescription));
final String template = ctx.getString(R.string.tiles_to_download_estimated_size);
updateLabel(zoom, latlonRect, downloadText, template, seekBar.getProgress());
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
updateLabel(zoom, latlonRect, downloadText, template, progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
builder.setPositiveButton(R.string.download_files, new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
run(zoom, seekBar.getProgress(), latlonRect, mapSource);
}
});
builder.setNegativeButton(R.string.default_buttons_cancel, null);
builder.setView(view);
builder.show();
}
private volatile boolean cancel = false;
public void run(final int zoom, final int progress, final RectF latlonRect, final ITileSource map){
cancel = false;
int numberTiles = 0;
for (int z = zoom; z <= progress + zoom; z++) {
int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left);
int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right);
int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top);
int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom);
numberTiles += (x2 - x1 + 1) * (y2 - y1 + 1);
}
final ProgressDialog progressDlg = new ProgressDialog(ctx);
progressDlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDlg.setMessage(ctx.getString(R.string.downloading));
progressDlg.setCancelable(true);
progressDlg.setMax(numberTiles);
progressDlg.setOnCancelListener(new DialogInterface.OnCancelListener(){
@Override
public void onCancel(DialogInterface dialog) {
cancel = true;
}
});
final MapTileDownloader instance = MapTileDownloader.getInstance();
final ArrayList<IMapDownloaderCallback> previousCallbacks =
new ArrayList<IMapDownloaderCallback>(instance.getDownloaderCallbacks());
instance.getDownloaderCallbacks().clear();
instance.addDownloaderCallback(new IMapDownloaderCallback(){
@Override
public void tileDownloaded(DownloadRequest request) {
progressDlg.setProgress(progressDlg.getProgress() + 1);
}
});
Runnable r = new Runnable(){
@Override
public void run() {
int requests = 0;
int limitRequests = 50;
try {
ResourceManager rm = app.getResourceManager();
for (int z = zoom; z <= zoom + progress && !cancel; z++) {
int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left);
int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right);
int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top);
int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom);
for (int x = x1; x <= x2 && !cancel; x++) {
for (int y = y1; y <= y2 && !cancel; y++) {
String tileId = rm.calculateTileId(map, x, y, z);
if (rm.tileExistOnFileSystem(tileId, map, x, y, z)) {
progressDlg.setProgress(progressDlg.getProgress() + 1);
} else {
rm.getTileImageForMapSync(tileId, map, x, y, z, true);
requests++;
}
if (!cancel) {
if (requests >= limitRequests) {
requests = 0;
while (instance.isSomethingBeingDownloaded()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new IllegalArgumentException(e);
}
}
}
}
}
}
}
if(cancel){
instance.refuseAllPreviousRequests();
} else {
while (instance.isSomethingBeingDownloaded()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new IllegalArgumentException(e);
}
}
}
mapView.refreshMap();
} catch (Exception e) {
log.error("Exception while downloading tiles ", e); //$NON-NLS-1$
instance.refuseAllPreviousRequests();
} finally {
instance.getDownloaderCallbacks().clear();
instance.getDownloaderCallbacks().addAll(previousCallbacks);
app.getResourceManager().reloadTilesFromFS();
}
progressDlg.dismiss();
}
};
new Thread(r, "Downloading tiles").start(); //$NON-NLS-1$
progressDlg.show();
}
private void updateLabel(final int zoom, final RectF latlonRect, final TextView downloadText, final String template, int progress) {
int numberTiles = 0;
for (int z = zoom; z <= progress + zoom; z++) {
int x1 = (int) MapUtils.getTileNumberX(z, latlonRect.left);
int x2 = (int) MapUtils.getTileNumberX(z, latlonRect.right);
int y1 = (int) MapUtils.getTileNumberY(z, latlonRect.top);
int y2 = (int) MapUtils.getTileNumberY(z, latlonRect.bottom);
numberTiles += (x2 - x1 + 1) * (y2 - y1 + 1);
}
downloadText.setText(MessageFormat.format(template, (progress + zoom)+"", //$NON-NLS-1$
numberTiles, (double)numberTiles*12/1000));
}
}

View file

@ -88,9 +88,9 @@ public class MainMenuActivity extends Activity {
} }
} }
public Animation getAnimation(boolean left){ public Animation getAnimation(int left, int top){
Animation anim = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_SELF, left ? -1 : 1, Animation anim = new TranslateAnimation(TranslateAnimation.RELATIVE_TO_SELF, left,
TranslateAnimation.RELATIVE_TO_SELF, 0, TranslateAnimation.RELATIVE_TO_SELF, 0, TranslateAnimation.RELATIVE_TO_SELF, 0); TranslateAnimation.RELATIVE_TO_SELF, 0, TranslateAnimation.RELATIVE_TO_SELF, top, TranslateAnimation.RELATIVE_TO_SELF, 0);
anim.setDuration(700); anim.setDuration(700);
anim.setInterpolator(new AccelerateInterpolator()); anim.setInterpolator(new AccelerateInterpolator());
return anim; return anim;
@ -102,16 +102,18 @@ public class MainMenuActivity extends Activity {
requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.menu); setContentView(R.layout.menu);
View head = (View) findViewById(R.id.Headliner);
head.startAnimation(getAnimation(0, -1));
View leftview = (View) findViewById(R.id.MapButton); View leftview = (View) findViewById(R.id.MapButton);
leftview.startAnimation(getAnimation(true)); leftview.startAnimation(getAnimation(-1, 0));
leftview = (View) findViewById(R.id.FavoritesButton); leftview = (View) findViewById(R.id.FavoritesButton);
leftview.startAnimation(getAnimation(true)); leftview.startAnimation(getAnimation(-1, 0));
View rightview = (View) findViewById(R.id.SettingsButton); View rightview = (View) findViewById(R.id.SettingsButton);
rightview.startAnimation(getAnimation(false)); rightview.startAnimation(getAnimation(1, 0));
rightview = (View) findViewById(R.id.SearchButton); rightview = (View) findViewById(R.id.SearchButton);
rightview.startAnimation(getAnimation(false)); rightview.startAnimation(getAnimation(1, 0));
final TextView textView = (TextView) findViewById(R.id.TextVersion); final TextView textView = (TextView) findViewById(R.id.TextVersion);
textView.setText(Version.APP_VERSION+ " "+ Version.APP_DESCRIPTION); //$NON-NLS-1$ textView.setText(Version.APP_VERSION+ " "+ Version.APP_DESCRIPTION); //$NON-NLS-1$

View file

@ -1554,6 +1554,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
actions.add(resources.getString(R.string.context_menu_item_create_poi)); actions.add(resources.getString(R.string.context_menu_item_create_poi));
actions.add(resources.getString(R.string.context_menu_item_open_bug)); actions.add(resources.getString(R.string.context_menu_item_open_bug));
actions.add(resources.getString(R.string.context_menu_item_update_map)); actions.add(resources.getString(R.string.context_menu_item_update_map));
actions.add(resources.getString(R.string.context_menu_item_download_map));
builder.setItems(actions.toArray(new String[actions.size()]), new DialogInterface.OnClickListener(){ builder.setItems(actions.toArray(new String[actions.size()]), new DialogInterface.OnClickListener(){
@Override @Override
@ -1587,6 +1588,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
osmBugsLayer.openBug(MapActivity.this, getLayoutInflater(), mapView, latitude, longitude); osmBugsLayer.openBug(MapActivity.this, getLayoutInflater(), mapView, latitude, longitude);
} else if(which == 7){ } else if(which == 7){
reloadTile(mapView.getZoom(), latitude, longitude); reloadTile(mapView.getZoom(), latitude, longitude);
} else if(which == 8){
DownloadTilesDialog dlg = new DownloadTilesDialog(MapActivity.this,
(OsmandApplication) getApplication(), mapView);
dlg.openDialog();
} }
} }
}); });