diff --git a/brouter-codec/src/main/java/btools/codec/MicroCache2.java b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
index 8cd8f30..16223a1 100644
--- a/brouter-codec/src/main/java/btools/codec/MicroCache2.java
+++ b/brouter-codec/src/main/java/btools/codec/MicroCache2.java
@@ -15,7 +15,7 @@ public final class MicroCache2 extends MicroCache
private int latBase;
private int cellsize;
- public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
+ public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor )
{
super( databuffer ); // sets ab=databuffer, aboffset=0
@@ -34,7 +34,7 @@ public final class MicroCache2 extends MicroCache
return b;
}
- public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
+ public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher )
{
super( null );
cellsize = 1000000 / divisor;
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
index ed25fd8..dc7191e 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/DirectWeaver.java
@@ -20,7 +20,7 @@ public final class DirectWeaver extends ByteDataWriter
private int size = 0;
- public DirectWeaver( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes ) throws Exception
+ public DirectWeaver( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher, OsmNodesMap hollowNodes )
{
super( null );
int cellsize = 1000000 / divisor;
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
index bf03490..f2c481b 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java
@@ -35,7 +35,7 @@ final class OsmFile
private int ncaches;
private int indexsize;
- public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws Exception
+ public OsmFile( PhysicalFile rafile, int lonDegree, int latDegree, DataBuffers dataBuffers ) throws IOException
{
this.lonDegree = lonDegree;
this.latDegree = latDegree;
@@ -111,7 +111,7 @@ final class OsmFile
return idx == -1 ? indexsize : posIdx[idx];
}
- public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws Exception
+ public int getDataInputForSubIdx( int subIdx, byte[] iobuffer ) throws IOException
{
int startPos = getPosIdx( subIdx - 1 );
int endPos = getPosIdx( subIdx );
@@ -128,7 +128,7 @@ final class OsmFile
}
public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator,
- WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes ) throws Exception
+ WaypointMatcher waypointMatcher, boolean reallyDecode, OsmNodesMap hollowNodes ) throws IOException
{
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
index 1c378e0..1798fd3 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java
@@ -31,13 +31,12 @@ final public class PhysicalFile
{
MicroCache.debug = true;
- String message = checkFileIntegrity( new File( args[0] ) );
-
- if ( message != null )
- {
- System.out.println( "************************************" );
- System.out.println( message );
- System.out.println( "************************************" );
+ try {
+ checkFileIntegrity( new File( args[0] ) );
+ } catch (IOException e) {
+ System.err.println( "************************************" );
+ e.printStackTrace();
+ System.err.println( "************************************" );
}
}
@@ -46,7 +45,7 @@ final public class PhysicalFile
*
* @return the error message if file corrupt, else null
*/
- public static String checkFileIntegrity( File f )
+ public static String checkFileIntegrity( File f ) throws IOException
{
PhysicalFile pf = null;
try
@@ -66,14 +65,6 @@ final public class PhysicalFile
}
}
}
- catch (IllegalArgumentException iae)
- {
- return iae.getMessage();
- }
- catch (Exception e)
- {
- return e.toString();
- }
finally
{
if ( pf != null )
@@ -88,7 +79,7 @@ final public class PhysicalFile
return null;
}
- public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws Exception
+ public PhysicalFile( File f, DataBuffers dataBuffers, int lookupVersion, int lookupMinorVersion ) throws IOException
{
fileName = f.getName();
byte[] iobuffer = dataBuffers.iobuffer;
@@ -102,7 +93,7 @@ final public class PhysicalFile
short readVersion = (short)(lv >> 48);
if ( i == 0 && lookupVersion != -1 && readVersion != lookupVersion )
{
- throw new IllegalArgumentException( "lookup version mismatch (old rd5?) lookups.dat="
+ throw new IOException( "lookup version mismatch (old rd5?) lookups.dat="
+ lookupVersion + " " + f. getAbsolutePath() + "=" + readVersion );
}
fileIndex[i] = lv & 0xffffffffffffL;
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
index 86ff392..3b33904 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffManager.java
@@ -8,8 +8,10 @@ package btools.mapaccess;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
final public class Rd5DiffManager
{
@@ -93,30 +95,31 @@ final public class Rd5DiffManager
}
}
- public static String getMD5( File f ) throws Exception
+ public static String getMD5( File f ) throws IOException
{
- MessageDigest md = MessageDigest.getInstance("MD5");
- BufferedInputStream bis = new BufferedInputStream( new FileInputStream( f ) );
- DigestInputStream dis = new DigestInputStream(bis, md);
- byte[] buf = new byte[8192];
- for(;;)
- {
- int len = dis.read( buf );
- if ( len <= 0 )
- {
- break;
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
+ DigestInputStream dis = new DigestInputStream(bis, md);
+ byte[] buf = new byte[8192];
+ for (; ; ) {
+ int len = dis.read(buf);
+ if (len <= 0) {
+ break;
+ }
}
- }
- dis.close();
- byte[] bytes = md.digest();
+ dis.close();
+ byte[] bytes = md.digest();
- StringBuilder sb = new StringBuilder();
- for (int j = 0; j < bytes.length; j++)
- {
- int v = bytes[j] & 0xff;
- sb.append( hexChar( v >>> 4 ) ).append( hexChar( v & 0xf ) );
+ StringBuilder sb = new StringBuilder();
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xff;
+ sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
+ }
+ return sb.toString();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException("MD5 algorithm not available", e);
}
- return sb.toString();
}
private static char hexChar( int v )
diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
index ac2f1d9..c5ffbf1 100644
--- a/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
+++ b/brouter-mapaccess/src/main/java/btools/mapaccess/Rd5DiffTool.java
@@ -12,6 +12,7 @@ import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.util.Arrays;
import btools.codec.DataBuffers;
@@ -49,9 +50,8 @@ final public class Rd5DiffTool implements ProgressListener
}
@Override
- public void updateProgress( String progress )
- {
- System.out.println( progress );
+ public void updateProgress(String task, int progress) {
+ System.out.println(task + ": " + progress + "%");
}
@Override
@@ -60,7 +60,7 @@ final public class Rd5DiffTool implements ProgressListener
return false;
}
- private static long[] readFileIndex( DataInputStream dis, DataOutputStream dos ) throws Exception
+ private static long[] readFileIndex( DataInputStream dis, DataOutputStream dos ) throws IOException
{
long[] fileIndex = new long[25];
for( int i=0; i<25; i++ )
@@ -85,7 +85,7 @@ final public class Rd5DiffTool implements ProgressListener
return index[tileIndex];
}
- private static int[] readPosIndex( DataInputStream dis, DataOutputStream dos ) throws Exception
+ private static int[] readPosIndex( DataInputStream dis, DataOutputStream dos ) throws IOException
{
int[] posIndex = new int[1024];
for( int i=0; i<1024; i++ )
@@ -105,7 +105,7 @@ final public class Rd5DiffTool implements ProgressListener
return idx == -1 ? 4096 : posIdx[idx];
}
- private static byte[] createMicroCache( int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode ) throws Exception
+ private static byte[] createMicroCache( int[] posIdx, int tileIdx, DataInputStream dis, boolean deltaMode ) throws IOException
{
if ( posIdx == null )
{
@@ -125,7 +125,7 @@ final public class Rd5DiffTool implements ProgressListener
return ab;
}
- private static MicroCache createMicroCache( byte[] ab, DataBuffers dataBuffers ) throws Exception
+ private static MicroCache createMicroCache( byte[] ab, DataBuffers dataBuffers )
{
if ( ab == null || ab.length == 0 )
{
@@ -286,7 +286,7 @@ final public class Rd5DiffTool implements ProgressListener
}
- public static void recoverFromDelta( File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */ ) throws Exception
+ public static void recoverFromDelta( File f1, File f2, File outFile, ProgressListener progress /* , File cmpFile */ ) throws IOException
{
if ( f2.length() == 0L )
{
@@ -341,7 +341,7 @@ final public class Rd5DiffTool implements ProgressListener
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
if ( pct != lastPct )
{
- progress.updateProgress( "Applying delta: " + pct + "%" );
+ progress.updateProgress("Applying delta", pct);
lastPct = pct;
}
@@ -468,7 +468,7 @@ final public class Rd5DiffTool implements ProgressListener
}
}
- public static void copyFile( File f1, File outFile, ProgressListener progress ) throws Exception
+ public static void copyFile( File f1, File outFile, ProgressListener progress ) throws IOException
{
boolean canceled = false;
DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) );
@@ -489,7 +489,7 @@ final public class Rd5DiffTool implements ProgressListener
int pct = (int)( (100. * sizeRead) / (sizeTotal+1) + 0.5 );
if ( pct != lastPct )
{
- progress.updateProgress( "Copying: " + pct + "%" );
+ progress.updateProgress("Copying", pct);
lastPct = pct;
}
int len = dis1.read( buf );
@@ -756,7 +756,7 @@ final public class Rd5DiffTool implements ProgressListener
this.dataBuffers = dataBuffers;
}
- public MicroCache readMC() throws Exception
+ public MicroCache readMC() throws IOException
{
if (skips < 0 )
{
@@ -775,7 +775,7 @@ final public class Rd5DiffTool implements ProgressListener
return mc;
}
- public void finish() throws Exception
+ public void finish()
{
skips = -1;
}
diff --git a/brouter-routing-app/build.gradle b/brouter-routing-app/build.gradle
index 4c88be6..d254021 100644
--- a/brouter-routing-app/build.gradle
+++ b/brouter-routing-app/build.gradle
@@ -5,7 +5,7 @@ plugins {
}
android {
- compileSdkVersion 30
+ compileSdkVersion 31
defaultConfig {
applicationId "btools.routingapp"
@@ -14,16 +14,16 @@ android {
versionName project.version
resValue('string', 'app_version', defaultConfig.versionName)
- setProperty("archivesBaseName","BRouterApp." + defaultConfig.versionName)
+ setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName)
minSdkVersion 14
-
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets.main.assets.srcDirs += new File(project.buildDir, 'assets')
- if(project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
+ if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
signingConfigs {
// this uses a file ~/.gradle/gradle.properties
// with content:
@@ -33,17 +33,17 @@ android {
// RELEASE_KEY_PASSWORD=*****
//
release {
- // enable signingConfig in buildTypes to get a signed apk file
- storeFile file(RELEASE_STORE_FILE)
- storePassword RELEASE_STORE_PASSWORD
- keyAlias RELEASE_KEY_ALIAS
- keyPassword RELEASE_KEY_PASSWORD
+ // enable signingConfig in buildTypes to get a signed apk file
+ storeFile file(RELEASE_STORE_FILE)
+ storePassword RELEASE_STORE_PASSWORD
+ keyAlias RELEASE_KEY_ALIAS
+ keyPassword RELEASE_KEY_PASSWORD
- // Optional, specify signing versions used
- v1SigningEnabled true
- v2SigningEnabled true
+ // Optional, specify signing versions used
+ v1SigningEnabled true
+ v2SigningEnabled true
- }
+ }
}
}
@@ -51,7 +51,7 @@ android {
release {
minifyEnabled false
debuggable false
- if(project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
+ if (project.hasProperty("RELEASE_STORE_FILE") && RELEASE_STORE_FILE.length() > 0) {
signingConfig signingConfigs.release
}
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
@@ -64,7 +64,8 @@ android {
}
lintOptions {
disable 'InvalidPackage'
- checkReleaseBuilds false //added this line to the build.gradle under the /android/app/build.gradle
+ checkReleaseBuilds false
+ //added this line to the build.gradle under the /android/app/build.gradle
}
compileOptions {
@@ -93,9 +94,10 @@ android {
}
dependencies {
-
- implementation 'androidx.appcompat:appcompat:1.3.1'
- implementation "androidx.constraintlayout:constraintlayout:2.1.2"
+ implementation 'androidx.appcompat:appcompat:1.4.1'
+ implementation "androidx.constraintlayout:constraintlayout:2.1.3"
+ implementation 'androidx.work:work-runtime:2.7.1'
+ implementation 'com.google.android.material:material:1.5.0'
implementation project(':brouter-mapaccess')
implementation project(':brouter-core')
@@ -106,6 +108,7 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation 'androidx.work:work-testing:2.7.1'
}
task generateProfiles(type: Exec) {
@@ -115,8 +118,7 @@ task generateProfiles(type: Exec) {
task generateProfilesZip(type: Zip) {
if (DefaultNativePlatform.getCurrentOperatingSystem().isWindows()) {
logger.warn("Note: On Windows run script '../misc/scripts/generate_profile_variants.sh' manually to include all profiles")
- }
- else {
+ } else {
dependsOn generateProfiles
}
archiveFileName = "profiles2.zip"
diff --git a/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java
new file mode 100644
index 0000000..a9004c0
--- /dev/null
+++ b/brouter-routing-app/src/androidTest/java/btools/routingapp/DownloadWorkerTest.java
@@ -0,0 +1,71 @@
+package btools.routingapp;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.work.Data;
+import androidx.work.ListenableWorker.Result;
+import androidx.work.testing.TestWorkerBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+@RunWith(AndroidJUnit4.class)
+public class DownloadWorkerTest {
+ private Context context;
+ private Executor executor;
+
+ @Before
+ public void setUp() {
+ context = ApplicationProvider.getApplicationContext();
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void testDownloadNewFile() {
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, new String[]{"E105_N50"})
+ .build();
+
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .setInputData(inputData)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.success()));
+ }
+
+ @Test
+ public void testDownloadInvalidSegment() {
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, new String[]{"X00"})
+ .build();
+
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .setInputData(inputData)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.failure()));
+ }
+
+ @Test
+ public void testDownloadNoSegments() {
+ DownloadWorker worker =
+ TestWorkerBuilder.from(context, DownloadWorker.class, executor)
+ .build();
+
+ Result result = worker.doWork();
+ assertThat(result, is(Result.failure()));
+ }
+}
diff --git a/brouter-routing-app/src/main/AndroidManifest.xml b/brouter-routing-app/src/main/AndroidManifest.xml
index c1ffbdf..76f6b51 100644
--- a/brouter-routing-app/src/main/AndroidManifest.xml
+++ b/brouter-routing-app/src/main/AndroidManifest.xml
@@ -3,7 +3,9 @@
-
@@ -11,17 +13,16 @@
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:preserveLegacyExternalStorage="true"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:theme="@style/Theme.App">
+ android:screenOrientation="unspecified">
@@ -29,20 +30,18 @@
+ android:launchMode="singleTask"
+ android:screenOrientation="landscape" />
-
+ android:label="Import Profile">
+
+
@@ -88,15 +87,10 @@
-
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
index 3d5f085..2550bf9 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BImportActivity.java
@@ -1,6 +1,5 @@
package btools.routingapp;
-import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -12,6 +11,7 @@ import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedReader;
import java.io.File;
@@ -20,7 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-public class BImportActivity extends Activity {
+public class BImportActivity extends AppCompatActivity {
// profile size is generally < 30 kb, so set max size to 100 kb
private static final int MAX_PROFILE_SIZE = 100000;
private EditText mTextFilename;
@@ -76,8 +76,8 @@ public class BImportActivity extends Activity {
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
- filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
- filesize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
+ filename = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
+ filesize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
}
}
// is the file extention ".brf" in the file name
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 286142e..c12ba62 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java
@@ -5,23 +5,29 @@ import static btools.routingapp.BInstallerView.MASK_DELETED_RD5;
import static btools.routingapp.BInstallerView.MASK_INSTALLED_RD5;
import static btools.routingapp.BInstallerView.MASK_SELECTED_RD5;
-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.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.StatFs;
import android.text.format.Formatter;
-import android.view.View;
import android.widget.Button;
import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.work.Constraints;
+import androidx.work.Data;
+import androidx.work.NetworkType;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkInfo;
+import androidx.work.WorkManager;
+import androidx.work.WorkRequest;
+
+import com.google.android.material.progressindicator.LinearProgressIndicator;
import java.io.File;
import java.util.ArrayList;
@@ -29,20 +35,15 @@ import java.util.Locale;
import btools.router.RoutingHelper;
-public class BInstallerActivity extends Activity {
+public class BInstallerActivity extends AppCompatActivity {
- public static final String DOWNLOAD_ACTION = "btools.routingapp.download";
private static final int DIALOG_CONFIRM_DELETE_ID = 1;
public static boolean downloadCanceled = false;
private File mBaseDir;
private BInstallerView mBInstallerView;
- private DownloadReceiver downloadReceiver;
- private View mDownloadInfo;
- private TextView mDownloadInfoText;
- private Button mButtonDownloadCancel;
private Button mButtonDownload;
private TextView mSummaryInfo;
- private View mSegmentsView;
+ private LinearProgressIndicator mProgressIndicator;
public static long getAvailableSpace(String baseDir) {
StatFs stat = new StatFs(baseDir);
@@ -63,7 +64,6 @@ public class BInstallerActivity extends Activity {
setContentView(R.layout.activity_binstaller);
mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
- mSegmentsView = findViewById(R.id.view_segments);
mBInstallerView = findViewById(R.id.BInstallerView);
mBInstallerView.setOnSelectListener(
() -> {
@@ -82,12 +82,7 @@ public class BInstallerActivity extends Activity {
}
}
);
- mDownloadInfo = findViewById(R.id.view_download_progress);
- mDownloadInfoText = findViewById(R.id.textViewDownloadProgress);
- mButtonDownloadCancel = findViewById(R.id.buttonDownloadCancel);
- mButtonDownloadCancel.setOnClickListener(view -> {
- cancelDownload();
- });
+ mProgressIndicator = findViewById(R.id.progressDownload);
mBaseDir = ConfigHelper.getBaseDir(this);
scanExistingFiles();
@@ -136,53 +131,77 @@ public class BInstallerActivity extends Activity {
}
}
- private void cancelDownload() {
- downloadCanceled = true;
- mDownloadInfoText.setText(getString(R.string.download_info_cancel));
- }
-
public void downloadAll(ArrayList downloadList) {
ArrayList urlparts = new ArrayList<>();
for (Integer i : downloadList) {
urlparts.add(baseNameForTile(i));
}
- mSegmentsView.setVisibility(View.GONE);
- mDownloadInfo.setVisibility(View.VISIBLE);
downloadCanceled = false;
- mDownloadInfoText.setText(R.string.download_info_start);
- Intent intent = new Intent(this, DownloadService.class);
- intent.putExtra("dir", mBaseDir.getAbsolutePath() + "/brouter/");
- intent.putExtra("urlparts", urlparts);
- startService(intent);
+ Data inputData = new Data.Builder()
+ .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0]))
+ .build();
+
+ Constraints constraints = new Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build();
+
+ WorkRequest downloadWorkRequest =
+ new OneTimeWorkRequest.Builder(DownloadWorker.class)
+ .setInputData(inputData)
+ .setConstraints(constraints)
+ .build();
+
+ WorkManager workManager = WorkManager.getInstance(getApplicationContext());
+ workManager.enqueue(downloadWorkRequest);
+
+ workManager
+ .getWorkInfoByIdLiveData(downloadWorkRequest.getId())
+ .observe(this, workInfo -> {
+ if (workInfo != null) {
+ if (workInfo.getState() == WorkInfo.State.ENQUEUED) {
+ Toast.makeText(this, "Download scheduled. Check internet connection if it doesn't start.", Toast.LENGTH_LONG).show();
+ mProgressIndicator.hide();
+ mProgressIndicator.setIndeterminate(true);
+ mProgressIndicator.show();
+ }
+
+ if (workInfo.getState() == WorkInfo.State.RUNNING) {
+ Data progress = workInfo.getProgress();
+ String segmentName = progress.getString(DownloadWorker.PROGRESS_SEGMENT_NAME);
+ int percent = progress.getInt(DownloadWorker.PROGRESS_SEGMENT_PERCENT, 0);
+ if (percent > 0) {
+ mProgressIndicator.setIndeterminate(false);
+ }
+ mProgressIndicator.setProgress(percent);
+ }
+
+ if (workInfo.getState().isFinished()) {
+ String result;
+ switch (workInfo.getState()) {
+ case FAILED:
+ result = "failed.";
+ break;
+ case CANCELLED:
+ result = "cancelled";
+ break;
+ case SUCCEEDED:
+ result = "succeeded";
+ break;
+ default:
+ result = "";
+ }
+ Toast.makeText(this, "Download " + result + ".", Toast.LENGTH_SHORT).show();
+ mProgressIndicator.hide();
+ scanExistingFiles();
+ }
+ }
+ });
deleteRawTracks(); // invalidate raw-tracks after data update
}
- @Override
- protected void onResume() {
- super.onResume();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(DOWNLOAD_ACTION);
-
- downloadReceiver = new DownloadReceiver();
- registerReceiver(downloadReceiver, filter);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (downloadReceiver != null) unregisterReceiver(downloadReceiver);
- System.exit(0);
- }
-
@Override
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder;
@@ -278,21 +297,4 @@ public class BInstallerActivity extends Activity {
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
return slon + "_" + slat;
}
-
- 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);
- if (!ready) {
- mSegmentsView.setVisibility(View.VISIBLE);
- mDownloadInfo.setVisibility(View.GONE);
- scanExistingFiles();
- }
- mDownloadInfoText.setText(txt);
- }
- }
- }
}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
index 5fb8e04..c3253ce 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java
@@ -1,7 +1,24 @@
package btools.routingapp;
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.StatFs;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.os.EnvironmentCompat;
+
import java.io.File;
-import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -9,36 +26,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.StatFs;
-import android.speech.tts.TextToSpeech.OnInitListener;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.os.EnvironmentCompat;
-
import btools.router.OsmNodeNamed;
-public class BRouterActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback {
+public class BRouterActivity extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback {
private static final int DIALOG_SELECTPROFILE_ID = 1;
private static final int DIALOG_EXCEPTION_ID = 2;
@@ -133,7 +123,7 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
startActivity(intent);
- // finish();
+ showNewDialog(DIALOG_MAINACTION_ID);
}
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
@@ -391,39 +381,12 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
private String maptoolDirCandidate;
- public boolean isOnline(Context context) {
- boolean result = false;
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- Network nw = connectivityManager.getActiveNetwork();
- if (nw == null) return false;
- NetworkCapabilities nwc = connectivityManager.getNetworkCapabilities(nw);
- if (nwc == null) return false;
- result = nwc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) |
- nwc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) |
- nwc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
-
- } else {
- NetworkInfo ni = connectivityManager.getActiveNetworkInfo();
- if (ni == null) return false;
- result = ni.getType() == ConnectivityManager.TYPE_WIFI ||
- ni.getType() == ConnectivityManager.TYPE_MOBILE ||
- ni.getType() == ConnectivityManager.TYPE_ETHERNET;
- }
-
- return result;
- }
-
@SuppressWarnings("deprecation")
public void selectProfile(String[] items) {
availableProfiles = items;
- // if we have internet access, first show the main action dialog
- if (isOnline(this)) {
- showDialog(DIALOG_MAINACTION_ID);
- } else {
- showDialog(DIALOG_SELECTPROFILE_ID);
- }
+ // show main dialog
+ showDialog(DIALOG_MAINACTION_ID);
}
@SuppressWarnings("deprecation")
@@ -626,6 +589,7 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mBRouterView.startSetup(null, true);
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 fa02aae..c21af97 100644
--- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
+++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java
@@ -1,5 +1,21 @@
package btools.routingapp;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Build;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -12,36 +28,13 @@ 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;
import java.util.List;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.AssetManager;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.os.Build;
-import android.os.Environment;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
import btools.mapaccess.OsmNode;
import btools.router.OsmNodeNamed;
@@ -108,11 +101,6 @@ public class BRouterView extends View {
public void init() {
try {
- DisplayMetrics metrics = new DisplayMetrics();
- ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
- imgw = metrics.widthPixels;
- imgh = metrics.heightPixels;
-
// get base dir from private file
File baseDir = ConfigHelper.getBaseDir(getContext());
// check if valid
@@ -703,6 +691,8 @@ public class BRouterView extends View {
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ imgw = w;
+ imgh = h;
}
private void toast(String msg) {
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
deleted file mode 100644
index 7457a84..0000000
--- a/brouter-routing-app/src/main/java/btools/routingapp/DownloadService.java
+++ /dev/null
@@ -1,487 +0,0 @@
-package btools.routingapp;
-
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Intent;
-import android.net.TrafficStats;
-import android.os.Build;
-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.util.Log;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.FileOutputStream;
-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;
-
- private ServerConfig mServerConfig;
-
- 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;
- mServerConfig = new ServerConfig(getApplicationContext());
-
- 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 {
- availableSize = BInstallerActivity.getAvailableSpace(baseDir);
- //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;
- }
-
- 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 profiles
- 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 = mServerConfig.getSegmentUrl() + 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.setConnectTimeout(5000);
- 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.." + name);
-
- delta = false;
- URL url = new URL(surl);
- connection = (HttpURLConnection) url.openConnection();
- connection.setConnectTimeout(5000);
- 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 (fname.exists()) fname.delete();
- 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 = mServerConfig.getLookups();
- for (String f : sa) {
- if (f.length() > 0) {
- File file = new File(baseDir + "profiles2", f);
- checkOrDownloadLookup(f, file);
- }
- }
-
- sa = mServerConfig.getProfiles();
- 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 = mServerConfig.getLookupUrl() + fileName;
- return downloadScript(url, f);
- }
-
- private String checkOrDownloadScript(String fileName, File f) {
- String url = mServerConfig.getProfilesUrl() + 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);
-
- URL url = new URL(surl);
- connection = (HttpURLConnection) url.openConnection();
- connection.setConnectTimeout(5000);
- 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 < 16 Mbit/s
- long dt = t0 + total / 2096 - 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 BInstallerActivity.downloadCanceled;
- }
-
-}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java
new file mode 100644
index 0000000..0b493fe
--- /dev/null
+++ b/brouter-routing-app/src/main/java/btools/routingapp/DownloadWorker.java
@@ -0,0 +1,327 @@
+package btools.routingapp;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import androidx.work.Data;
+import androidx.work.ForegroundInfo;
+import androidx.work.WorkManager;
+import androidx.work.Worker;
+import androidx.work.WorkerParameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Random;
+
+import btools.mapaccess.PhysicalFile;
+import btools.mapaccess.Rd5DiffManager;
+import btools.mapaccess.Rd5DiffTool;
+import btools.util.ProgressListener;
+
+public class DownloadWorker extends Worker {
+ public static final String KEY_INPUT_SEGMENT_NAMES = "SEGMENT_NAMES";
+ public static final String PROGRESS_SEGMENT_NAME = "PROGRESS_SEGMENT_NAME";
+ public static final String PROGRESS_SEGMENT_PERCENT = "PROGRESS_SEGMENT_PERCENT";
+
+ private final static boolean DEBUG = false;
+ private static final int NOTIFICATION_ID = new Random().nextInt();
+ private static final String PROFILES_DIR = "profiles2/";
+ private static final String SEGMENTS_DIR = "segments4/";
+ private static final String SEGMENT_DIFF_SUFFIX = ".df5";
+ private static final String SEGMENT_SUFFIX = ".rd5";
+ private static final String LOG_TAG = "DownloadWorker";
+
+ private final NotificationManager notificationManager;
+ private final ServerConfig mServerConfig;
+ private final File baseDir;
+ private final ProgressListener diffProgressListener;
+ private final DownloadProgressListener downloadProgressListener;
+ private final Data.Builder progressBuilder = new Data.Builder();
+ private final NotificationCompat.Builder notificationBuilder;
+
+ public DownloadWorker(
+ @NonNull Context context,
+ @NonNull WorkerParameters parameters) {
+ super(context, parameters);
+ notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ mServerConfig = new ServerConfig(context);
+ baseDir = new File(ConfigHelper.getBaseDir(context), "brouter");
+
+ notificationBuilder = createNotificationBuilder();
+
+ downloadProgressListener = new DownloadProgressListener() {
+ private String currentDownloadName;
+ private DownloadType currentDownloadType;
+ private int lastProgressPercent;
+
+ @Override
+ public void onDownloadStart(String downloadName, DownloadType downloadType) {
+ currentDownloadName = downloadName;
+ currentDownloadType = downloadType;
+ if (downloadType == DownloadType.SEGMENT) {
+ progressBuilder.putString(PROGRESS_SEGMENT_NAME, downloadName);
+ notificationBuilder.setContentText(downloadName);
+ }
+ }
+
+ @Override
+ public void onDownloadInfo(String info) {
+ notificationBuilder.setContentText(currentDownloadName + ": " + info);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ }
+
+ @Override
+ public void onDownloadProgress(int max, int progress) {
+ int progressPercent = (int) (progress * 100L / max);
+
+ // Only report segments and update if it changed to avoid hammering NotificationManager
+ if (currentDownloadType != DownloadType.SEGMENT || progressPercent == lastProgressPercent) {
+ return;
+ }
+
+ if (max > 0) {
+ notificationBuilder.setProgress(max, progress, false);
+ progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, progressPercent);
+ } else {
+ notificationBuilder.setProgress(0, 0, true);
+ progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, -1);
+ }
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ setProgressAsync(progressBuilder.build());
+
+ lastProgressPercent = progressPercent;
+ }
+
+ @Override
+ public void onDownloadFinished() {
+ }
+ };
+
+ diffProgressListener = new ProgressListener() {
+ @Override
+ public void updateProgress(String task, int progress) {
+ downloadProgressListener.onDownloadInfo(task);
+ downloadProgressListener.onDownloadProgress(100, progress);
+ }
+
+ @Override
+ public boolean isCanceled() {
+ return isStopped();
+ }
+ };
+ }
+
+ @NonNull
+ @Override
+ public Result doWork() {
+ Data inputData = getInputData();
+ String[] segmentNames = inputData.getStringArray(KEY_INPUT_SEGMENT_NAMES);
+ if (segmentNames == null) {
+ if (DEBUG) Log.d(LOG_TAG, "Failure: no segmentNames");
+ return Result.failure();
+ }
+ notificationBuilder.setContentText("Starting Download");
+ // Mark the Worker as important
+ setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build()));
+ try {
+ if (DEBUG) Log.d(LOG_TAG, "Download lookup & profiles");
+ downloadLookupAndProfiles();
+
+ for (String segmentName : segmentNames) {
+ downloadProgressListener.onDownloadStart(segmentName, DownloadType.SEGMENT);
+ if (DEBUG) Log.d(LOG_TAG, "Download segment " + segmentName);
+ downloadSegment(mServerConfig.getSegmentUrl(), segmentName + SEGMENT_SUFFIX);
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, e);
+ return Result.failure();
+ } catch (InterruptedException e) {
+ Log.w(LOG_TAG, e);
+ return Result.failure();
+ }
+ if (DEBUG) Log.d(LOG_TAG, "doWork finished");
+ return Result.success();
+ }
+
+ private void downloadLookupAndProfiles() throws IOException, InterruptedException {
+ String[] lookups = mServerConfig.getLookups();
+ for (String fileName : lookups) {
+ if (fileName.length() > 0) {
+ File lookupFile = new File(baseDir, PROFILES_DIR + fileName);
+ String lookupLocation = mServerConfig.getLookupUrl() + fileName;
+ URL lookupUrl = new URL(lookupLocation);
+ downloadProgressListener.onDownloadStart(fileName, DownloadType.LOOKUP);
+ downloadFile(lookupUrl, lookupFile, false);
+ downloadProgressListener.onDownloadFinished();
+ }
+ }
+
+ String[] profiles = mServerConfig.getProfiles();
+ for (String fileName : profiles) {
+ if (fileName.length() > 0) {
+ File profileFile = new File(baseDir, PROFILES_DIR + fileName);
+ if (profileFile.exists()) {
+ String profileLocation = mServerConfig.getProfilesUrl() + fileName;
+ URL profileUrl = new URL(profileLocation);
+ downloadProgressListener.onDownloadStart(fileName, DownloadType.PROFILE);
+ downloadFile(profileUrl, profileFile, false);
+ downloadProgressListener.onDownloadFinished();
+ }
+ }
+ }
+ }
+
+ private void downloadSegment(String segmentBaseUrl, String segmentName) throws IOException, InterruptedException {
+ File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName);
+ File segmentFileTemp = new File(segmentFile.getAbsolutePath() + "_tmp");
+ try {
+ if (segmentFile.exists()) {
+ if (DEBUG) Log.d(LOG_TAG, "Calculating local checksum");
+ String md5 = Rd5DiffManager.getMD5(segmentFile);
+ String segmentDeltaLocation = segmentBaseUrl + "diff/" + segmentName.replace(SEGMENT_SUFFIX, "/" + md5 + SEGMENT_DIFF_SUFFIX);
+ URL segmentDeltaUrl = new URL(segmentDeltaLocation);
+ if (httpFileExists(segmentDeltaUrl)) {
+ File segmentDeltaFile = new File(segmentFile.getAbsolutePath() + "_diff");
+ try {
+ downloadFile(segmentDeltaUrl, segmentDeltaFile, true);
+ if (DEBUG) Log.d(LOG_TAG, "Applying delta");
+ Rd5DiffTool.recoverFromDelta(segmentFile, segmentDeltaFile, segmentFileTemp, diffProgressListener);
+ } catch (IOException e) {
+ throw new IOException("Failed to download & apply delta update", e);
+ } finally {
+ segmentDeltaFile.delete();
+ }
+ }
+ }
+
+ if (!segmentFileTemp.exists()) {
+ URL segmentUrl = new URL(segmentBaseUrl + segmentName);
+ downloadFile(segmentUrl, segmentFileTemp, true);
+ }
+
+ PhysicalFile.checkFileIntegrity(segmentFileTemp);
+ if (segmentFile.exists()) {
+ if (!segmentFile.delete()) {
+ throw new IOException("Failed to delete existing " + segmentFile.getAbsolutePath());
+ }
+ }
+
+ if (!segmentFileTemp.renameTo(segmentFile)) {
+ throw new IOException("Failed to write " + segmentFile.getAbsolutePath());
+ }
+ } finally {
+ segmentFileTemp.delete();
+ }
+ }
+
+ private boolean httpFileExists(URL downloadUrl) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
+ connection.setConnectTimeout(5000);
+ connection.setRequestMethod("HEAD");
+ connection.connect();
+
+ return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
+ }
+
+ private void downloadFile(URL downloadUrl, File outputFile, boolean limitDownloadSpeed) throws IOException, InterruptedException {
+ HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
+ connection.setConnectTimeout(5000);
+ connection.connect();
+
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException("HTTP Request failed");
+ }
+ int fileLength = connection.getContentLength();
+ try (
+ InputStream input = connection.getInputStream();
+ OutputStream output = new FileOutputStream(outputFile)
+ ) {
+ byte[] buffer = new byte[4096];
+ int total = 0;
+ long t0 = System.currentTimeMillis();
+ int count;
+ while ((count = input.read(buffer)) != -1) {
+ if (isStopped()) {
+ throw new InterruptedException();
+ }
+ total += count;
+ output.write(buffer, 0, count);
+
+ downloadProgressListener.onDownloadProgress(fileLength, total);
+
+ if (limitDownloadSpeed) {
+ // enforce < 16 Mbit/s
+ long dt = t0 + total / 2096 - System.currentTimeMillis();
+ if (dt > 0) {
+ Thread.sleep(dt);
+ }
+ }
+ }
+ }
+ }
+
+ @NonNull
+ private NotificationCompat.Builder createNotificationBuilder() {
+ Context context = getApplicationContext();
+ String id = context.getString(R.string.notification_channel_id);
+ String title = context.getString(R.string.notification_title);
+ String cancel = context.getString(R.string.cancel_download);
+ // This PendingIntent can be used to cancel the worker
+ PendingIntent intent = WorkManager.getInstance(context)
+ .createCancelPendingIntent(getId());
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createChannel();
+ }
+
+ return new NotificationCompat.Builder(context, id)
+ .setContentTitle(title)
+ .setTicker(title)
+ .setOnlyAlertOnce(true)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setSmallIcon(android.R.drawable.stat_sys_download)
+ .setOngoing(true)
+ // Add the cancel action to the notification which can
+ // be used to cancel the worker
+ .addAction(android.R.drawable.ic_delete, cancel, intent);
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ private void createChannel() {
+ CharSequence name = getApplicationContext().getString(R.string.channel_name);
+ int importance = NotificationManager.IMPORTANCE_LOW;
+ NotificationChannel channel = new NotificationChannel(getApplicationContext().getString(R.string.notification_channel_id), name, importance);
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ notificationManager.createNotificationChannel(channel);
+ }
+
+ enum DownloadType {
+ LOOKUP,
+ PROFILE,
+ SEGMENT
+ }
+
+ interface DownloadProgressListener {
+ void onDownloadStart(String downloadName, DownloadType downloadType);
+
+ void onDownloadInfo(String info);
+
+ void onDownloadProgress(int max, int progress);
+
+ void onDownloadFinished();
+ }
+}
diff --git a/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java b/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
deleted file mode 100644
index a97f296..0000000
--- a/brouter-routing-app/src/main/java/btools/routingapp/NotificationHelper.java
+++ /dev/null
@@ -1,134 +0,0 @@
-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);
- }
-}
diff --git a/brouter-routing-app/src/main/res/layout/activity_binstaller.xml b/brouter-routing-app/src/main/res/layout/activity_binstaller.xml
index 11e4a84..5f0e7c1 100644
--- a/brouter-routing-app/src/main/res/layout/activity_binstaller.xml
+++ b/brouter-routing-app/src/main/res/layout/activity_binstaller.xml
@@ -1,71 +1,45 @@
-
+ android:layout_height="match_parent">
-
+
+
+
+
+
+
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/brouter-routing-app/src/main/res/values/strings.xml b/brouter-routing-app/src/main/res/values/strings.xml
index b7d0d46..6663c01 100644
--- a/brouter-routing-app/src/main/res/values/strings.xml
+++ b/brouter-routing-app/src/main/res/values/strings.xml
@@ -30,4 +30,7 @@
Update %s
Select segments
Size=%s\nFree=%s
+ brouter_download
+ Download Segments
+ Downloads
diff --git a/brouter-routing-app/src/main/res/values/styles.xml b/brouter-routing-app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..2d2ee81
--- /dev/null
+++ b/brouter-routing-app/src/main/res/values/styles.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/brouter-util/src/main/java/btools/util/ProgressListener.java b/brouter-util/src/main/java/btools/util/ProgressListener.java
index eda4efe..4a6d473 100644
--- a/brouter-util/src/main/java/btools/util/ProgressListener.java
+++ b/brouter-util/src/main/java/btools/util/ProgressListener.java
@@ -1,8 +1,9 @@
package btools.util;
+
public interface ProgressListener
{
- public void updateProgress( String progress );
+ public void updateProgress(String task, int progress);
public boolean isCanceled();
}