Merge pull request #511 from afischerdev/app-update

App update
This commit is contained in:
afischerdev 2023-03-20 16:59:13 +01:00 committed by GitHub
commit fc22892a66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 552 additions and 142 deletions

View file

@ -352,7 +352,7 @@ public class RoutingEngine extends Thread {
} }
private void logException(Throwable t) { private void logException(Throwable t) {
errorMessage = t instanceof IllegalArgumentException ? t.getMessage() : t.toString(); errorMessage = t instanceof RuntimeException ? t.getMessage() : t.toString();
logInfo("Error (linksProcessed=" + linksProcessed + " open paths: " + openSet.getSize() + "): " + errorMessage); logInfo("Error (linksProcessed=" + linksProcessed + " open paths: " + openSet.getSize() + "): " + errorMessage);
} }

View file

@ -187,6 +187,8 @@ public final class NodesCache {
ghostWakeup += segment.getDataSize(); ghostWakeup += segment.getDataSize();
} }
return segment; return segment;
} catch (IOException re) {
throw new RuntimeException(re.getMessage());
} catch (RuntimeException re) { } catch (RuntimeException re) {
throw re; throw re;
} catch (Exception e) { } catch (Exception e) {
@ -294,14 +296,14 @@ public final class NodesCache {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
MatchedWaypoint mwp = unmatchedWaypoints.get(i); MatchedWaypoint mwp = unmatchedWaypoints.get(i);
if (mwp.crosspoint == null) { if (mwp.crosspoint == null) {
if (unmatchedWaypoints.size() > 1 && i == unmatchedWaypoints.size()-1 && unmatchedWaypoints.get(i-1).direct) { if (unmatchedWaypoints.size() > 1 && i == unmatchedWaypoints.size() - 1 && unmatchedWaypoints.get(i - 1).direct) {
mwp.crosspoint = new OsmNode(mwp.waypoint.ilon, mwp.waypoint.ilat); mwp.crosspoint = new OsmNode(mwp.waypoint.ilon, mwp.waypoint.ilat);
mwp.direct = true; mwp.direct = true;
} else { } else {
throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile"); throw new IllegalArgumentException(mwp.name + "-position not mapped in existing datafile");
} }
} }
if (unmatchedWaypoints.size() > 1 && i == unmatchedWaypoints.size()-1 && unmatchedWaypoints.get(i-1).direct) { if (unmatchedWaypoints.size() > 1 && i == unmatchedWaypoints.size() - 1 && unmatchedWaypoints.get(i - 1).direct) {
mwp.crosspoint = new OsmNode(mwp.waypoint.ilon, mwp.waypoint.ilat); mwp.crosspoint = new OsmNode(mwp.waypoint.ilon, mwp.waypoint.ilat);
mwp.direct = true; mwp.direct = true;
} }

View file

@ -37,6 +37,27 @@ final public class PhysicalFile {
} }
} }
public static int checkVersionIntegrity(File f) {
int version = -1;
RandomAccessFile raf = null;
try {
byte[] iobuffer = new byte[200];
raf = new RandomAccessFile(f, "r");
raf.readFully(iobuffer, 0, 200);
ByteDataReader dis = new ByteDataReader(iobuffer);
long lv = dis.readLong();
version = (int) (lv >> 48);
} catch (IOException e) {
} finally {
try {
if (raf != null) raf.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return version;
}
/** /**
* Checks the integrity of the file using the build-in checksums * Checks the integrity of the file using the build-in checksums
* *
@ -81,7 +102,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 IOException("lookup version mismatch (old rd5?) lookups.dat=" throw new IOException("lookup version mismatch (old rd5?) lookups.dat="
+ lookupVersion + " " + f.getAbsolutePath() + "=" + readVersion); + lookupVersion + " " + f.getName() + "=" + readVersion);
} }
fileIndex[i] = lv & 0xffffffffffffL; fileIndex[i] = lv & 0xffffffffffffL;
} }

View file

@ -5,9 +5,10 @@ plugins {
} }
android { android {
compileSdkVersion 31 compileSdkVersion 33
defaultConfig { defaultConfig {
namespace 'btools.routingapp'
applicationId "btools.routingapp" applicationId "btools.routingapp"
versionCode 45 versionCode 45
@ -17,7 +18,7 @@ android {
setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName) setProperty("archivesBaseName", "BRouterApp." + defaultConfig.versionName)
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 30 targetSdkVersion 33
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@ -84,10 +85,10 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "androidx.constraintlayout:constraintlayout:2.1.3" implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation 'androidx.work:work-runtime:2.7.1' implementation 'androidx.work:work-runtime:2.8.0'
implementation 'com.google.android.material:material:1.5.0' implementation 'com.google.android.material:material:1.8.0'
implementation project(':brouter-mapaccess') implementation project(':brouter-mapaccess')
implementation project(':brouter-core') implementation project(':brouter-core')
@ -96,9 +97,13 @@ dependencies {
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.work:work-testing:2.7.1' androidTestImplementation 'androidx.work:work-testing:2.8.0'
}
gradle.projectsEvaluated {
preBuild.dependsOn(generateProfilesZip, generateReadmesZip)
} }
check.dependsOn 'checkstyle' check.dependsOn 'checkstyle'

View file

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="btools.routingapp"> >
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application <application
android:allowBackup="false" android:allowBackup="false"

View file

@ -5,22 +5,30 @@ 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.Manifest;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
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.util.Log;
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 android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LiveData;
import androidx.work.Constraints; import androidx.work.Constraints;
import androidx.work.Data; import androidx.work.Data;
import androidx.work.ExistingWorkPolicy;
import androidx.work.NetworkType; import androidx.work.NetworkType;
import androidx.work.OneTimeWorkRequest; import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo; import androidx.work.WorkInfo;
@ -28,23 +36,36 @@ import androidx.work.WorkManager;
import androidx.work.WorkRequest; import androidx.work.WorkRequest;
import com.google.android.material.progressindicator.LinearProgressIndicator; import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutionException;
import btools.router.RoutingHelper; import btools.router.RoutingHelper;
public class BInstallerActivity extends AppCompatActivity { public class BInstallerActivity extends AppCompatActivity {
private static final int DIALOG_CONFIRM_DELETE_ID = 1; private static final int DIALOG_CONFIRM_DELETE_ID = 1;
private static final int DIALOG_CONFIRM_NEXTSTEPS_ID = 2;
private static final int DIALOG_CONFIRM_GETDIFFS_ID = 3;
public static final int MY_PERMISSIONS_REQUEST_NITIFICATION = 100;
public static boolean downloadCanceled = false; public static boolean downloadCanceled = false;
private File mBaseDir; private File mBaseDir;
private BInstallerView mBInstallerView; private BInstallerView mBInstallerView;
private Button mButtonDownload; private Button mButtonDownload;
private TextView mSummaryInfo; private TextView mSummaryInfo;
private TextView mDownloadSummaryInfo;
private LinearProgressIndicator mProgressIndicator; private LinearProgressIndicator mProgressIndicator;
private ArrayList<Integer> selectedTiles;
BInstallerView.OnSelectListener onSelectListener;
@SuppressWarnings("deprecation")
public static long getAvailableSpace(String baseDir) { public static long getAvailableSpace(String baseDir) {
StatFs stat = new StatFs(baseDir); StatFs stat = new StatFs(baseDir);
@ -60,32 +81,78 @@ public class BInstallerActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
boolean running = isDownloadRunning(DownloadWorker.class);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.activity_binstaller); setContentView(R.layout.activity_binstaller);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED) {
// nothing to do
}
if (shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
//
} else {
// You can directly ask for the permission.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, MY_PERMISSIONS_REQUEST_NITIFICATION);
}
}
mSummaryInfo = findViewById(R.id.textViewSegmentSummary); mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
mBInstallerView = findViewById(R.id.BInstallerView); mBInstallerView = findViewById(R.id.BInstallerView);
mBInstallerView.setOnSelectListener( onSelectListener = new BInstallerView.OnSelectListener() {
() -> { @Override
public void onSelect() {
//if (!isDownloadRunning(DownloadWorker.class))
updateDownloadButton(); updateDownloadButton();
} }
); };
mBInstallerView.setOnSelectListener(onSelectListener);
mButtonDownload = findViewById(R.id.buttonDownload); mButtonDownload = findViewById(R.id.buttonDownload);
mButtonDownload.setOnClickListener( mButtonDownload.setOnClickListener(
view -> { view -> {
if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) { if (isDownloadRunning(DownloadWorker.class)) {
stopDownload();
} else if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) {
showConfirmDelete(); showConfirmDelete();
} else if (mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5).size() > 0) { } else if (mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5).size() > 0) {
mBInstallerView.setOnSelectListener(null);
downloadSelectedTiles(); downloadSelectedTiles();
} else { } else {
mBInstallerView.setOnSelectListener(null);
downloadInstalledTiles(); downloadInstalledTiles();
} }
} }
); );
mProgressIndicator = findViewById(R.id.progressDownload); mProgressIndicator = findViewById(R.id.progressDownload);
mDownloadSummaryInfo = findViewById(R.id.textViewDownloadSummary);
mDownloadSummaryInfo.setVisibility(View.INVISIBLE);
mBaseDir = ConfigHelper.getBaseDir(this); mBaseDir = ConfigHelper.getBaseDir(this);
scanExistingFiles();
if (running) {
mProgressIndicator.show();
mButtonDownload.setEnabled(false);
WorkManager instance = WorkManager.getInstance(getApplicationContext());
LiveData<List<WorkInfo>> ld = instance.getWorkInfosForUniqueWorkLiveData(DownloadWorker.WORKER_NAME);
ld.observe(this, listOfWorkInfo -> {
// If there are no matching work info, do nothing
if (listOfWorkInfo == null || listOfWorkInfo.isEmpty()) {
return;
}
for (WorkInfo workInfo : listOfWorkInfo) {
startObserver(workInfo);
}
});
} else {
scanExistingFiles();
}
} }
private String getSegmentsPlural(int count) { private String getSegmentsPlural(int count) {
@ -131,19 +198,24 @@ public class BInstallerActivity extends AppCompatActivity {
} }
} }
public void downloadAll(ArrayList<Integer> downloadList) { public void downloadAll(ArrayList<Integer> downloadList, int all) {
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));
} }
downloadCanceled = false; downloadCanceled = false;
mProgressIndicator.show();
mButtonDownload.setEnabled(false);
Data inputData = new Data.Builder() Data inputData = new Data.Builder()
.putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0])) .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0]))
.putInt(DownloadWorker.KEY_INPUT_SEGMENT_ALL, all)
.build(); .build();
Constraints constraints = new Constraints.Builder() Constraints constraints = new Constraints.Builder()
.setRequiresBatteryNotLow(true)
.setRequiresStorageNotLow(true)
.setRequiredNetworkType(NetworkType.CONNECTED) .setRequiredNetworkType(NetworkType.CONNECTED)
.build(); .build();
@ -154,65 +226,112 @@ public class BInstallerActivity extends AppCompatActivity {
.build(); .build();
WorkManager workManager = WorkManager.getInstance(getApplicationContext()); WorkManager workManager = WorkManager.getInstance(getApplicationContext());
workManager.enqueue(downloadWorkRequest); workManager.enqueueUniqueWork(DownloadWorker.WORKER_NAME, ExistingWorkPolicy.KEEP, (OneTimeWorkRequest) downloadWorkRequest);
try {
WorkInfo wi = WorkManager.getInstance(getApplicationContext()).getWorkInfoById(downloadWorkRequest.getId()).get();
if (wi != null && (wi.getState() == WorkInfo.State.ENQUEUED || wi.getState() == WorkInfo.State.BLOCKED)) {
Log.d("worker", "cancel " + wi.getState());
//WorkManager.getInstance(getApplicationContext()).cancelWorkById(downloadWorkRequest.getId());
}
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
Log.d("worker", "canceled " + e.getMessage());
//e.printStackTrace();
}
workManager workManager
.getWorkInfoByIdLiveData(downloadWorkRequest.getId()) .getWorkInfoByIdLiveData(downloadWorkRequest.getId())
.observe(this, workInfo -> { .observe(this, workInfo -> {
if (workInfo != null) { startObserver(workInfo);
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 = "Download failed";
break;
case CANCELLED:
result = "Download cancelled";
break;
case SUCCEEDED:
result = "Download succeeded";
break;
default:
result = "";
}
if (workInfo.getState() != WorkInfo.State.FAILED) {
Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
} else {
String error = workInfo.getOutputData().getString(DownloadWorker.KEY_OUTPUT_ERROR);
Toast.makeText(this, result + ": " + error, Toast.LENGTH_LONG).show();
}
mProgressIndicator.hide();
scanExistingFiles();
}
}
}); });
deleteRawTracks(); // invalidate raw-tracks after data update deleteRawTracks(); // invalidate raw-tracks after data update
} }
@Override private void startObserver(WorkInfo workInfo) {
protected Dialog onCreateDialog(int id) { if (workInfo != null) {
if (workInfo.getState() == WorkInfo.State.ENQUEUED || workInfo.getState() == WorkInfo.State.BLOCKED) {
Log.d("worker", "cancel " + workInfo.getState());
//WorkManager.getInstance(getApplicationContext()).cancelWorkById(downloadWorkRequest.getId());
}
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) {
mDownloadSummaryInfo.setVisibility(View.VISIBLE);
Data progress = workInfo.getProgress();
String segmentName = progress.getString(DownloadWorker.PROGRESS_SEGMENT_NAME);
int percent = progress.getInt(DownloadWorker.PROGRESS_SEGMENT_PERCENT, 0);
if (percent > 0) {
mDownloadSummaryInfo.setText("Downloading .. " + segmentName);
}
if (percent > 0) {
mProgressIndicator.setIndeterminate(false);
}
mProgressIndicator.setProgress(percent);
mButtonDownload.setText(getString(R.string.action_cancel));
mButtonDownload.setEnabled(true);
}
if (workInfo.getState().isFinished()) {
String result;
switch (workInfo.getState()) {
case FAILED:
result = "Download failed";
break;
case CANCELLED:
result = "Download cancelled";
break;
case SUCCEEDED:
result = "Download succeeded";
break;
default:
result = "";
}
String error = null;
if (workInfo.getState() != WorkInfo.State.FAILED) {
Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
} else {
error = workInfo.getOutputData().getString(DownloadWorker.KEY_OUTPUT_ERROR);
if (error != null && !error.startsWith("Version")) {
Toast.makeText(this, result + ": " + error, Toast.LENGTH_LONG).show();
}
}
if (error != null && error.startsWith("Version error")) {
showConfirmNextSteps();
} else if (error != null && error.startsWith("Version diffs")) {
showConfirmGetDiffs();
} else {
mBInstallerView.setOnSelectListener(onSelectListener);
mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5);
scanExistingFiles();
}
mProgressIndicator.hide();
mDownloadSummaryInfo.setVisibility(View.INVISIBLE);
mButtonDownload.setEnabled(true);
}
}
}
protected Dialog createADialog(int id) {
AlertDialog.Builder builder; AlertDialog.Builder builder;
builder = new AlertDialog.Builder(this);
builder.setCancelable(false);
switch (id) { switch (id) {
case DIALOG_CONFIRM_DELETE_ID: case DIALOG_CONFIRM_DELETE_ID:
builder = new AlertDialog.Builder(this);
builder builder
.setTitle("Confirm Delete") .setTitle("Confirm Delete")
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() { .setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@ -225,15 +344,77 @@ public class BInstallerActivity extends AppCompatActivity {
}); });
return builder.create(); return builder.create();
case DIALOG_CONFIRM_NEXTSTEPS_ID:
builder
.setTitle("Version Problem")
.setMessage("The base version for tiles has changed. What to do?")
.setPositiveButton("Continue with current download, delete other old data", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
ArrayList<Integer> allTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
for (Integer sel : allTiles) {
if (!selectedTiles.contains(sel)) {
mBInstallerView.toggleTileStatus(sel, 0);
new File(mBaseDir, "brouter/segments4/" + baseNameForTile(sel) + ".rd5").delete();
}
}
downloadSelectedTiles();
}
}).setNegativeButton("Select all for download and start", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
downloadInstalledTiles();
}
}).setNeutralButton("Cancel now, complete on an other day", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
File tmplookupFile = new File(mBaseDir, "brouter/profiles2/lookups.dat.tmp");
tmplookupFile.delete();
finish();
}
});
return builder.create();
case DIALOG_CONFIRM_GETDIFFS_ID:
builder
.setTitle("Version Differences")
.setMessage("The base version for some tiles is different. What to do?")
.setPositiveButton("Download all different tiles", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
downloadDiffVersionTiles();
}
}).setNegativeButton("Drop all different tiles", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dropDiffVersionTiles();
}
}).setNeutralButton("Cancel now, complete on an other day", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
finish();
}
});
return builder.create();
default: default:
return null; return null;
} }
} }
public void showConfirmDelete() { void showADialog(int id) {
showDialog(DIALOG_CONFIRM_DELETE_ID); Dialog d = createADialog(id);
if (d != null) d.show();
} }
public void showConfirmDelete() {
showADialog(DIALOG_CONFIRM_DELETE_ID);
}
public void showConfirmNextSteps() {
showADialog(DIALOG_CONFIRM_NEXTSTEPS_ID);
}
private void showConfirmGetDiffs() {
showADialog(DIALOG_CONFIRM_GETDIFFS_ID);
}
private void scanExistingFiles() { private void scanExistingFiles() {
mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5); mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5);
@ -270,14 +451,51 @@ public class BInstallerActivity extends AppCompatActivity {
} }
private void downloadSelectedTiles() { private void downloadSelectedTiles() {
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5); selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
downloadAll(selectedTiles); downloadAll(selectedTiles, DownloadWorker.VALUE_SEGMENT_PARTS);
mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5);
} }
private void downloadInstalledTiles() { private void downloadInstalledTiles() {
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5); ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
downloadAll(selectedTiles); ArrayList<Integer> tmpSelectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
if (tmpSelectedTiles.size() > 0) {
selectedTiles.addAll(tmpSelectedTiles);
}
downloadAll(selectedTiles, DownloadWorker.VALUE_SEGMENT_ALL);
}
private void downloadDiffVersionTiles() {
downloadAll(new ArrayList<Integer>(), DownloadWorker.VALUE_SEGMENT_DIFFS);
}
private void dropDiffVersionTiles() {
downloadAll(new ArrayList<Integer>(), DownloadWorker.VALUE_SEGMENT_DROPDIFFS);
}
private boolean isDownloadRunning(Class<?> serviceClass) {
WorkManager instance = WorkManager.getInstance(getApplicationContext());
ListenableFuture<List<WorkInfo>> statuses = instance.getWorkInfosForUniqueWork(DownloadWorker.WORKER_NAME);
try {
boolean running = false;
List<WorkInfo> workInfoList = statuses.get();
for (WorkInfo workInfo : workInfoList) {
WorkInfo.State state = workInfo.getState();
running = state == WorkInfo.State.RUNNING | state == WorkInfo.State.ENQUEUED;
}
return running;
} catch (ExecutionException e) {
e.printStackTrace();
return false;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
void stopDownload() {
WorkManager workManager = WorkManager.getInstance(getApplicationContext());
workManager.cancelAllWork();
} }
private int tileForBaseName(String basename) { private int tileForBaseName(String basename) {

View file

@ -75,18 +75,20 @@ public class BInstallerView extends View {
} }
public void setTileStatus(int tileIndex, int tileMask) { public void setTileStatus(int tileIndex, int tileMask) {
tileStatus[tileIndex] |= tileMask; if (mOnSelectListener == null) {
if (mOnSelectListener != null) { return;
mOnSelectListener.onSelect();
} }
tileStatus[tileIndex] |= tileMask;
mOnSelectListener.onSelect();
invalidate(); invalidate();
} }
public void toggleTileStatus(int tileIndex, int tileMask) { public void toggleTileStatus(int tileIndex, int tileMask) {
tileStatus[tileIndex] ^= tileMask; if (mOnSelectListener == null) {
if (mOnSelectListener != null) { return;
mOnSelectListener.onSelect();
} }
tileStatus[tileIndex] ^= tileMask;
mOnSelectListener.onSelect();
invalidate(); invalidate();
} }

View file

@ -78,8 +78,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
setContentView(mBRouterView); setContentView(mBRouterView);
} }
@Override protected Dialog createADialog(int id) {
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder; AlertDialog.Builder builder;
builder = new AlertDialog.Builder(this); builder = new AlertDialog.Builder(this);
builder.setCancelable(false); builder.setCancelable(false);
@ -103,7 +102,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
if (item == 0) if (item == 0)
startDownloadManager(); startDownloadManager();
else else
showDialog(DIALOG_SELECTPROFILE_ID); showADialog(DIALOG_SELECTPROFILE_ID);
} }
}) })
.setNegativeButton("Close", new DialogInterface.OnClickListener() { .setNegativeButton("Close", new DialogInterface.OnClickListener() {
@ -214,7 +213,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
if (selectedBasedir < availableBasedirs.size()) { if (selectedBasedir < availableBasedirs.size()) {
mBRouterView.startSetup(availableBasedirs.get(selectedBasedir), true); mBRouterView.startSetup(availableBasedirs.get(selectedBasedir), true);
} else { } else {
showDialog(DIALOG_TEXTENTRY_ID); showADialog(DIALOG_TEXTENTRY_ID);
} }
} }
}); });
@ -338,17 +337,14 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
Arrays.sort(availableProfiles); Arrays.sort(availableProfiles);
// show main dialog // show main dialog
showDialog(DIALOG_MAINACTION_ID); showADialog(DIALOG_MAINACTION_ID);
} }
public void startDownloadManager() { public void startDownloadManager() {
if (!mBRouterView.hasUpToDateLookups()) { showADialog(DIALOG_SHOW_DM_INFO_ID);
showDialog(DIALOG_OLDDATAHINT_ID);
} else {
showDialog(DIALOG_SHOW_DM_INFO_ID);
}
} }
@SuppressWarnings("deprecation")
public void selectBasedir(ArrayList<File> items, String message) { public void selectBasedir(ArrayList<File> items, String message) {
this.message = message; this.message = message;
availableBasedirs = items; availableBasedirs = items;
@ -357,7 +353,11 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
long size = 0L; long size = 0L;
try { try {
StatFs stat = new StatFs(f.getAbsolutePath()); StatFs stat = new StatFs(f.getAbsolutePath());
size = (long) stat.getAvailableBlocks() * stat.getBlockSize(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
size = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
} else {
size = (long) stat.getAvailableBlocks() * stat.getBlockSize();
}
} catch (Exception e) { } catch (Exception e) {
/* ignore */ } /* ignore */ }
dirFreeSizes.add(size); dirFreeSizes.add(size);
@ -379,26 +379,26 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
basedirOptions[bdidx] = "Enter path manually"; basedirOptions[bdidx] = "Enter path manually";
} }
showDialog(DIALOG_SELECTBASEDIR_ID); showADialog(DIALOG_SELECTBASEDIR_ID);
} }
public void selectRoutingModes(String[] modes, boolean[] modesChecked, String message) { public void selectRoutingModes(String[] modes, boolean[] modesChecked, String message) {
routingModes = modes; routingModes = modes;
routingModesChecked = modesChecked; routingModesChecked = modesChecked;
this.message = message; this.message = message;
showDialog(DIALOG_ROUTINGMODES_ID); showADialog(DIALOG_ROUTINGMODES_ID);
} }
public void showModeConfigOverview(String message) { public void showModeConfigOverview(String message) {
this.message = message; this.message = message;
showDialog(DIALOG_MODECONFIGOVERVIEW_ID); showADialog(DIALOG_MODECONFIGOVERVIEW_ID);
} }
public void selectVias(String[] items) { public void selectVias(String[] items) {
availableVias = items; availableVias = items;
selectedVias = new HashSet<>(availableVias.length); selectedVias = new HashSet<>(availableVias.length);
Collections.addAll(selectedVias, items); Collections.addAll(selectedVias, items);
showDialog(DIALOG_VIASELECT_ID); showADialog(DIALOG_VIASELECT_ID);
} }
public void selectWaypoint(String[] items) { public void selectWaypoint(String[] items) {
@ -412,15 +412,20 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat
public void selectNogos(List<OsmNodeNamed> nogoList) { public void selectNogos(List<OsmNodeNamed> nogoList) {
this.nogoList = nogoList; this.nogoList = nogoList;
showDialog(DIALOG_NOGOSELECT_ID); showADialog(DIALOG_NOGOSELECT_ID);
}
private void showADialog(int id) {
Dialog d = createADialog(id);
if (d!=null) d.show();
} }
private void showNewDialog(int id) { private void showNewDialog(int id) {
if (dialogIds.contains(id)) { if (dialogIds.contains(id)) {
removeDialog(id); // removeDialog(id);
} }
dialogIds.add(id); dialogIds.add(id);
showDialog(id); showADialog(id);
} }
public void showErrorMessage(String msg) { public void showErrorMessage(String msg) {

View file

@ -279,7 +279,7 @@ public class BRouterService extends Service {
private void logBundle(Bundle params) { private void logBundle(Bundle params) {
if (AppLogger.isLogging()) { if (AppLogger.isLogging()) {
for (String k : params.keySet()) { for (String k : params.keySet()) {
Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.get(k); Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.getString(k);
String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=" + val.toString()); String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=" + val.toString());
AppLogger.log(desc); AppLogger.log(desc);
} }

View file

@ -5,9 +5,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
@ -740,7 +742,8 @@ public class BRouterView extends View {
paintPosition(n.ilon, n.ilat, color, minradius); paintPosition(n.ilon, n.ilat, color, minradius);
} }
canvas.drawBitmap(imgPixels, 0, imgw, (float) 0., (float) 0., imgw, imgh, false, null); Bitmap bmp = Bitmap.createBitmap(imgPixels, imgw, imgh, Bitmap.Config.RGB_565);
canvas.drawBitmap(bmp, 0, 0, null);
// nogo circles if any // nogo circles if any
for (int ngi = 0; ngi < nogoList.size(); ngi++) { for (int ngi = 0; ngi < nogoList.size(); ngi++) {

View file

@ -17,28 +17,42 @@ import androidx.work.Worker;
import androidx.work.WorkerParameters; import androidx.work.WorkerParameters;
import java.io.File; import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random; import java.util.Random;
import btools.expressions.BExpressionMetaData;
import btools.mapaccess.PhysicalFile; import btools.mapaccess.PhysicalFile;
import btools.mapaccess.Rd5DiffManager; import btools.mapaccess.Rd5DiffManager;
import btools.mapaccess.Rd5DiffTool; import btools.mapaccess.Rd5DiffTool;
import btools.util.ProgressListener; import btools.util.ProgressListener;
public class DownloadWorker extends Worker { public class DownloadWorker extends Worker {
public static final String WORKER_NAME = "BRouterWorker";
private final static boolean DEBUG = false;
public static final String KEY_INPUT_SEGMENT_NAMES = "SEGMENT_NAMES"; public static final String KEY_INPUT_SEGMENT_NAMES = "SEGMENT_NAMES";
public static final String KEY_INPUT_SEGMENT_ALL = "SEGMENT_ALL";
public static final String KEY_OUTPUT_ERROR = "ERROR"; public static final String KEY_OUTPUT_ERROR = "ERROR";
public static final int VALUE_SEGMENT_PARTS = 0;
public static final int VALUE_SEGMENT_ALL = 1;
public static final int VALUE_SEGMENT_DIFFS = 2;
public static final int VALUE_SEGMENT_DROPDIFFS = 3;
public static final String PROGRESS_SEGMENT_NAME = "PROGRESS_SEGMENT_NAME"; public static final String PROGRESS_SEGMENT_NAME = "PROGRESS_SEGMENT_NAME";
public static final String PROGRESS_SEGMENT_PERCENT = "PROGRESS_SEGMENT_PERCENT"; 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 int NOTIFICATION_ID = new Random().nextInt();
private static final String PROFILES_DIR = "profiles2/"; public static final String PROFILES_DIR = "profiles2/";
private static final String SEGMENTS_DIR = "segments4/"; private static final String SEGMENTS_DIR = "segments4/";
private static final String SEGMENT_DIFF_SUFFIX = ".df5"; private static final String SEGMENT_DIFF_SUFFIX = ".df5";
private static final String SEGMENT_SUFFIX = ".rd5"; private static final String SEGMENT_SUFFIX = ".rd5";
@ -51,6 +65,11 @@ public class DownloadWorker extends Worker {
private final DownloadProgressListener downloadProgressListener; private final DownloadProgressListener downloadProgressListener;
private final Data.Builder progressBuilder = new Data.Builder(); private final Data.Builder progressBuilder = new Data.Builder();
private final NotificationCompat.Builder notificationBuilder; private final NotificationCompat.Builder notificationBuilder;
private int downloadAll;
private boolean versionChanged;
private List<URL> done = new ArrayList<>();
int version = -1;
public DownloadWorker( public DownloadWorker(
@NonNull Context context, @NonNull Context context,
@ -70,16 +89,21 @@ public class DownloadWorker extends Worker {
@Override @Override
public void onDownloadStart(String downloadName, DownloadType downloadType) { public void onDownloadStart(String downloadName, DownloadType downloadType) {
if (DEBUG) Log.d(LOG_TAG, "onDownloadStart " + downloadName);
currentDownloadName = downloadName; currentDownloadName = downloadName;
currentDownloadType = downloadType; currentDownloadType = downloadType;
if (downloadType == DownloadType.SEGMENT) { if (downloadType == DownloadType.SEGMENT) {
progressBuilder.putString(PROGRESS_SEGMENT_NAME, downloadName); progressBuilder.putString(PROGRESS_SEGMENT_NAME, downloadName);
notificationBuilder.setContentText(downloadName); notificationBuilder.setContentText(downloadName);
} else {
progressBuilder.putString(PROGRESS_SEGMENT_NAME, "check profiles");
} }
setProgressAsync(progressBuilder.build());
} }
@Override @Override
public void onDownloadInfo(String info) { public void onDownloadInfo(String info) {
if (DEBUG) Log.d(LOG_TAG, "onDownloadInfo " + info);
notificationBuilder.setContentText(currentDownloadName + ": " + info); notificationBuilder.setContentText(currentDownloadName + ": " + info);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
} }
@ -100,6 +124,7 @@ public class DownloadWorker extends Worker {
notificationBuilder.setProgress(0, 0, true); notificationBuilder.setProgress(0, 0, true);
progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, -1); progressBuilder.putInt(PROGRESS_SEGMENT_PERCENT, -1);
} }
progressBuilder.putString(PROGRESS_SEGMENT_NAME, currentDownloadName);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
setProgressAsync(progressBuilder.build()); setProgressAsync(progressBuilder.build());
@ -108,12 +133,14 @@ public class DownloadWorker extends Worker {
@Override @Override
public void onDownloadFinished() { public void onDownloadFinished() {
if (DEBUG) Log.d(LOG_TAG, "onDownloadFinished ");
} }
}; };
diffProgressListener = new ProgressListener() { diffProgressListener = new ProgressListener() {
@Override @Override
public void updateProgress(String task, int progress) { public void updateProgress(String task, int progress) {
if (DEBUG) Log.d(LOG_TAG, "updateProgress " + task + " " + progress);
downloadProgressListener.onDownloadInfo(task); downloadProgressListener.onDownloadInfo(task);
downloadProgressListener.onDownloadProgress(100, progress); downloadProgressListener.onDownloadProgress(100, progress);
} }
@ -131,6 +158,9 @@ public class DownloadWorker extends Worker {
Data inputData = getInputData(); Data inputData = getInputData();
Data.Builder output = new Data.Builder(); Data.Builder output = new Data.Builder();
String[] segmentNames = inputData.getStringArray(KEY_INPUT_SEGMENT_NAMES); String[] segmentNames = inputData.getStringArray(KEY_INPUT_SEGMENT_NAMES);
downloadAll = inputData.getInt(KEY_INPUT_SEGMENT_ALL, 0);
if (DEBUG)
Log.d(LOG_TAG, "doWork done " + done.size() + " segs " + segmentNames.length + " " + this);
if (segmentNames == null) { if (segmentNames == null) {
if (DEBUG) Log.d(LOG_TAG, "Failure: no segmentNames"); if (DEBUG) Log.d(LOG_TAG, "Failure: no segmentNames");
return Result.failure(); return Result.failure();
@ -140,49 +170,127 @@ public class DownloadWorker extends Worker {
setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build())); setForegroundAsync(new ForegroundInfo(NOTIFICATION_ID, notificationBuilder.build()));
try { try {
if (DEBUG) Log.d(LOG_TAG, "Download lookup & profiles"); if (DEBUG) Log.d(LOG_TAG, "Download lookup & profiles");
downloadLookupAndProfiles(); if (!downloadLookup()) {
output.putString(KEY_OUTPUT_ERROR, "Version error");
return Result.failure(output.build());
}
if (!versionChanged && downloadAll != VALUE_SEGMENT_ALL) {
List<String> tmpSegementNames = new ArrayList<>();
File segmentFolder = new File(baseDir, SEGMENTS_DIR);
File[] files = segmentFolder.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return (file.getPath().endsWith(SEGMENT_SUFFIX));
}
});
for (File f : files) {
int thePFversion = PhysicalFile.checkVersionIntegrity(f);
if (DEBUG) Log.d("worker", "check " + f.getName() + " " + thePFversion + "=" + version);
if (thePFversion != -1 && thePFversion != version) {
tmpSegementNames.add(f.getName().substring(0, f.getName().indexOf(".")));
versionChanged = true;
}
}
if (tmpSegementNames.size() > 0 && (downloadAll != VALUE_SEGMENT_DIFFS && downloadAll != VALUE_SEGMENT_DROPDIFFS)) {
output.putString(KEY_OUTPUT_ERROR, "Version diffs");
return Result.failure(output.build());
}
if (downloadAll == VALUE_SEGMENT_DIFFS) {
segmentNames = tmpSegementNames.toArray(new String[0]);
} else if (downloadAll == VALUE_SEGMENT_DROPDIFFS) {
for (String segmentName : tmpSegementNames) {
File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName + SEGMENT_SUFFIX);
segmentFile.delete();
}
return Result.success();
}
}
downloadProfiles();
for (String segmentName : segmentNames) { for (String segmentName : segmentNames) {
if (isStopped()) break;
downloadProgressListener.onDownloadStart(segmentName, DownloadType.SEGMENT); downloadProgressListener.onDownloadStart(segmentName, DownloadType.SEGMENT);
if (DEBUG) Log.d(LOG_TAG, "Download segment " + segmentName); if (DEBUG) Log.d(LOG_TAG, "Download segment " + segmentName);
downloadSegment(mServerConfig.getSegmentUrl(), segmentName + SEGMENT_SUFFIX); downloadSegment(mServerConfig.getSegmentUrl(), segmentName + SEGMENT_SUFFIX);
} }
} catch (IOException e) { } catch (IOException e) {
Log.w(LOG_TAG, e); output.putString(KEY_OUTPUT_ERROR, e.getMessage());
output.putString(KEY_OUTPUT_ERROR, e.toString());
return Result.failure(output.build()); return Result.failure(output.build());
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.w(LOG_TAG, e); output.putString(KEY_OUTPUT_ERROR, e.getMessage());
output.putString(KEY_OUTPUT_ERROR, e.toString());
return Result.failure(output.build()); return Result.failure(output.build());
} }
if (DEBUG) Log.d(LOG_TAG, "doWork finished"); if (DEBUG) Log.d(LOG_TAG, "doWork finished");
return Result.success(); return Result.success();
} }
private void downloadLookupAndProfiles() throws IOException, InterruptedException { private boolean downloadLookup() throws IOException, InterruptedException {
String[] lookups = mServerConfig.getLookups(); String[] lookups = mServerConfig.getLookups();
for (String fileName : lookups) { for (String fileName : lookups) {
if (fileName.length() > 0) { if (fileName.length() > 0) {
File lookupFile = new File(baseDir, PROFILES_DIR + fileName); File lookupFile = new File(baseDir, PROFILES_DIR + fileName);
String lookupLocation = mServerConfig.getLookupUrl() + fileName; BExpressionMetaData meta = new BExpressionMetaData();
URL lookupUrl = new URL(lookupLocation); meta.readMetaData(lookupFile);
downloadProgressListener.onDownloadStart(fileName, DownloadType.LOOKUP); version = meta.lookupVersion;
downloadFile(lookupUrl, lookupFile, false);
downloadProgressListener.onDownloadFinished(); int size = (int) (lookupFile.exists() ? lookupFile.length() : 0);
File tmplookupFile = new File(baseDir, PROFILES_DIR + fileName + ".tmp");
boolean changed = false;
if (tmplookupFile.exists()) {
lookupFile.delete();
tmplookupFile.renameTo(lookupFile);
versionChanged = true;
meta.readMetaData(lookupFile);
version = meta.lookupVersion;
} else {
String lookupLocation = mServerConfig.getLookupUrl() + fileName;
URL lookupUrl = new URL(lookupLocation);
downloadProgressListener.onDownloadStart(fileName, DownloadType.LOOKUP);
changed = downloadFile(lookupUrl, tmplookupFile, size, false, DownloadType.LOOKUP);
downloadProgressListener.onDownloadFinished();
done.add(lookupUrl);
}
if (changed && downloadAll == VALUE_SEGMENT_PARTS) {
meta = new BExpressionMetaData();
meta.readMetaData(tmplookupFile);
int newversion = meta.lookupVersion;
if (DEBUG) Log.d(LOG_TAG, "version old " + version + " new " + newversion);
if (version != newversion) {
return false;
}
} else if (changed) {
lookupFile.delete();
tmplookupFile.renameTo(lookupFile);
versionChanged = changed;
meta.readMetaData(lookupFile);
version = meta.lookupVersion;
} else {
if (tmplookupFile.exists()) tmplookupFile.delete();
}
} }
} }
return true;
}
private void downloadProfiles() throws IOException, InterruptedException {
String[] profiles = mServerConfig.getProfiles(); String[] profiles = mServerConfig.getProfiles();
for (String fileName : profiles) { for (String fileName : profiles) {
if (isStopped()) break;
if (fileName.length() > 0) { if (fileName.length() > 0) {
File profileFile = new File(baseDir, PROFILES_DIR + fileName); File profileFile = new File(baseDir, PROFILES_DIR + fileName);
if (profileFile.exists()) { //if (profileFile.exists())
{
String profileLocation = mServerConfig.getProfilesUrl() + fileName; String profileLocation = mServerConfig.getProfilesUrl() + fileName;
URL profileUrl = new URL(profileLocation); URL profileUrl = new URL(profileLocation);
int size = (int) (profileFile.exists() ? profileFile.length() : 0);
downloadProgressListener.onDownloadStart(fileName, DownloadType.PROFILE); downloadProgressListener.onDownloadStart(fileName, DownloadType.PROFILE);
downloadFile(profileUrl, profileFile, false); downloadFile(profileUrl, profileFile, size, false, DownloadType.PROFILE);
downloadProgressListener.onDownloadFinished(); downloadProgressListener.onDownloadFinished();
done.add(profileUrl);
} }
} }
} }
@ -191,29 +299,38 @@ public class DownloadWorker extends Worker {
private void downloadSegment(String segmentBaseUrl, String segmentName) throws IOException, InterruptedException { private void downloadSegment(String segmentBaseUrl, String segmentName) throws IOException, InterruptedException {
File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName); File segmentFile = new File(baseDir, SEGMENTS_DIR + segmentName);
File segmentFileTemp = new File(segmentFile.getAbsolutePath() + "_tmp"); File segmentFileTemp = new File(segmentFile.getAbsolutePath() + "_tmp");
if (DEBUG) Log.d(LOG_TAG, "Download " + segmentName + " " + version + " " + versionChanged);
try { try {
if (segmentFile.exists()) { if (segmentFile.exists()) {
if (DEBUG) Log.d(LOG_TAG, "Calculating local checksum"); if (!versionChanged) { // no diff file on version change
String md5 = Rd5DiffManager.getMD5(segmentFile); String md5 = Rd5DiffManager.getMD5(segmentFile);
String segmentDeltaLocation = segmentBaseUrl + "diff/" + segmentName.replace(SEGMENT_SUFFIX, "/" + md5 + SEGMENT_DIFF_SUFFIX); if (DEBUG) Log.d(LOG_TAG, "Calculating local checksum " + md5);
URL segmentDeltaUrl = new URL(segmentDeltaLocation); String segmentDeltaLocation = segmentBaseUrl + "diff/" + segmentName.replace(SEGMENT_SUFFIX, "/" + md5 + SEGMENT_DIFF_SUFFIX);
if (httpFileExists(segmentDeltaUrl)) { URL segmentDeltaUrl = new URL(segmentDeltaLocation);
File segmentDeltaFile = new File(segmentFile.getAbsolutePath() + "_diff"); if (httpFileExists(segmentDeltaUrl)) {
try { File segmentDeltaFile = new File(segmentFile.getAbsolutePath() + "_diff");
downloadFile(segmentDeltaUrl, segmentDeltaFile, true); try {
if (DEBUG) Log.d(LOG_TAG, "Applying delta"); downloadFile(segmentDeltaUrl, segmentDeltaFile, 0, true, DownloadType.SEGMENT);
Rd5DiffTool.recoverFromDelta(segmentFile, segmentDeltaFile, segmentFileTemp, diffProgressListener); done.add(segmentDeltaUrl);
} catch (IOException e) { if (DEBUG) Log.d(LOG_TAG, "Applying delta");
throw new IOException("Failed to download & apply delta update", e); Rd5DiffTool.recoverFromDelta(segmentFile, segmentDeltaFile, segmentFileTemp, diffProgressListener);
} finally { } catch (IOException e) {
segmentDeltaFile.delete(); throw new IOException("Failed to download & apply delta update", e);
} finally {
segmentDeltaFile.delete();
}
}
} else {
if (segmentFileTemp.exists()) {
segmentFileTemp.delete();
} }
} }
} }
if (!segmentFileTemp.exists()) { if (!segmentFileTemp.exists()) {
URL segmentUrl = new URL(segmentBaseUrl + segmentName); URL segmentUrl = new URL(segmentBaseUrl + segmentName);
downloadFile(segmentUrl, segmentFileTemp, true); downloadFile(segmentUrl, segmentFileTemp, 0, true, DownloadType.SEGMENT);
done.add(segmentUrl);
} }
PhysicalFile.checkFileIntegrity(segmentFileTemp); PhysicalFile.checkFileIntegrity(segmentFileTemp);
@ -235,12 +352,14 @@ public class DownloadWorker extends Worker {
HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(); HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
connection.setConnectTimeout(5000); connection.setConnectTimeout(5000);
connection.setRequestMethod("HEAD"); connection.setRequestMethod("HEAD");
connection.setDoInput(false);
connection.connect(); connection.connect();
return connection.getResponseCode() == HttpURLConnection.HTTP_OK; return connection.getResponseCode() == HttpURLConnection.HTTP_OK;
} }
private void downloadFile(URL downloadUrl, File outputFile, boolean limitDownloadSpeed) throws IOException, InterruptedException { private boolean downloadFile(URL downloadUrl, File outputFile, int fileSize, boolean limitDownloadSpeed, DownloadType type) throws IOException, InterruptedException {
if (DEBUG) Log.d(LOG_TAG, "download " + outputFile.getAbsolutePath());
HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(); HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection();
connection.setConnectTimeout(5000); connection.setConnectTimeout(5000);
connection.connect(); connection.connect();
@ -248,11 +367,25 @@ public class DownloadWorker extends Worker {
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
throw new IOException("HTTP Request failed: " + downloadUrl + " returned " + connection.getResponseCode()); throw new IOException("HTTP Request failed: " + downloadUrl + " returned " + connection.getResponseCode());
} }
int fileLength = connection.getContentLength(); int dataLength = connection.getContentLength();
try ( // no need of download when size equal
InputStream input = connection.getInputStream(); // file size not the best coice but easy to handle, date is not available
OutputStream output = new FileOutputStream(outputFile) switch (type) {
) { case LOOKUP:
if (fileSize == dataLength) return false;
break;
case PROFILE:
if (fileSize == dataLength) return false;
break;
default:
break;
}
InputStream input = null;
OutputStream output = null;
try {
input = connection.getInputStream();
output = new FileOutputStream(outputFile);
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int total = 0; int total = 0;
long t0 = System.currentTimeMillis(); long t0 = System.currentTimeMillis();
@ -264,7 +397,7 @@ public class DownloadWorker extends Worker {
total += count; total += count;
output.write(buffer, 0, count); output.write(buffer, 0, count);
downloadProgressListener.onDownloadProgress(fileLength, total); downloadProgressListener.onDownloadProgress(dataLength, total);
if (limitDownloadSpeed) { if (limitDownloadSpeed) {
// enforce < 16 Mbit/s // enforce < 16 Mbit/s
@ -274,7 +407,12 @@ public class DownloadWorker extends Worker {
} }
} }
} }
} finally {
if (input != null) input.close();
if (output != null) output.close();
connection.disconnect();
} }
return true;
} }
@NonNull @NonNull
@ -313,7 +451,7 @@ public class DownloadWorker extends Worker {
notificationManager.createNotificationChannel(channel); notificationManager.createNotificationChannel(channel);
} }
enum DownloadType { public enum DownloadType {
LOOKUP, LOOKUP,
PROFILE, PROFILE,
SEGMENT SEGMENT

View file

@ -42,4 +42,17 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textViewDownloadSummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_below="@+id/progressDownload"
android:textColor="@android:color/primary_text_light"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -29,6 +29,7 @@
<string name="action_delete">Delete %s</string> <string name="action_delete">Delete %s</string>
<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="action_cancel">Stop Download</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_channel_id">brouter_download</string>
<string name="notification_title">Download Segments</string> <string name="notification_title">Download Segments</string>

View file

@ -13,7 +13,7 @@ application {
attributes "Main-Class": getMainClass(), "Implementation-Version": project.version attributes "Main-Class": getMainClass(), "Implementation-Version": project.version
} }
} }
task fatJar(type: Jar) { task fatJar(type: Jar) {
archiveFileName = 'brouter-' + project.version + '-all.jar' archiveFileName = 'brouter-' + project.version + '-all.jar'
@ -34,7 +34,7 @@ application {
} }
distZip { distZip {
dependsOn fatJar dependsOn fatJar, ':brouter-routing-app:packageRelease'
archiveFileName = 'brouter-' + project.version + '.zip' archiveFileName = 'brouter-' + project.version + '.zip'
} }

View file

@ -7,7 +7,7 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.2' classpath 'com.android.tools.build:gradle:7.4.2'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

View file

@ -16,5 +16,6 @@ org.gradle.jvmargs=-Xmx1536m
# https://developer.android.com/topic/libraries/support-library/androidx-rn # https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true android.enableJetifier=false

View file

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip