diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java index 6119a0b..42c47d9 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerActivity.java @@ -10,18 +10,25 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.os.StatFs; import android.text.format.Formatter; +import android.util.Log; +import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; 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.Data; +import androidx.work.ExistingWorkPolicy; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; import androidx.work.WorkInfo; @@ -29,22 +36,34 @@ import androidx.work.WorkManager; import androidx.work.WorkRequest; import com.google.android.material.progressindicator.LinearProgressIndicator; +import com.google.common.util.concurrent.ListenableFuture; import java.io.File; import java.util.ArrayList; +import java.util.List; import java.util.Locale; +import java.util.concurrent.ExecutionException; import btools.router.RoutingHelper; public class BInstallerActivity extends AppCompatActivity { 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; private File mBaseDir; private BInstallerView mBInstallerView; private Button mButtonDownload; private TextView mSummaryInfo; + private TextView mDownloadSummaryInfo; private LinearProgressIndicator mProgressIndicator; + private ArrayList selectedTiles; + + BInstallerView.OnSelectListener onSelectListener; public static long getAvailableSpace(String baseDir) { StatFs stat = new StatFs(baseDir); @@ -61,6 +80,8 @@ public class BInstallerActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + boolean running = isDownloadRunning(DownloadWorker.class); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); setContentView(R.layout.activity_binstaller); @@ -80,27 +101,57 @@ public class BInstallerActivity extends AppCompatActivity { mSummaryInfo = findViewById(R.id.textViewSegmentSummary); mBInstallerView = findViewById(R.id.BInstallerView); - mBInstallerView.setOnSelectListener( - () -> { + onSelectListener = new BInstallerView.OnSelectListener() { + @Override + public void onSelect() { + //if (!isDownloadRunning(DownloadWorker.class)) updateDownloadButton(); } - ); + }; + + mBInstallerView.setOnSelectListener(onSelectListener); + mButtonDownload = findViewById(R.id.buttonDownload); mButtonDownload.setOnClickListener( view -> { - if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) { + if (isDownloadRunning(DownloadWorker.class)) { + stopDownload(); + } else if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) { showConfirmDelete(); } else if (mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5).size() > 0) { + mBInstallerView.setOnSelectListener(null); downloadSelectedTiles(); } else { + mBInstallerView.setOnSelectListener(null); downloadInstalledTiles(); } } ); mProgressIndicator = findViewById(R.id.progressDownload); + mDownloadSummaryInfo = findViewById(R.id.textViewDownloadSummary); + mDownloadSummaryInfo.setVisibility(View.INVISIBLE); mBaseDir = ConfigHelper.getBaseDir(this); - scanExistingFiles(); + + if (running) { + mProgressIndicator.show(); + mButtonDownload.setEnabled(false); + WorkManager instance = WorkManager.getInstance(getApplicationContext()); + LiveData> 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) { @@ -146,19 +197,24 @@ public class BInstallerActivity extends AppCompatActivity { } } - public void downloadAll(ArrayList downloadList) { + public void downloadAll(ArrayList downloadList, int all) { ArrayList urlparts = new ArrayList<>(); for (Integer i : downloadList) { urlparts.add(baseNameForTile(i)); } downloadCanceled = false; + mProgressIndicator.show(); + mButtonDownload.setEnabled(false); Data inputData = new Data.Builder() .putStringArray(DownloadWorker.KEY_INPUT_SEGMENT_NAMES, urlparts.toArray(new String[0])) + .putInt(DownloadWorker.KEY_INPUT_SEGMENT_ALL, all) .build(); Constraints constraints = new Constraints.Builder() + .setRequiresBatteryNotLow(true) + .setRequiresStorageNotLow(true) .setRequiredNetworkType(NetworkType.CONNECTED) .build(); @@ -169,59 +225,104 @@ public class BInstallerActivity extends AppCompatActivity { .build(); 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 .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 = "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(); - } - } + startObserver(workInfo); }); deleteRawTracks(); // invalidate raw-tracks after data update } + private void startObserver(WorkInfo workInfo) { + 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); + } + } + + } + @Override protected Dialog onCreateDialog(int id) { AlertDialog.Builder builder; @@ -240,6 +341,56 @@ public class BInstallerActivity extends AppCompatActivity { }); return builder.create(); + case DIALOG_CONFIRM_NEXTSTEPS_ID: + builder = new AlertDialog.Builder(this); + 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 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 = new AlertDialog.Builder(this); + 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: return null; } @@ -249,6 +400,15 @@ public class BInstallerActivity extends AppCompatActivity { showDialog(DIALOG_CONFIRM_DELETE_ID); } + public void showConfirmNextSteps() { + showDialog(DIALOG_CONFIRM_NEXTSTEPS_ID); + } + + private void showConfirmGetDiffs() { + showDialog(DIALOG_CONFIRM_GETDIFFS_ID); + } + + private void scanExistingFiles() { mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5); @@ -285,14 +445,51 @@ public class BInstallerActivity extends AppCompatActivity { } private void downloadSelectedTiles() { - ArrayList selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5); - downloadAll(selectedTiles); - mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5); + selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5); + downloadAll(selectedTiles, DownloadWorker.VALUE_SEGMENT_PARTS); } private void downloadInstalledTiles() { ArrayList selectedTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5); - downloadAll(selectedTiles); + ArrayList 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(), DownloadWorker.VALUE_SEGMENT_DIFFS); + } + + private void dropDiffVersionTiles() { + downloadAll(new ArrayList(), DownloadWorker.VALUE_SEGMENT_DROPDIFFS); + } + + private boolean isDownloadRunning(Class serviceClass) { + WorkManager instance = WorkManager.getInstance(getApplicationContext()); + + ListenableFuture> statuses = instance.getWorkInfosForUniqueWork(DownloadWorker.WORKER_NAME); + try { + boolean running = false; + List 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) { diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java index ffe75c2..ef8dcca 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BInstallerView.java @@ -75,18 +75,20 @@ public class BInstallerView extends View { } public void setTileStatus(int tileIndex, int tileMask) { - tileStatus[tileIndex] |= tileMask; - if (mOnSelectListener != null) { - mOnSelectListener.onSelect(); + if (mOnSelectListener == null) { + return; } + tileStatus[tileIndex] |= tileMask; + mOnSelectListener.onSelect(); invalidate(); } public void toggleTileStatus(int tileIndex, int tileMask) { - tileStatus[tileIndex] ^= tileMask; - if (mOnSelectListener != null) { - mOnSelectListener.onSelect(); + if (mOnSelectListener == null) { + return; } + tileStatus[tileIndex] ^= tileMask; + mOnSelectListener.onSelect(); invalidate(); } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java index f093ec5..2639cba 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterActivity.java @@ -342,11 +342,7 @@ public class BRouterActivity extends AppCompatActivity implements ActivityCompat } public void startDownloadManager() { - if (!mBRouterView.hasUpToDateLookups()) { - showDialog(DIALOG_OLDDATAHINT_ID); - } else { - showDialog(DIALOG_SHOW_DM_INFO_ID); - } + showDialog(DIALOG_SHOW_DM_INFO_ID); } public void selectBasedir(ArrayList items, String message) {