diff --git a/brouter-routing-app/build.gradle b/brouter-routing-app/build.gradle
index 677ed19..5ec912e 100644
--- a/brouter-routing-app/build.gradle
+++ b/brouter-routing-app/build.gradle
@@ -71,7 +71,7 @@ android {
dependencies {
- implementation 'androidx.appcompat:appcompat:1.3.0'
+ implementation 'androidx.appcompat:appcompat:1.3.1'
implementation project(':brouter-mapaccess')
implementation project(':brouter-core')
diff --git a/brouter-routing-app/src/main/AndroidManifest.xml b/brouter-routing-app/src/main/AndroidManifest.xml
index f5d4713..90eee53 100644
--- a/brouter-routing-app/src/main/AndroidManifest.xml
+++ b/brouter-routing-app/src/main/AndroidManifest.xml
@@ -8,14 +8,17 @@
+
+ android:allowBackup="false">
+ android:exported="true"
+ android:screenOrientation="unspecified" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
@@ -24,6 +27,8 @@
+
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/assets/segments4.zip b/brouter-routing-app/src/main/assets/segments4.zip
index dd18139..c7671fd 100644
Binary files a/brouter-routing-app/src/main/assets/segments4.zip and b/brouter-routing-app/src/main/assets/segments4.zip differ
diff --git a/brouter-routing-app/src/main/assets/serverconfig.txt b/brouter-routing-app/src/main/assets/serverconfig.txt
new file mode 100644
index 0000000..ff9eeb7
--- /dev/null
+++ b/brouter-routing-app/src/main/assets/serverconfig.txt
@@ -0,0 +1,16 @@
+#
+# data download parameter
+#
+# Keep in mind, when profiles downloaded they overwrite old files
+# So when modifying an original profile make a copy from it
+#
+
+segment_url=https://brouter.de/brouter/segments4/
+lookup_url=https://brouter.de/brouter/segments4/
+profiles_url=https://brouter.de/brouter/segments4/
+
+
+# these are comma separated arrays
+
+check_lookup=lookups.dat
+check_profiles=car-eco.brf,car-fast.brf,dummy.brf,fastbike.brf,fastbike-asia-pacific.brf,fastbike-lowtraffic.brf,moped.brf,shortest.brf,trekking.brf,vm-forum-liegerad-schnell.brf,vm-forum-velomobil-schnell.brf
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/ic_launcher-playstore.png b/brouter-routing-app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..5c716a9
Binary files /dev/null and b/brouter-routing-app/src/main/ic_launcher-playstore.png differ
diff --git a/brouter-routing-app/src/main/icon_brouter.svg b/brouter-routing-app/src/main/icon_brouter.svg
new file mode 100644
index 0000000..1f9f5dd
--- /dev/null
+++ b/brouter-routing-app/src/main/icon_brouter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
index 52c2c98..8a1c93f 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
@@ -6,20 +6,42 @@ import java.util.Set;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.speech.tts.TextToSpeech.OnInitListener;
+import android.util.Log;
public class BInstallerActivity extends Activity implements OnInitListener {
+ public static final String DOWNLOAD_ACTION = "btools.routingapp.download";
+
private static final int DIALOG_CONFIRM_DELETE_ID = 1;
private BInstallerView mBInstallerView;
private PowerManager mPowerManager;
private WakeLock mWakeLock;
+ private DownloadReceiver myReceiver;
+
+
+ public class DownloadReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.hasExtra("txt")) {
+ String txt = intent.getStringExtra("txt");
+ boolean ready = intent.getBooleanExtra("ready", false);
+ mBInstallerView.setState(txt, ready);
+ }
+ }
+ }
+
/** Called when the activity is first created. */
@Override
@@ -51,6 +73,12 @@ public class BInstallerActivity extends Activity implements OnInitListener {
*/
mWakeLock.acquire();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(DOWNLOAD_ACTION);
+
+ myReceiver = new DownloadReceiver();
+ registerReceiver(myReceiver, filter);
+
// Start the download manager
mBInstallerView.startInstaller();
}
@@ -58,6 +86,18 @@ public class BInstallerActivity extends Activity implements OnInitListener {
@Override
protected void onPause() {
super.onPause();
+
+
+ super.onPause();
+
+ mWakeLock.release();
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (myReceiver != null) unregisterReceiver(myReceiver);
System.exit(0);
}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
index 7c707dd..debd00d 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java
@@ -7,10 +7,12 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Locale;
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -23,6 +25,7 @@ import android.os.PowerManager;
import android.os.StatFs;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
@@ -63,7 +66,7 @@ public class BInstallerView extends View
private File baseDir;
private boolean isDownloading = false;
- private volatile boolean downloadCanceled = false;
+ public static boolean downloadCanceled = false;
private long currentDownloadSize;
private String currentDownloadFile = "";
@@ -78,6 +81,9 @@ public class BInstallerView extends View
Paint pnt_1 = new Paint();
Paint pnt_2 = new Paint();
Paint paint = new Paint();
+
+ Activity mActivity;
+
protected String baseNameForTile( int tileIndex )
{
@@ -133,6 +139,7 @@ public class BInstallerView extends View
int tidx_min = -1;
int min_size = Integer.MAX_VALUE;
+ ArrayList downloadList = new ArrayList<>();
// prepare download list
for( int ix=0; ix<72; ix++ )
{
@@ -142,6 +149,7 @@ public class BInstallerView extends View
if ( ( tileStatus[tidx] & MASK_SELECTED_RD5 ) != 0 )
{
int tilesize = BInstallerSizes.getRd5Size(tidx);
+ downloadList.add(tidx);
if ( tilesize > 0 && tilesize < min_size )
{
tidx_min = tidx;
@@ -150,29 +158,39 @@ public class BInstallerView extends View
}
}
}
- if ( tidx_min != -1 )
- {
- tileStatus[tidx_min] ^= tileStatus[tidx_min] & MASK_SELECTED_RD5;
- startDownload( tidx_min );
+
+ if (downloadList.size()>0) {
+ isDownloading = true;
+ downloadAll(downloadList);
+ for (Integer i : downloadList) {
+ tileStatus[i.intValue()] ^= tileStatus[i.intValue()] & MASK_SELECTED_RD5;
+ }
+ downloadList.clear();
}
}
-
- private void startDownload( int tileIndex )
- {
-
- String namebase = baseNameForTile( tileIndex );
- String baseurl = "http://brouter.de/brouter/segments4/";
- currentDownloadFile = namebase + ".rd5";
- currentDownloadOperation = "Checking";
- String url = baseurl + currentDownloadFile;
- isDownloading = true;
- downloadCanceled = false;
- currentDownloadSize = 0;
- downloadAction = "Connecting... ";
- final DownloadTask downloadTask = new DownloadTask(getContext());
- downloadTask.execute( url );
+
+ private void downloadAll(ArrayList downloadList) {
+ ArrayList urlparts = new ArrayList<>();
+ for (Integer i: downloadList) {
+ urlparts.add(baseNameForTile( i.intValue() ));
+ }
+
+ currentDownloadOperation = "Start download ...";
+ downloadAction = "";
+ downloadCanceled = false;
+ isDownloading = true;
+
+ //final DownloadBackground downloadTask = new DownloadBackground(getContext(), urlparts, baseDir);
+ //downloadTask.execute( );
+ Intent intent = new Intent(mActivity, DownloadService.class);
+ intent.putExtra("dir", baseDir.getAbsolutePath()+"/brouter/");
+ intent.putExtra("urlparts", urlparts);
+ mActivity.startService(intent);
+
+ deleteRawTracks(); // invalidate raw-tracks after data update
}
+
public void downloadDone( boolean success )
{
isDownloading = false;
@@ -184,6 +202,16 @@ public class BInstallerView extends View
invalidate();
}
+ public void setState(String txt, boolean b) {
+ currentDownloadOperation = txt;
+ downloadAction = "";
+ isDownloading = b;
+ if (!b) {
+ scanExistingFiles();
+ }
+ invalidate();
+ }
+
private int tileIndex( float x, float y )
{
int ix = (int)(72.f * x / bmp.getWidth());
@@ -300,6 +328,7 @@ public class BInstallerView extends View
public BInstallerView(Context context) {
super(context);
+ mActivity = (Activity) context;
DisplayMetrics metrics = new DisplayMetrics();
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
@@ -314,10 +343,15 @@ public class BInstallerView extends View
imgw = (int)(imgwOrig / scaleOrig);
imgh = (int)(imghOrig / scaleOrig);
+
+ totalSize = 0;
+ rd5Tiles = 0;
+ delTiles = 0;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w,h,oldw,oldh);
}
private void toast( String msg )
@@ -390,10 +424,11 @@ public class BInstallerView extends View
{
String sizeHint = currentDownloadSize > 0 ? " (" + ((currentDownloadSize + mb-1)/mb) + " MB)" : "";
paint.setTextSize(30);
- canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
+ canvas.drawText( currentDownloadOperation, 30, (imgh/3)*2-30, paint);
+ // canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
canvas.drawText( downloadAction, 30, (imgh/3)*2, paint);
}
- if ( !tilesVisible )
+ if ( !tilesVisible && !isDownloading)
{
paint.setTextSize(35);
canvas.drawText( "Touch region to zoom in!", 30, (imgh/3)*2, paint);
@@ -655,209 +690,7 @@ float tx, ty;
return true;
}
-
- // usually, subclasses of AsyncTask are declared inside the activity class.
- // that way, you can easily modify the UI thread from here
- private class DownloadTask extends AsyncTask implements ProgressListener {
- private Context context;
- private PowerManager.WakeLock mWakeLock;
- public DownloadTask(Context context) {
- this.context = context;
- }
- @Override
- public void updateProgress( String progress )
- {
- newDownloadAction = progress;
- publishProgress( 0 );
- }
-
- @Override
- public boolean isCanceled()
- {
- return isDownloadCanceled();
- }
-
- @Override
- protected String doInBackground(String... sUrls)
- {
- InputStream input = null;
- OutputStream output = null;
- HttpURLConnection connection = null;
- String surl = sUrls[0];
- File fname = null;
- File tmp_file = null;
- try
- {
- try
- {
- int slidx = surl.lastIndexOf( "segments4/" );
- String name = surl.substring( slidx+10 );
- String surlBase = surl.substring( 0, slidx+10 );
- fname = new File (baseDir, "brouter/segments4/" + name);
-
- boolean delta = true;
-
- if ( fname.exists() )
- {
- updateProgress( "Calculating local checksum.." );
-
- // first check for a delta file
-
- String md5 = Rd5DiffManager.getMD5( fname );
- String surlDelta = surlBase + "diff/" + name.replace( ".rd5", "/" + md5 + ".df5" );
-
- URL urlDelta = new URL(surlDelta);
-
- updateProgress( "Connecting.." );
-
- connection = (HttpURLConnection) urlDelta.openConnection();
- connection.connect();
-
- // 404 kind of expected here, means there's no delta file
- if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND )
- {
- connection = null;
- }
- }
-
- if ( connection == null )
- {
- delta = false;
- URL url = new URL(surl);
- connection = (HttpURLConnection) url.openConnection();
- connection.connect();
- }
- // expect HTTP 200 OK, so we don't mistakenly save error report
- // instead of the file
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- return "Server returned HTTP " + connection.getResponseCode()
- + " " + connection.getResponseMessage();
- }
-
- // this will be useful to display download percentage
- // might be -1: server did not report the length
- int fileLength = connection.getContentLength();
- currentDownloadSize = fileLength;
- if ( availableSize >= 0 && fileLength > availableSize ) return "not enough space on sd-card";
-
- currentDownloadOperation = delta ? "Updating" : "Loading";
-
- // download the file
- input = connection.getInputStream();
-
- tmp_file = new File( fname.getAbsolutePath() + ( delta ? "_diff" : "_tmp" ) );
- output = new FileOutputStream( tmp_file );
-
- byte[] data = new byte[4096];
- long total = 0;
- long t0 = System.currentTimeMillis();
- int count;
- while ((count = input.read(data)) != -1) {
- if (isDownloadCanceled()) {
- return "Download canceled!";
- }
- total += count;
- // publishing the progress....
- if (fileLength > 0) // only if total length is known
- {
- int pct = (int) (total * 100 / fileLength);
- updateProgress( "Progress " + pct + "%" );
- }
- else
- {
- updateProgress( "Progress (unnown size)" );
- }
-
- output.write(data, 0, count);
-
- // enforce < 2 Mbit/s
- long dt = t0 + total/524 - System.currentTimeMillis();
- if ( dt > 0 )
- {
- try { Thread.sleep( dt ); } catch( InterruptedException ie ) {}
- }
- }
- output.close();
- output = null;
-
- if ( delta )
- {
- updateProgress( "Applying delta.." );
- File diffFile = tmp_file;
- tmp_file = new File( fname + "_tmp" );
- Rd5DiffTool.recoverFromDelta( fname, diffFile, tmp_file, this );
- diffFile.delete();
- }
- if (isDownloadCanceled())
- {
- return "Canceled!";
- }
- if ( tmp_file != null )
- {
- updateProgress( "Verifying integrity.." );
- String check_result = PhysicalFile.checkFileIntegrity( tmp_file );
- if ( check_result != null ) return check_result;
-
- if ( !tmp_file.renameTo( fname ) )
- {
- return "Could not rename to " + fname.getAbsolutePath();
- }
- deleteRawTracks(); // invalidate raw-tracks after data update
- }
- return null;
- } catch (Exception e) {
- return e.toString();
- } finally {
- try {
- if (output != null)
- output.close();
- if (input != null)
- input.close();
- } catch (IOException ignored) {
- }
-
- if (connection != null)
- connection.disconnect();
- }
- }
- finally
- {
- if ( tmp_file != null ) tmp_file.delete(); // just to be sure
- }
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- // take CPU lock to prevent CPU from going off if the user
- // presses the power button during download
- PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
- mWakeLock.acquire();
- }
-
- @Override
- protected void onProgressUpdate(Integer... progress) {
- if ( !newDownloadAction.equals( downloadAction ) )
- {
- downloadAction = newDownloadAction;
- invalidate();
- }
- }
-
- @Override
- protected void onPostExecute(String result) {
- mWakeLock.release();
- downloadDone( result == null );
-
- if (result != null)
- Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
- else
- Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
- }
-
- } // download task
-}
+}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
index 5c7c13e..18e71f0 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
@@ -4,12 +4,14 @@ import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -76,7 +78,9 @@ public class BRouterView extends View
private String profileName;
private String sourceHint;
private boolean waitingForSelection = false;
+ private boolean waitingForMigration = false;
private String rawTrackPath;
+ private String oldMigrationPath;
private boolean needsViaSelection;
private boolean needsNogoSelection;
@@ -124,8 +128,18 @@ public class BRouterView extends View
File brd = new File( baseDir, "brouter" );
if ( brd.isDirectory() )
{
- startSetup( baseDir, false );
- return;
+ if (brd.getAbsolutePath().contains("/Android/data/")) {
+ String message = "(previous basedir " + baseDir + " has to migrate )" ;
+
+ ( (BRouterActivity) getContext() ).selectBasedir( getStorageDirectories(), guessBaseDir(), message );
+ waitingForSelection = true;
+ waitingForMigration = true;
+ oldMigrationPath = brd.getAbsolutePath();
+ return;
+ } else {
+ startSetup( baseDir, false );
+ return;
+ }
}
}
String message = baseDir == null ? "(no basedir configured previously)" : "(previous basedir " + baseDir
@@ -202,6 +216,12 @@ public class BRouterView extends View
File inputDir = new File (basedir, "/import");
assertDirectoryExists( "input directory", inputDir, null, version );
+ // new init is done move old files
+ if (waitingForMigration) {
+ moveFolders(oldMigrationPath, basedir + "/brouter");
+ waitingForMigration = false;
+ }
+
int deviceLevel = android.os.Build.VERSION.SDK_INT;
int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
canAccessSdCard = deviceLevel < 23 || targetSdkVersion == 10;
@@ -320,6 +340,67 @@ public class BRouterView extends View
waitingForSelection = true;
}
+ private void moveFolders(String oldMigrationPath, String basedir) {
+ File oldDir = new File(oldMigrationPath);
+ File[] oldFiles = oldDir.listFiles();
+ for (File f: oldFiles) {
+ if (f.isDirectory()) {
+ int index = f.getAbsolutePath().lastIndexOf("/");
+ String tmpdir = basedir + f.getAbsolutePath().substring(index);
+ moveFolders(f.getAbsolutePath(), tmpdir);
+ } else {
+ if ( ! f.getName().startsWith("v1.6")) {
+ moveFile(oldMigrationPath, f.getName(), basedir);
+ }
+ }
+
+ }
+ }
+
+ private void moveFile(String inputPath, String inputFile, String outputPath) {
+
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+
+ //create output directory if it doesn't exist
+ File dir = new File (outputPath);
+ if (!dir.exists())
+ {
+ dir.mkdirs();
+ }
+
+
+ in = new FileInputStream(inputPath + "/" + inputFile);
+ out = new FileOutputStream(outputPath + "/" + inputFile);
+
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = in.read(buffer)) != -1) {
+ out.write(buffer, 0, read);
+ }
+ in.close();
+ in = null;
+
+ // write the output file
+ out.flush();
+ out.close();
+ out = null;
+
+ // delete the original file
+ new File(inputPath + "/" + inputFile).delete();
+
+
+ }
+
+ catch (FileNotFoundException fnfe1) {
+ Log.e("tag", fnfe1.getMessage());
+ }
+ catch (Exception e) {
+ Log.e("tag", e.getMessage());
+ }
+
+ }
public boolean hasUpToDateLookups()
{
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
new file mode 100644
index 0000000..b026a20
--- /dev/null
+++ b/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
@@ -0,0 +1,544 @@
+package btools.routingapp;
+
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Intent;
+import android.net.TrafficStats;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.StatFs;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+
+import btools.mapaccess.PhysicalFile;
+import btools.mapaccess.Rd5DiffManager;
+import btools.mapaccess.Rd5DiffTool;
+import btools.util.ProgressListener;
+
+public class DownloadService extends Service implements ProgressListener {
+
+ private static final boolean DEBUG = false;
+
+ String segmenturl = "https://brouter.de/brouter/segments4/";
+ String lookupurl = "https://brouter.de/brouter/segments4/";
+ String profilesurl = "https://brouter.de/brouter/segments4/";
+ String checkLookup = "lookups.dat";
+ String checkProfiles = "";
+
+ private NotificationHelper mNotificationHelper;
+ private List mUrlList;
+ private String baseDir;
+
+ private volatile String newDownloadAction = "";
+ private volatile String currentDownloadOperation = "";
+ private long availableSize;
+
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+ private NotificationManager mNM;
+ String downloadUrl;
+ public static boolean serviceState = false;
+ private boolean bIsDownloading;
+
+ // Handler that receives messages from the thread
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ bIsDownloading = true;
+ downloadFiles();
+
+ stopForeground(true);
+ stopSelf(msg.arg1);
+ mNotificationHelper.stopNotification();
+ }
+ }
+
+
+ @Override
+ public void onCreate() {
+ if (DEBUG) Log.d("SERVICE", "onCreate");
+ serviceState = true;
+
+ HandlerThread thread = new HandlerThread("ServiceStartArguments", 1);
+ thread.start();
+
+ // Get the HandlerThread's Looper and use it for our Handler
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper);
+
+ availableSize = -1;
+ try
+ {
+ StatFs stat = new StatFs(baseDir);
+ availableSize = (long)stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
+ }
+ catch (Exception e) { /* ignore */ }
+
+ }
+
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (DEBUG) Log.d("SERVICE", "onStartCommand");
+
+ mNotificationHelper = new NotificationHelper(this);
+ Bundle extra = intent.getExtras();
+ if (extra != null) {
+ String dir = extra.getString("dir");
+ List urlparts = extra.getStringArrayList("urlparts");
+ mUrlList = urlparts;
+ baseDir = dir;
+
+ File configFile = new File (dir, "segments4/serverconfig.txt");
+ if ( configFile.exists() ) {
+ try {
+ BufferedReader br = new BufferedReader( new FileReader( configFile ) );
+ for ( ;; )
+ {
+ String line = br.readLine();
+ if ( line == null ) break;
+ if ( line.trim().startsWith( "segment_url=" ) ) {
+ segmenturl = line.substring(12);
+ }
+ else if ( line.trim().startsWith( "lookup_url=" ) ) {
+ lookupurl = line.substring(11);
+ }
+ else if ( line.trim().startsWith( "profiles_url=" ) ) {
+ profilesurl = line.substring(13);
+ }
+ else if ( line.trim().startsWith( "check_lookup=" ) ) {
+ checkLookup = line.substring(13);
+ }
+ else if ( line.trim().startsWith( "check_profiles=" ) ) {
+ checkProfiles = line.substring(15);
+ }
+ }
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+ mNotificationHelper.startNotification(this);
+
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ mServiceHandler.sendMessage(msg);
+
+ // If we get killed, after returning from here, restart
+ return START_STICKY;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Log.d("SERVICE", "onDestroy");
+ serviceState = false;
+ super.onDestroy();
+ }
+
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+
+ public void downloadFiles() {
+
+ // first check lookup table and prifles
+ String result = checkScripts();
+ if ( result != null) {
+ if (DEBUG) Log.d("BR", "error: " + result);
+ bIsDownloading = false;
+ updateProgress( "finished " );
+
+ Toast.makeText(this, result, Toast.LENGTH_LONG).show();
+ return;
+ }
+
+
+ int count = 1;
+ int size = mUrlList.size();
+ for (String part: mUrlList) {
+ String url = segmenturl + part + ".rd5";
+ if (DEBUG) Log.d("BR", "downlaod " + url);
+
+ result = download(count, size, url);
+ if (result != null) {
+ if (DEBUG) Log.d("BR", "" + result);
+ Toast.makeText(this, result, Toast.LENGTH_LONG).show();
+ break;
+ } else {
+ updateProgress( "Download " + part + " " + count + "/"+ size + " finshed");
+ }
+ count++;
+ }
+
+ bIsDownloading = false;
+ updateProgress( "finished " );
+ }
+
+
+ public void updateProgress( String progress )
+ {
+ if ( !newDownloadAction.equals( progress ) )
+ {
+ if (DEBUG) Log.d("BR", "up " + progress);
+ Intent intent = new Intent(BInstallerActivity.DOWNLOAD_ACTION);
+ intent.putExtra("txt", progress);
+ intent.putExtra("ready", bIsDownloading);
+ sendBroadcast(intent);;
+ newDownloadAction = progress;
+ mNotificationHelper.progressUpdate(newDownloadAction);
+ }
+
+ }
+
+ private String download(int counter, int size, String surl)
+ {
+ InputStream input = null;
+ OutputStream output = null;
+ HttpURLConnection connection = null;
+ File fname = null;
+ File tmp_file = null;
+ try
+ {
+ try
+ {
+ TrafficStats.setThreadStatsTag(1);
+
+ int slidx = surl.lastIndexOf( "segments4/" );
+ String name = surl.substring( slidx+10 );
+ String surlBase = surl.substring( 0, slidx+10 );
+ fname = new File (baseDir, "segments4/" + name);
+
+ boolean delta = true;
+
+ // if (!targetFile.getParentFile().exists()) targetFile.getParentFile().mkdirs();
+ if ( fname.exists() )
+ {
+ updateProgress( "Calculating local checksum.." );
+
+ // first check for a delta file
+ String md5 = Rd5DiffManager.getMD5( fname );
+ String surlDelta = surlBase + "diff/" + name.replace( ".rd5", "/" + md5 + ".df5" );
+
+ URL urlDelta = new URL(surlDelta);
+
+ connection = (HttpURLConnection) urlDelta.openConnection();
+ connection.connect();
+
+ // 404 kind of expected here, means there's no delta file
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND )
+ {
+ connection = null;
+ } else {
+ updateProgress( "Connecting.." + surlDelta );
+ }
+ }
+
+ if ( connection == null )
+ {
+ updateProgress( "Connecting.." + surl );
+
+ delta = false;
+ URL url = new URL(surl);
+ connection = (HttpURLConnection) url.openConnection();
+ connection.connect();
+ }
+
+ updateProgress( "Connecting.." + counter + "/"+size );
+
+ // expect HTTP 200 OK, so we don't mistakenly save error report
+ // instead of the file
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ return "Server returned HTTP " + connection.getResponseCode()
+ + " " + connection.getResponseMessage();
+ }
+
+ // this will be useful to display download percentage
+ // might be -1: server did not report the length
+ int fileLength = connection.getContentLength();
+ long currentDownloadSize = fileLength;
+ if ( availableSize >= 0 && fileLength > availableSize ) return "not enough space on sd-card";
+
+ currentDownloadOperation = delta ? "Updating" : "Loading";
+ updateProgress( currentDownloadOperation);
+
+ // download the file
+ input = connection.getInputStream();
+
+ tmp_file = new File( fname.getAbsolutePath() + ( delta ? "_diff" : "_tmp" ) );
+ output = new FileOutputStream( tmp_file );
+
+ byte[] data = new byte[4096];
+ long total = 0;
+ long t0 = System.currentTimeMillis();
+ int count;
+ while ((count = input.read(data)) != -1) {
+ if (isCanceled()) {
+ return "Download canceled!";
+ }
+ total += count;
+ // publishing the progress....
+ if (fileLength > 0) // only if total length is known
+ {
+ int pct = (int) (total * 100 / fileLength);
+ updateProgress( "Progress " + counter + "/"+size + " .. " + pct + "%" );
+ }
+ else
+ {
+ updateProgress( "Progress (unnown size)" );
+ }
+
+ output.write(data, 0, count);
+
+ // enforce < 2 Mbit/s
+ long dt = t0 + total/524 - System.currentTimeMillis();
+ if ( dt > 0 )
+ {
+ try { Thread.sleep( dt ); } catch( InterruptedException ie ) {}
+ }
+ }
+ output.close();
+ output = null;
+
+ if ( delta )
+ {
+ updateProgress( "Applying delta.." );
+ File diffFile = tmp_file;
+ tmp_file = new File( fname + "_tmp" );
+ Rd5DiffTool.recoverFromDelta( fname, diffFile, tmp_file, this );
+ diffFile.delete();
+ }
+ if (isCanceled())
+ {
+ return "Canceled!";
+ }
+ if ( tmp_file != null )
+ {
+ updateProgress( "Verifying integrity.." );
+ String check_result = PhysicalFile.checkFileIntegrity( tmp_file );
+ if ( check_result != null ) {
+ if (check_result.startsWith("version old lookups.dat") ) {
+
+ }
+ return check_result;
+ }
+
+ if ( !tmp_file.renameTo( fname ) )
+ {
+ return "Could not rename to " + fname.getAbsolutePath();
+ }
+
+ }
+ return null;
+ } catch (Exception e) {
+ e.printStackTrace(); ;
+ return e.toString();
+ } finally {
+ try {
+ if (output != null)
+ output.close();
+ if (input != null)
+ input.close();
+ } catch (IOException ignored) {
+ }
+
+ if (connection != null)
+ connection.disconnect();
+ }
+ }
+ finally
+ {
+ if ( tmp_file != null ) tmp_file.delete(); // just to be sure
+ }
+ }
+
+ private String checkScripts() {
+
+ String[] sa = checkLookup.split(",");
+ for (String f: sa) {
+ if (f.length()>0) {
+ File file = new File(baseDir + "profiles2", f);
+ checkOrDownloadLookup(f, file);
+ }
+ }
+
+ sa = checkProfiles.split(",");
+ for (String f : sa) {
+ if (f.length()>0) {
+ File file = new File(baseDir + "profiles2", f);
+ if (file.exists()) {
+ String result = checkOrDownloadScript(f, file);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private String checkOrDownloadLookup(String fileName, File f) {
+ String url = lookupurl + fileName;
+ return downloadScript(url, f);
+ }
+
+ private String checkOrDownloadScript(String fileName, File f) {
+ String url = profilesurl + fileName;
+ return downloadScript(url, f);
+ }
+
+ private String downloadScript(String surl, File f) {
+ long size = 0L;
+ if (f.exists()) {
+ size = f.length();
+ }
+
+ InputStream input = null;
+ OutputStream output = null;
+ HttpURLConnection connection = null;
+ File tmp_file = null;
+ File targetFile = f;
+
+ try
+ {
+ try
+ {
+ TrafficStats.setThreadStatsTag(1);
+
+ if ( connection == null )
+ {
+ URL url = new URL(surl);
+ connection = (HttpURLConnection) url.openConnection();
+ connection.connect();
+ }
+ // expect HTTP 200 OK, so we don't mistakenly save error report
+ // instead of the file
+ if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
+ return null;
+ }
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ return "Server returned HTTP " + connection.getResponseCode()
+ + " " + connection.getResponseMessage() + " " + f.getName();
+ }
+
+
+ // this will be useful to display download percentage
+ // might be -1: server did not report the length
+ long fileLength = (long)connection.getContentLength();
+ if (DEBUG) Log.d("BR", "file size " + size + " == " + fileLength + " " + f.getName());
+ if (fileLength != size) {
+ long currentDownloadSize = fileLength;
+ if (availableSize >= 0 && fileLength > availableSize)
+ return "not enough space on sd-card";
+
+ currentDownloadOperation = "Updating";
+
+ // download the file
+ input = connection.getInputStream();
+
+ tmp_file = new File(f.getAbsolutePath() + "_tmp");
+ output = new FileOutputStream(tmp_file);
+
+ byte data[] = new byte[4096];
+ long total = 0;
+ long t0 = System.currentTimeMillis();
+ int count;
+ while ((count = input.read(data)) != -1) {
+ if (isCanceled()) {
+ return "Download canceled!";
+ }
+ total += count;
+ // publishing the progress....
+ if (fileLength > 0) // only if total length is known
+ {
+ int pct = (int) (total * 100 / fileLength);
+ updateProgress("Progress " + pct + "%");
+ } else {
+ updateProgress("Progress (unnown size)");
+ }
+
+ output.write(data, 0, count);
+
+ // enforce < 2 Mbit/s
+ long dt = t0 + total / 524 - System.currentTimeMillis();
+ if (dt > 0) {
+ try {
+ Thread.sleep(dt);
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+ output.close();
+ output = null;
+ }
+
+ if (isCanceled())
+ {
+ return "Canceled!";
+ }
+ if ( tmp_file != null )
+ {
+ f.delete();
+
+ if ( !tmp_file.renameTo( f ) )
+ {
+ return "Could not rename to " + f.getName();
+ }
+ if (DEBUG) Log.d("BR", "update " + f.getName());
+ }
+ return null;
+ } catch (Exception e) {
+ return e.toString() ;
+ } finally {
+ try {
+ if (output != null)
+ output.close();
+ if (input != null)
+ input.close();
+ } catch (IOException ignored) {
+ }
+
+ if (connection != null)
+ connection.disconnect();
+
+ }
+ }
+ finally
+ {
+ if ( tmp_file != null ) tmp_file.delete(); // just to be sure
+ }
+
+ }
+
+
+ public boolean isCanceled() {
+ return BInstallerView.downloadCanceled;
+ }
+
+}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java b/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
new file mode 100644
index 0000000..ddbf4a1
--- /dev/null
+++ b/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
@@ -0,0 +1,135 @@
+package btools.routingapp;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioAttributes;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.core.app.NotificationCompat;
+
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+
+public class NotificationHelper {
+
+ private static final boolean DEBUG = false;
+
+ public static String BRouterNotificationChannel1 = "brouter_channel_01";
+
+ private Context mContext;
+ private int NOTIFICATION_ID = 111;
+ private Notification mNotification;
+ private NotificationManager mNotificationManager;
+ private PendingIntent mContentIntent;
+ private CharSequence mContentTitle;
+
+ public NotificationHelper(Context context)
+ {
+ if (DEBUG) Log.d("NH", "init " );
+ mContext = context;
+ createNotificationChannels();
+ }
+
+ public void startNotification(Service service) {
+ if (DEBUG) Log.d("NH", "startNotification " );
+
+ mNotification = createNotification("BRouter Download", "Download some files");
+
+ if (service != null) service.startForeground(NOTIFICATION_ID, mNotification);
+
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification);
+
+ }
+
+ public void progressUpdate(String text) {
+ mNotification = createNotification("BRouter Download", text);
+ mNotification.flags = Notification.FLAG_NO_CLEAR |
+ Notification.FLAG_ONGOING_EVENT;
+
+ mNotificationManager.notify(NOTIFICATION_ID, mNotification);
+ }
+
+
+ public Notification createNotification(String title, String desc) {
+
+ Intent resultIntent = new Intent(mContext, BInstallerActivity.class);
+
+ Intent notificationIntent = new Intent();
+ mContentIntent = PendingIntent.getActivity(mContext, 0, resultIntent, PendingIntent.FLAG_IMMUTABLE);
+
+ mNotificationManager = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+
+
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, BRouterNotificationChannel1);
+ builder.setSmallIcon(android.R.drawable.stat_sys_download)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setContentTitle(title)
+ .setContentText(desc)
+ .setTicker(desc)
+ .setOngoing(true)
+ .setAutoCancel(true)
+ .setOnlyAlertOnce(true)
+ .setCategory(NotificationCompat.CATEGORY_SERVICE)
+ .setContentIntent(mContentIntent);
+
+ return builder.build();
+
+ } else {
+ final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
+ builder.setSmallIcon(android.R.drawable.stat_sys_download)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setContentTitle(title)
+ .setContentText(desc)
+ .setOnlyAlertOnce(true)
+ .setCategory(NotificationCompat.CATEGORY_SERVICE)
+ .setContentIntent(mContentIntent);
+
+ return builder.build();
+ }
+
+ }
+
+ /**
+ * create notification channels
+ */
+ public void createNotificationChannels() {
+ if (DEBUG) Log.d("NH", "createNotificationChannels " );
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+
+ NotificationManager sNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ // Sound channel
+ CharSequence name = "BRouter Download";
+ // The user-visible description of the channel.
+ String description = "BRouter Download Channel"; //getString(R.string.channel_description);
+
+ NotificationChannel channel = new NotificationChannel(BRouterNotificationChannel1, name, NotificationManager.IMPORTANCE_LOW);
+ channel.setDescription(description);
+ AudioAttributes att = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_UNKNOWN)
+ .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+ .build();
+ channel.setSound(null, null);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+
+ sNotificationManager.createNotificationChannel(channel);
+
+ }
+ }
+
+ public void stopNotification() {
+ if (DEBUG) Log.d("NH", "stopNotification " );
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ mNotificationManager.deleteNotificationChannel(BRouterNotificationChannel1);
+ }
+ mNotificationManager.cancel(NOTIFICATION_ID);
+ }
+}
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/res/drawable/ic_launcher_background.xml b/brouter-routing-app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..7c8c867
--- /dev/null
+++ b/brouter-routing-app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/brouter-routing-app/src/main/res/drawable/ic_launcher_foreground.xml b/brouter-routing-app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2393404
--- /dev/null
+++ b/brouter-routing-app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/brouter-routing-app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher.png b/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cea3d79
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..0042e71
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher.png b/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..7944fa0
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1266e2a
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher.png b/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..f71fcae
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9648664
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2140746
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9ba382b
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..5c86dd0
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3ac14a4
Binary files /dev/null and b/brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1d288b1..6df5e88 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip