Merge pull request #413 from zod/download-worker
Use WorkManager for Downloads
This commit is contained in:
commit
4794b28774
20 changed files with 659 additions and 948 deletions
|
@ -15,7 +15,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
private int latBase;
|
private int latBase;
|
||||||
private int cellsize;
|
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
|
super( databuffer ); // sets ab=databuffer, aboffset=0
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
return b;
|
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 );
|
super( null );
|
||||||
cellsize = 1000000 / divisor;
|
cellsize = 1000000 / divisor;
|
||||||
|
|
|
@ -20,7 +20,7 @@ public final class DirectWeaver extends ByteDataWriter
|
||||||
|
|
||||||
private int size = 0;
|
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 );
|
super( null );
|
||||||
int cellsize = 1000000 / divisor;
|
int cellsize = 1000000 / divisor;
|
||||||
|
|
|
@ -35,7 +35,7 @@ final class OsmFile
|
||||||
private int ncaches;
|
private int ncaches;
|
||||||
private int indexsize;
|
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.lonDegree = lonDegree;
|
||||||
this.latDegree = latDegree;
|
this.latDegree = latDegree;
|
||||||
|
@ -111,7 +111,7 @@ final class OsmFile
|
||||||
return idx == -1 ? indexsize : posIdx[idx];
|
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 startPos = getPosIdx( subIdx - 1 );
|
||||||
int endPos = getPosIdx( subIdx );
|
int endPos = getPosIdx( subIdx );
|
||||||
|
@ -128,7 +128,7 @@ final class OsmFile
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache createMicroCache( int lonIdx, int latIdx, DataBuffers dataBuffers, TagValueValidator wayValidator,
|
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 );
|
int subIdx = ( latIdx - divisor * latDegree ) * divisor + ( lonIdx - divisor * lonDegree );
|
||||||
|
|
||||||
|
|
|
@ -31,13 +31,12 @@ final public class PhysicalFile
|
||||||
{
|
{
|
||||||
MicroCache.debug = true;
|
MicroCache.debug = true;
|
||||||
|
|
||||||
String message = checkFileIntegrity( new File( args[0] ) );
|
try {
|
||||||
|
checkFileIntegrity( new File( args[0] ) );
|
||||||
if ( message != null )
|
} catch (IOException e) {
|
||||||
{
|
System.err.println( "************************************" );
|
||||||
System.out.println( "************************************" );
|
e.printStackTrace();
|
||||||
System.out.println( message );
|
System.err.println( "************************************" );
|
||||||
System.out.println( "************************************" );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +45,7 @@ final public class PhysicalFile
|
||||||
*
|
*
|
||||||
* @return the error message if file corrupt, else null
|
* @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;
|
PhysicalFile pf = null;
|
||||||
try
|
try
|
||||||
|
@ -66,14 +65,6 @@ final public class PhysicalFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException iae)
|
|
||||||
{
|
|
||||||
return iae.getMessage();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return e.toString();
|
|
||||||
}
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if ( pf != null )
|
if ( pf != null )
|
||||||
|
@ -88,7 +79,7 @@ final public class PhysicalFile
|
||||||
return null;
|
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();
|
fileName = f.getName();
|
||||||
byte[] iobuffer = dataBuffers.iobuffer;
|
byte[] iobuffer = dataBuffers.iobuffer;
|
||||||
|
@ -102,7 +93,7 @@ final public class PhysicalFile
|
||||||
short readVersion = (short)(lv >> 48);
|
short readVersion = (short)(lv >> 48);
|
||||||
if ( i == 0 && lookupVersion != -1 && readVersion != lookupVersion )
|
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 );
|
+ lookupVersion + " " + f. getAbsolutePath() + "=" + readVersion );
|
||||||
}
|
}
|
||||||
fileIndex[i] = lv & 0xffffffffffffL;
|
fileIndex[i] = lv & 0xffffffffffffL;
|
||||||
|
|
|
@ -8,8 +8,10 @@ package btools.mapaccess;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
final public class Rd5DiffManager
|
final public class Rd5DiffManager
|
||||||
{
|
{
|
||||||
|
@ -93,17 +95,16 @@ final public class Rd5DiffManager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMD5( File f ) throws Exception
|
public static String getMD5( File f ) throws IOException
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
|
||||||
DigestInputStream dis = new DigestInputStream(bis, md);
|
DigestInputStream dis = new DigestInputStream(bis, md);
|
||||||
byte[] buf = new byte[8192];
|
byte[] buf = new byte[8192];
|
||||||
for(;;)
|
for (; ; ) {
|
||||||
{
|
|
||||||
int len = dis.read(buf);
|
int len = dis.read(buf);
|
||||||
if ( len <= 0 )
|
if (len <= 0) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,12 +112,14 @@ final public class Rd5DiffManager
|
||||||
byte[] bytes = md.digest();
|
byte[] bytes = md.digest();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (int j = 0; j < bytes.length; j++)
|
for (int j = 0; j < bytes.length; j++) {
|
||||||
{
|
|
||||||
int v = bytes[j] & 0xff;
|
int v = bytes[j] & 0xff;
|
||||||
sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
|
sb.append(hexChar(v >>> 4)).append(hexChar(v & 0xf));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new IOException("MD5 algorithm not available", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char hexChar( int v )
|
private static char hexChar( int v )
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import btools.codec.DataBuffers;
|
import btools.codec.DataBuffers;
|
||||||
|
@ -49,9 +50,8 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateProgress( String progress )
|
public void updateProgress(String task, int progress) {
|
||||||
{
|
System.out.println(task + ": " + progress + "%");
|
||||||
System.out.println( progress );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return false;
|
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];
|
long[] fileIndex = new long[25];
|
||||||
for( int i=0; i<25; i++ )
|
for( int i=0; i<25; i++ )
|
||||||
|
@ -85,7 +85,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return index[tileIndex];
|
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];
|
int[] posIndex = new int[1024];
|
||||||
for( int i=0; i<1024; i++ )
|
for( int i=0; i<1024; i++ )
|
||||||
|
@ -105,7 +105,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return idx == -1 ? 4096 : posIdx[idx];
|
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 )
|
if ( posIdx == null )
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return ab;
|
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 )
|
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 )
|
if ( f2.length() == 0L )
|
||||||
{
|
{
|
||||||
|
@ -341,7 +341,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
|
int pct = (int)(100. * bytesProcessed / getTileEnd( fileIndex1, 24 ) + 0.5 );
|
||||||
if ( pct != lastPct )
|
if ( pct != lastPct )
|
||||||
{
|
{
|
||||||
progress.updateProgress( "Applying delta: " + pct + "%" );
|
progress.updateProgress("Applying delta", pct);
|
||||||
lastPct = 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;
|
boolean canceled = false;
|
||||||
DataInputStream dis1 = new DataInputStream( new BufferedInputStream( new FileInputStream( f1 ) ) );
|
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 );
|
int pct = (int)( (100. * sizeRead) / (sizeTotal+1) + 0.5 );
|
||||||
if ( pct != lastPct )
|
if ( pct != lastPct )
|
||||||
{
|
{
|
||||||
progress.updateProgress( "Copying: " + pct + "%" );
|
progress.updateProgress("Copying", pct);
|
||||||
lastPct = pct;
|
lastPct = pct;
|
||||||
}
|
}
|
||||||
int len = dis1.read( buf );
|
int len = dis1.read( buf );
|
||||||
|
@ -756,7 +756,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
this.dataBuffers = dataBuffers;
|
this.dataBuffers = dataBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache readMC() throws Exception
|
public MicroCache readMC() throws IOException
|
||||||
{
|
{
|
||||||
if (skips < 0 )
|
if (skips < 0 )
|
||||||
{
|
{
|
||||||
|
@ -775,7 +775,7 @@ final public class Rd5DiffTool implements ProgressListener
|
||||||
return mc;
|
return mc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finish() throws Exception
|
public void finish()
|
||||||
{
|
{
|
||||||
skips = -1;
|
skips = -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "btools.routingapp"
|
applicationId "btools.routingapp"
|
||||||
|
@ -64,7 +64,8 @@ android {
|
||||||
}
|
}
|
||||||
lintOptions {
|
lintOptions {
|
||||||
disable 'InvalidPackage'
|
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 {
|
compileOptions {
|
||||||
|
@ -93,9 +94,10 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.4.1'
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
|
implementation 'androidx.work:work-runtime:2.7.1'
|
||||||
|
implementation 'com.google.android.material:material:1.5.0'
|
||||||
|
|
||||||
implementation project(':brouter-mapaccess')
|
implementation project(':brouter-mapaccess')
|
||||||
implementation project(':brouter-core')
|
implementation project(':brouter-core')
|
||||||
|
@ -106,6 +108,7 @@ dependencies {
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
androidTestImplementation 'androidx.work:work-testing:2.7.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
task generateProfiles(type: Exec) {
|
task generateProfiles(type: Exec) {
|
||||||
|
@ -115,8 +118,7 @@ task generateProfiles(type: Exec) {
|
||||||
task generateProfilesZip(type: Zip) {
|
task generateProfilesZip(type: Zip) {
|
||||||
if (DefaultNativePlatform.getCurrentOperatingSystem().isWindows()) {
|
if (DefaultNativePlatform.getCurrentOperatingSystem().isWindows()) {
|
||||||
logger.warn("Note: On Windows run script '../misc/scripts/generate_profile_variants.sh' manually to include all profiles")
|
logger.warn("Note: On Windows run script '../misc/scripts/generate_profile_variants.sh' manually to include all profiles")
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
dependsOn generateProfiles
|
dependsOn generateProfiles
|
||||||
}
|
}
|
||||||
archiveFileName = "profiles2.zip"
|
archiveFileName = "profiles2.zip"
|
||||||
|
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,9 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="btools.routingapp">
|
package="btools.routingapp">
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
tools:ignore="ScopedStorage" />
|
tools:ignore="ScopedStorage" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
@ -11,17 +13,16 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:preserveLegacyExternalStorage="true">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:preserveLegacyExternalStorage="true"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:theme="@style/Theme.App">
|
||||||
<activity
|
<activity
|
||||||
android:name=".BRouterActivity"
|
android:name=".BRouterActivity"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:screenOrientation="unspecified"
|
android:screenOrientation="unspecified">
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
@ -29,20 +30,18 @@
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".BInstallerActivity"
|
android:name=".BInstallerActivity"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:screenOrientation="landscape"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"></activity>
|
android:launchMode="singleTask"
|
||||||
|
android:screenOrientation="landscape" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".BImportActivity"
|
android:name=".BImportActivity"
|
||||||
android:label="Import Profile"
|
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar">
|
android:label="Import Profile">
|
||||||
<!-- some apps (bluemail) do not recognize the .brf file extention, startactivity+intent is done for attachement with text/plain -->
|
<!-- some apps (bluemail) do not recognize the .brf file extension, startactivity+intent is done for attachment with text/plain -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
<data android:mimeType="text/plain" />
|
<data android:mimeType="text/plain" />
|
||||||
|
@ -88,15 +87,10 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:exported="true"
|
|
||||||
android:name=".BRouterService"
|
android:name=".BRouterService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
android:process=":brouter_service" />
|
android:process=":brouter_service" />
|
||||||
<service
|
|
||||||
android:name="btools.routingapp.DownloadService"
|
|
||||||
android:label="Download Service"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:enabled="true" />
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
@ -12,6 +11,7 @@ import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -20,7 +20,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
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
|
// profile size is generally < 30 kb, so set max size to 100 kb
|
||||||
private static final int MAX_PROFILE_SIZE = 100000;
|
private static final int MAX_PROFILE_SIZE = 100000;
|
||||||
private EditText mTextFilename;
|
private EditText mTextFilename;
|
||||||
|
@ -76,8 +76,8 @@ public class BImportActivity extends Activity {
|
||||||
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
|
try (Cursor cursor = this.getContentResolver().query(intent.getData(), new String[]{
|
||||||
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
|
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
filename = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
filename = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
|
||||||
filesize = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
|
filesize = cursor.getLong(cursor.getColumnIndexOrThrow(OpenableColumns.SIZE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// is the file extention ".brf" in the file name
|
// is the file extention ".brf" in the file name
|
||||||
|
|
|
@ -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_INSTALLED_RD5;
|
||||||
import static btools.routingapp.BInstallerView.MASK_SELECTED_RD5;
|
import static btools.routingapp.BInstallerView.MASK_SELECTED_RD5;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.StatFs;
|
import android.os.StatFs;
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
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.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -29,20 +35,15 @@ import java.util.Locale;
|
||||||
|
|
||||||
import btools.router.RoutingHelper;
|
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;
|
private static final int DIALOG_CONFIRM_DELETE_ID = 1;
|
||||||
public static boolean downloadCanceled = false;
|
public static boolean downloadCanceled = false;
|
||||||
private File mBaseDir;
|
private File mBaseDir;
|
||||||
private BInstallerView mBInstallerView;
|
private BInstallerView mBInstallerView;
|
||||||
private DownloadReceiver downloadReceiver;
|
|
||||||
private View mDownloadInfo;
|
|
||||||
private TextView mDownloadInfoText;
|
|
||||||
private Button mButtonDownloadCancel;
|
|
||||||
private Button mButtonDownload;
|
private Button mButtonDownload;
|
||||||
private TextView mSummaryInfo;
|
private TextView mSummaryInfo;
|
||||||
private View mSegmentsView;
|
private LinearProgressIndicator mProgressIndicator;
|
||||||
|
|
||||||
public static long getAvailableSpace(String baseDir) {
|
public static long getAvailableSpace(String baseDir) {
|
||||||
StatFs stat = new StatFs(baseDir);
|
StatFs stat = new StatFs(baseDir);
|
||||||
|
@ -63,7 +64,6 @@ public class BInstallerActivity extends Activity {
|
||||||
|
|
||||||
setContentView(R.layout.activity_binstaller);
|
setContentView(R.layout.activity_binstaller);
|
||||||
mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
|
mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
|
||||||
mSegmentsView = findViewById(R.id.view_segments);
|
|
||||||
mBInstallerView = findViewById(R.id.BInstallerView);
|
mBInstallerView = findViewById(R.id.BInstallerView);
|
||||||
mBInstallerView.setOnSelectListener(
|
mBInstallerView.setOnSelectListener(
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -82,12 +82,7 @@ public class BInstallerActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
mDownloadInfo = findViewById(R.id.view_download_progress);
|
mProgressIndicator = findViewById(R.id.progressDownload);
|
||||||
mDownloadInfoText = findViewById(R.id.textViewDownloadProgress);
|
|
||||||
mButtonDownloadCancel = findViewById(R.id.buttonDownloadCancel);
|
|
||||||
mButtonDownloadCancel.setOnClickListener(view -> {
|
|
||||||
cancelDownload();
|
|
||||||
});
|
|
||||||
|
|
||||||
mBaseDir = ConfigHelper.getBaseDir(this);
|
mBaseDir = ConfigHelper.getBaseDir(this);
|
||||||
scanExistingFiles();
|
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<Integer> downloadList) {
|
public void downloadAll(ArrayList<Integer> downloadList) {
|
||||||
ArrayList<String> urlparts = new ArrayList<>();
|
ArrayList<String> urlparts = new ArrayList<>();
|
||||||
for (Integer i : downloadList) {
|
for (Integer i : downloadList) {
|
||||||
urlparts.add(baseNameForTile(i));
|
urlparts.add(baseNameForTile(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
mSegmentsView.setVisibility(View.GONE);
|
|
||||||
mDownloadInfo.setVisibility(View.VISIBLE);
|
|
||||||
downloadCanceled = false;
|
downloadCanceled = false;
|
||||||
mDownloadInfoText.setText(R.string.download_info_start);
|
|
||||||
|
|
||||||
Intent intent = new Intent(this, DownloadService.class);
|
Data inputData = new Data.Builder()
|
||||||
intent.putExtra("dir", mBaseDir.getAbsolutePath() + "/brouter/");
|
.putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0]))
|
||||||
intent.putExtra("urlparts", urlparts);
|
.build();
|
||||||
startService(intent);
|
|
||||||
|
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
|
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
|
@Override
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
AlertDialog.Builder builder;
|
AlertDialog.Builder builder;
|
||||||
|
@ -278,21 +297,4 @@ public class BInstallerActivity extends Activity {
|
||||||
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
||||||
return slon + "_" + slat;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,24 @@
|
||||||
package btools.routingapp;
|
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.io.File;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -9,36 +26,9 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
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;
|
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_SELECTPROFILE_ID = 1;
|
||||||
private static final int DIALOG_EXCEPTION_ID = 2;
|
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) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
|
Intent intent = new Intent(BRouterActivity.this, BInstallerActivity.class);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
// finish();
|
showNewDialog(DIALOG_MAINACTION_ID);
|
||||||
}
|
}
|
||||||
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
}).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
@ -391,39 +381,12 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
|
||||||
|
|
||||||
private String maptoolDirCandidate;
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
public void selectProfile(String[] items) {
|
public void selectProfile(String[] items) {
|
||||||
availableProfiles = items;
|
availableProfiles = items;
|
||||||
|
|
||||||
// if we have internet access, first show the main action dialog
|
// show main dialog
|
||||||
if (isOnline(this)) {
|
|
||||||
showDialog(DIALOG_MAINACTION_ID);
|
showDialog(DIALOG_MAINACTION_ID);
|
||||||
} else {
|
|
||||||
showDialog(DIALOG_SELECTPROFILE_ID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@ -626,6 +589,7 @@ public class BRouterActivity extends Activity implements ActivityCompat.OnReques
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
if (requestCode == 0) {
|
if (requestCode == 0) {
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
mBRouterView.startSetup(null, true);
|
mBRouterView.startSetup(null, true);
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
package btools.routingapp;
|
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.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -12,36 +28,13 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
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.expressions.BExpressionMetaData;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.router.OsmNodeNamed;
|
import btools.router.OsmNodeNamed;
|
||||||
|
@ -108,11 +101,6 @@ public class BRouterView extends View {
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
try {
|
try {
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
|
||||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
|
||||||
imgw = metrics.widthPixels;
|
|
||||||
imgh = metrics.heightPixels;
|
|
||||||
|
|
||||||
// get base dir from private file
|
// get base dir from private file
|
||||||
File baseDir = ConfigHelper.getBaseDir(getContext());
|
File baseDir = ConfigHelper.getBaseDir(getContext());
|
||||||
// check if valid
|
// check if valid
|
||||||
|
@ -703,6 +691,8 @@ public class BRouterView extends View {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
imgw = w;
|
||||||
|
imgh = h;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toast(String msg) {
|
private void toast(String msg) {
|
||||||
|
|
|
@ -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<String> 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<String> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/view_segments"
|
android:id="@+id/view_segments"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<btools.routingapp.BInstallerView
|
<btools.routingapp.BInstallerView
|
||||||
android:id="@+id/BInstallerView"
|
android:id="@+id/BInstallerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
@ -38,34 +33,13 @@
|
||||||
android:textColor="@android:color/primary_text_light"
|
android:textColor="@android:color/primary_text_light"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
android:id="@+id/progressDownload"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/view_download_progress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textViewDownloadProgress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="20sp" />
|
|
||||||
|
|
||||||
<Space
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/buttonDownloadCancel"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:text="@string/cancel_download" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
|
@ -30,4 +30,7 @@
|
||||||
<string name="action_update">Update %s</string>
|
<string name="action_update">Update %s</string>
|
||||||
<string name="action_select">Select segments</string>
|
<string name="action_select">Select segments</string>
|
||||||
<string name="summary_segments">Size=%s\nFree=%s</string>
|
<string name="summary_segments">Size=%s\nFree=%s</string>
|
||||||
|
<string name="notification_channel_id">brouter_download</string>
|
||||||
|
<string name="notification_title">Download Segments</string>
|
||||||
|
<string name="channel_name">Downloads</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
10
brouter-routing-app/src/main/res/values/styles.xml
Normal file
10
brouter-routing-app/src/main/res/values/styles.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<style name="Theme.App" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="linearProgressIndicatorStyle">@style/Widget.App.LinearProgressIndicator</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.App.LinearProgressIndicator" parent="Widget.MaterialComponents.LinearProgressIndicator">
|
||||||
|
<item name="trackThickness">20dp</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
|
@ -1,8 +1,9 @@
|
||||||
package btools.util;
|
package btools.util;
|
||||||
|
|
||||||
|
|
||||||
public interface ProgressListener
|
public interface ProgressListener
|
||||||
{
|
{
|
||||||
public void updateProgress( String progress );
|
public void updateProgress(String task, int progress);
|
||||||
|
|
||||||
public boolean isCanceled();
|
public boolean isCanceled();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue