Merge pull request #390 from zod/cleanup-binstaller
Use layouts in DownloadManager
This commit is contained in:
commit
80d0a30729
6 changed files with 533 additions and 615 deletions
|
@ -95,6 +95,7 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
|
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
|
||||||
|
|
||||||
implementation project(':brouter-mapaccess')
|
implementation project(':brouter-mapaccess')
|
||||||
implementation project(':brouter-core')
|
implementation project(':brouter-core')
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import static btools.routingapp.BInstallerView.MASK_CURRENT_RD5;
|
||||||
import java.util.Set;
|
import static btools.routingapp.BInstallerView.MASK_DELETED_RD5;
|
||||||
|
import static btools.routingapp.BInstallerView.MASK_INSTALLED_RD5;
|
||||||
|
import static btools.routingapp.BInstallerView.MASK_SELECTED_RD5;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
|
@ -12,101 +14,176 @@ import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.PowerManager.WakeLock;
|
|
||||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
|
||||||
import android.os.StatFs;
|
import android.os.StatFs;
|
||||||
import android.util.Log;
|
import android.text.format.Formatter;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import btools.router.RoutingHelper;
|
||||||
|
|
||||||
public class BInstallerActivity extends Activity {
|
public class BInstallerActivity extends Activity {
|
||||||
|
|
||||||
public static final String DOWNLOAD_ACTION = "btools.routingapp.download";
|
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;
|
||||||
|
private File mBaseDir;
|
||||||
private BInstallerView mBInstallerView;
|
private BInstallerView mBInstallerView;
|
||||||
private PowerManager mPowerManager;
|
private DownloadReceiver downloadReceiver;
|
||||||
private WakeLock mWakeLock;
|
private View mDownloadInfo;
|
||||||
private DownloadReceiver myReceiver;
|
private TextView mDownloadInfoText;
|
||||||
|
private Button mButtonDownloadCancel;
|
||||||
|
private Button mButtonDownload;
|
||||||
|
private TextView mSummaryInfo;
|
||||||
|
private View mSegmentsView;
|
||||||
|
|
||||||
|
public static long getAvailableSpace(String baseDir) {
|
||||||
|
StatFs stat = new StatFs(baseDir);
|
||||||
|
|
||||||
public class DownloadReceiver extends BroadcastReceiver {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
|
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
||||||
|
} else {
|
||||||
|
//noinspection deprecation
|
||||||
|
return (long) stat.getAvailableBlocks() * stat.getBlockSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
if (intent.hasExtra("txt")) {
|
super.onCreate(savedInstanceState);
|
||||||
String txt = intent.getStringExtra("txt");
|
|
||||||
boolean ready = intent.getBooleanExtra("ready", false);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||||
mBInstallerView.setState(txt, ready);
|
|
||||||
|
setContentView(R.layout.activity_binstaller);
|
||||||
|
mSummaryInfo = findViewById(R.id.textViewSegmentSummary);
|
||||||
|
mSegmentsView = findViewById(R.id.view_segments);
|
||||||
|
mBInstallerView = findViewById(R.id.BInstallerView);
|
||||||
|
mBInstallerView.setOnSelectListener(
|
||||||
|
() -> {
|
||||||
|
updateDownloadButton();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
mButtonDownload = findViewById(R.id.buttonDownload);
|
||||||
|
mButtonDownload.setOnClickListener(
|
||||||
|
view -> {
|
||||||
|
if (mBInstallerView.getSelectedTiles(MASK_DELETED_RD5).size() > 0) {
|
||||||
|
showConfirmDelete();
|
||||||
|
} else if (mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5).size() > 0) {
|
||||||
|
downloadSelectedTiles();
|
||||||
|
} else {
|
||||||
|
downloadInstalledTiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
mDownloadInfo = findViewById(R.id.view_download_progress);
|
||||||
|
mDownloadInfoText = findViewById(R.id.textViewDownloadProgress);
|
||||||
|
mButtonDownloadCancel = findViewById(R.id.buttonDownloadCancel);
|
||||||
|
mButtonDownloadCancel.setOnClickListener(view -> {
|
||||||
|
cancelDownload();
|
||||||
|
});
|
||||||
|
|
||||||
|
mBaseDir = ConfigHelper.getBaseDir(this);
|
||||||
|
scanExistingFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSegmentsPlural(int count) {
|
||||||
|
Resources res = getResources();
|
||||||
|
return res.getQuantityString(R.plurals.numberOfSegments, count, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDownloadButton() {
|
||||||
|
final ArrayList<Integer> selectedTilesDownload = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
|
||||||
|
final ArrayList<Integer> selectedTilesUpdate = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
|
||||||
|
final ArrayList<Integer> selectedTilesDelete = mBInstallerView.getSelectedTiles(MASK_DELETED_RD5);
|
||||||
|
mSummaryInfo.setText("");
|
||||||
|
|
||||||
|
if (selectedTilesDelete.size() > 0) {
|
||||||
|
mButtonDownload.setText(getString(R.string.action_delete, getSegmentsPlural(selectedTilesDelete.size())));
|
||||||
|
mButtonDownload.setEnabled(true);
|
||||||
|
} else if (selectedTilesDownload.size() > 0) {
|
||||||
|
long tileSize = 0;
|
||||||
|
for (int tileIndex : selectedTilesDownload) {
|
||||||
|
tileSize += BInstallerSizes.getRd5Size(tileIndex);
|
||||||
|
}
|
||||||
|
mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size())));
|
||||||
|
mButtonDownload.setEnabled(true);
|
||||||
|
mSummaryInfo.setText(getString(R.string.summary_segments, Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath()))));
|
||||||
|
} else if (selectedTilesUpdate.size() > 0) {
|
||||||
|
mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size())));
|
||||||
|
mButtonDownload.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
mButtonDownload.setText(getString(R.string.action_select));
|
||||||
|
mButtonDownload.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteRawTracks() {
|
||||||
|
File modeDir = new File(mBaseDir, "brouter/modes");
|
||||||
|
String[] fileNames = modeDir.list();
|
||||||
|
if (fileNames == null) return;
|
||||||
|
for (String fileName : fileNames) {
|
||||||
|
if (fileName.endsWith("_rawtrack.dat")) {
|
||||||
|
File f = new File(modeDir, fileName);
|
||||||
|
f.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cancelDownload() {
|
||||||
|
downloadCanceled = true;
|
||||||
|
mDownloadInfoText.setText(getString(R.string.download_info_cancel));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public void downloadAll(ArrayList<Integer> downloadList) {
|
||||||
* Called when the activity is first created.
|
ArrayList<String> urlparts = new ArrayList<>();
|
||||||
*/
|
for (Integer i : downloadList) {
|
||||||
@Override
|
urlparts.add(baseNameForTile(i));
|
||||||
@SuppressWarnings("deprecation")
|
}
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
// Get an instance of the PowerManager
|
mSegmentsView.setVisibility(View.GONE);
|
||||||
mPowerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
mDownloadInfo.setVisibility(View.VISIBLE);
|
||||||
|
downloadCanceled = false;
|
||||||
|
mDownloadInfoText.setText(R.string.download_info_start);
|
||||||
|
|
||||||
// Create a bright wake lock
|
Intent intent = new Intent(this, DownloadService.class);
|
||||||
mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
|
intent.putExtra("dir", mBaseDir.getAbsolutePath() + "/brouter/");
|
||||||
.getName());
|
intent.putExtra("urlparts", urlparts);
|
||||||
|
startService(intent);
|
||||||
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
deleteRawTracks(); // invalidate raw-tracks after data update
|
||||||
|
|
||||||
// instantiate our simulation view and set it as the activity's content
|
|
||||||
mBInstallerView = new BInstallerView(this);
|
|
||||||
setContentView(mBInstallerView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
/*
|
|
||||||
* when the activity is resumed, we acquire a wake-lock so that the
|
|
||||||
* screen stays on, since the user will likely not be fiddling with the
|
|
||||||
* screen or buttons.
|
|
||||||
*/
|
|
||||||
mWakeLock.acquire();
|
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter();
|
IntentFilter filter = new IntentFilter();
|
||||||
filter.addAction(DOWNLOAD_ACTION);
|
filter.addAction(DOWNLOAD_ACTION);
|
||||||
|
|
||||||
myReceiver = new DownloadReceiver();
|
downloadReceiver = new DownloadReceiver();
|
||||||
registerReceiver(myReceiver, filter);
|
registerReceiver(downloadReceiver, filter);
|
||||||
|
|
||||||
// Start the download manager
|
|
||||||
mBInstallerView.startInstaller();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
||||||
|
|
||||||
super.onPause();
|
|
||||||
|
|
||||||
mWakeLock.release();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (myReceiver != null) unregisterReceiver(myReceiver);
|
if (downloadReceiver != null) unregisterReceiver(downloadReceiver);
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
protected Dialog onCreateDialog(int id) {
|
protected Dialog onCreateDialog(int id) {
|
||||||
AlertDialog.Builder builder;
|
AlertDialog.Builder builder;
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
@ -116,7 +193,7 @@ public class BInstallerActivity extends Activity {
|
||||||
.setTitle("Confirm Delete")
|
.setTitle("Confirm Delete")
|
||||||
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
.setMessage("Really delete?").setPositiveButton("Yes", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
mBInstallerView.deleteSelectedTiles();
|
deleteSelectedTiles();
|
||||||
}
|
}
|
||||||
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
|
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
@ -129,29 +206,93 @@ public class BInstallerActivity extends Activity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void showConfirmDelete() {
|
public void showConfirmDelete() {
|
||||||
showDialog(DIALOG_CONFIRM_DELETE_ID);
|
showDialog(DIALOG_CONFIRM_DELETE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Integer> dialogIds = new HashSet<Integer>();
|
private void scanExistingFiles() {
|
||||||
|
mBInstallerView.clearAllTilesStatus(MASK_CURRENT_RD5 | MASK_INSTALLED_RD5 | MASK_DELETED_RD5 | MASK_SELECTED_RD5);
|
||||||
|
|
||||||
private void showNewDialog(int id) {
|
scanExistingFiles(new File(mBaseDir, "brouter/segments4"));
|
||||||
if (dialogIds.contains(Integer.valueOf(id))) {
|
|
||||||
removeDialog(id);
|
File secondary = RoutingHelper.getSecondarySegmentDir(new File(mBaseDir, "brouter/segments4"));
|
||||||
|
if (secondary != null) {
|
||||||
|
scanExistingFiles(secondary);
|
||||||
}
|
}
|
||||||
dialogIds.add(Integer.valueOf(id));
|
|
||||||
showDialog(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scanExistingFiles(File dir) {
|
||||||
|
String[] fileNames = dir.list();
|
||||||
|
if (fileNames == null) return;
|
||||||
|
String suffix = ".rd5";
|
||||||
|
for (String fileName : fileNames) {
|
||||||
|
if (fileName.endsWith(suffix)) {
|
||||||
|
String basename = fileName.substring(0, fileName.length() - suffix.length());
|
||||||
|
int tileIndex = tileForBaseName(basename);
|
||||||
|
mBInstallerView.setTileStatus(tileIndex, MASK_INSTALLED_RD5);
|
||||||
|
|
||||||
static public long getAvailableSpace(String baseDir) {
|
long age = System.currentTimeMillis() - new File(dir, fileName).lastModified();
|
||||||
StatFs stat = new StatFs(baseDir);
|
if (age < 10800000) mBInstallerView.setTileStatus(tileIndex, MASK_CURRENT_RD5); // 3 hours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
private void deleteSelectedTiles() {
|
||||||
return stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
|
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_DELETED_RD5);
|
||||||
} else {
|
for (int tileIndex : selectedTiles) {
|
||||||
return stat.getAvailableBlocks() * stat.getBlockSize();
|
new File(mBaseDir, "brouter/segments4/" + baseNameForTile(tileIndex) + ".rd5").delete();
|
||||||
|
}
|
||||||
|
scanExistingFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadSelectedTiles() {
|
||||||
|
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_SELECTED_RD5);
|
||||||
|
downloadAll(selectedTiles);
|
||||||
|
mBInstallerView.clearAllTilesStatus(MASK_SELECTED_RD5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadInstalledTiles() {
|
||||||
|
ArrayList<Integer> selectedTiles = mBInstallerView.getSelectedTiles(MASK_INSTALLED_RD5);
|
||||||
|
downloadAll(selectedTiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int tileForBaseName(String basename) {
|
||||||
|
String uname = basename.toUpperCase(Locale.ROOT);
|
||||||
|
int idx = uname.indexOf("_");
|
||||||
|
if (idx < 0) return -1;
|
||||||
|
String slon = uname.substring(0, idx);
|
||||||
|
String slat = uname.substring(idx + 1);
|
||||||
|
int ilon = slon.charAt(0) == 'W' ? -Integer.parseInt(slon.substring(1)) :
|
||||||
|
(slon.charAt(0) == 'E' ? Integer.parseInt(slon.substring(1)) : -1);
|
||||||
|
int ilat = slat.charAt(0) == 'S' ? -Integer.parseInt(slat.substring(1)) :
|
||||||
|
(slat.charAt(0) == 'N' ? Integer.parseInt(slat.substring(1)) : -1);
|
||||||
|
if (ilon < -180 || ilon >= 180 || ilon % 5 != 0) return -1;
|
||||||
|
if (ilat < -90 || ilat >= 90 || ilat % 5 != 0) return -1;
|
||||||
|
return (ilon + 180) / 5 + 72 * ((ilat + 90) / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String baseNameForTile(int tileIndex) {
|
||||||
|
int lon = (tileIndex % 72) * 5 - 180;
|
||||||
|
int lat = (tileIndex / 72) * 5 - 90;
|
||||||
|
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
||||||
|
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
||||||
|
return slon + "_" + slat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DownloadReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent.hasExtra("txt")) {
|
||||||
|
String txt = intent.getStringExtra("txt");
|
||||||
|
boolean ready = intent.getBooleanExtra("ready", false);
|
||||||
|
if (!ready) {
|
||||||
|
mSegmentsView.setVisibility(View.VISIBLE);
|
||||||
|
mDownloadInfo.setVisibility(View.GONE);
|
||||||
|
scanExistingFiles();
|
||||||
|
}
|
||||||
|
mDownloadInfoText.setText(txt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,7 @@
|
||||||
package btools.routingapp;
|
package btools.routingapp;
|
||||||
|
|
||||||
import java.io.File;
|
import android.annotation.SuppressLint;
|
||||||
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.ArrayList;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
@ -20,262 +9,37 @@ import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.PowerManager;
|
|
||||||
import android.os.StatFs;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.view.GestureDetector;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
import android.view.ScaleGestureDetector;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import btools.mapaccess.PhysicalFile;
|
import java.io.IOException;
|
||||||
import btools.mapaccess.Rd5DiffManager;
|
import java.io.InputStream;
|
||||||
import btools.mapaccess.Rd5DiffTool;
|
import java.util.ArrayList;
|
||||||
import btools.router.RoutingHelper;
|
|
||||||
import btools.util.ProgressListener;
|
|
||||||
|
|
||||||
public class BInstallerView extends View {
|
public class BInstallerView extends View {
|
||||||
private static final int MASK_SELECTED_RD5 = 1;
|
public static final int MASK_SELECTED_RD5 = 1;
|
||||||
private static final int MASK_DELETED_RD5 = 2;
|
public static final int MASK_DELETED_RD5 = 2;
|
||||||
private static final int MASK_INSTALLED_RD5 = 4;
|
public static final int MASK_INSTALLED_RD5 = 4;
|
||||||
private static final int MASK_CURRENT_RD5 = 8;
|
public static final int MASK_CURRENT_RD5 = 8;
|
||||||
|
private static final float SCALE_GRID_VISIBLE = 3;
|
||||||
private int imgwOrig;
|
private final Bitmap bmp;
|
||||||
private int imghOrig;
|
private final float[] testVector = new float[2];
|
||||||
private float scaleOrig;
|
private final int[] tileStatus;
|
||||||
|
private final Matrix mat;
|
||||||
private int imgw;
|
private final GestureDetector mGestureDetector;
|
||||||
private int imgh;
|
private final ScaleGestureDetector mScaleGestureDetector;
|
||||||
|
Paint paintGrid = new Paint();
|
||||||
private float lastDownX;
|
Paint paintTiles = new Paint();
|
||||||
private float lastDownY;
|
|
||||||
|
|
||||||
private Bitmap bmp;
|
|
||||||
|
|
||||||
private float viewscale;
|
private float viewscale;
|
||||||
|
|
||||||
private float[] testVector = new float[2];
|
|
||||||
|
|
||||||
private int[] tileStatus;
|
|
||||||
|
|
||||||
private boolean tilesVisible = false;
|
private boolean tilesVisible = false;
|
||||||
|
private OnSelectListener mOnSelectListener;
|
||||||
|
|
||||||
private long availableSize;
|
public BInstallerView(Context context, AttributeSet attrs) {
|
||||||
private File baseDir;
|
super(context, attrs);
|
||||||
private File segmentDir;
|
|
||||||
|
|
||||||
private boolean isDownloading = false;
|
|
||||||
public static boolean downloadCanceled = false;
|
|
||||||
|
|
||||||
private long currentDownloadSize;
|
|
||||||
private String currentDownloadFile = "";
|
|
||||||
private volatile String currentDownloadOperation = "";
|
|
||||||
private String downloadAction = "";
|
|
||||||
private volatile String newDownloadAction = "";
|
|
||||||
|
|
||||||
private long totalSize = 0;
|
|
||||||
private long rd5Tiles = 0;
|
|
||||||
private long delTiles = 0;
|
|
||||||
|
|
||||||
Paint pnt_1 = new Paint();
|
|
||||||
Paint pnt_2 = new Paint();
|
|
||||||
Paint paint = new Paint();
|
|
||||||
|
|
||||||
Activity mActivity;
|
|
||||||
|
|
||||||
|
|
||||||
protected String baseNameForTile(int tileIndex) {
|
|
||||||
int lon = (tileIndex % 72) * 5 - 180;
|
|
||||||
int lat = (tileIndex / 72) * 5 - 90;
|
|
||||||
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
|
||||||
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
|
|
||||||
return slon + "_" + slat;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int gridPos2Tileindex(int ix, int iy) {
|
|
||||||
return (35 - iy) * 72 + (ix >= 70 ? ix - 70 : ix + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int tileForBaseName(String basename) {
|
|
||||||
String uname = basename.toUpperCase(Locale.ROOT);
|
|
||||||
int idx = uname.indexOf("_");
|
|
||||||
if (idx < 0) return -1;
|
|
||||||
String slon = uname.substring(0, idx);
|
|
||||||
String slat = uname.substring(idx + 1);
|
|
||||||
int ilon = slon.charAt(0) == 'W' ? -Integer.parseInt(slon.substring(1)) :
|
|
||||||
(slon.charAt(0) == 'E' ? Integer.parseInt(slon.substring(1)) : -1);
|
|
||||||
int ilat = slat.charAt(0) == 'S' ? -Integer.parseInt(slat.substring(1)) :
|
|
||||||
(slat.charAt(0) == 'N' ? Integer.parseInt(slat.substring(1)) : -1);
|
|
||||||
if (ilon < -180 || ilon >= 180 || ilon % 5 != 0) return -1;
|
|
||||||
if (ilat < -90 || ilat >= 90 || ilat % 5 != 0) return -1;
|
|
||||||
return (ilon + 180) / 5 + 72 * ((ilat + 90) / 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isDownloadCanceled() {
|
|
||||||
return downloadCanceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleDownload() {
|
|
||||||
if (isDownloading) {
|
|
||||||
downloadCanceled = true;
|
|
||||||
downloadAction = "Canceling...";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delTiles > 0) {
|
|
||||||
((BInstallerActivity) getContext()).showConfirmDelete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tidx_min = -1;
|
|
||||||
int min_size = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
ArrayList<Integer> downloadList = new ArrayList<>();
|
|
||||||
// prepare download list
|
|
||||||
for (int ix = 0; ix < 72; ix++) {
|
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
|
||||||
if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
|
|
||||||
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
|
||||||
downloadList.add(tidx);
|
|
||||||
if (tilesize > 0 && tilesize < min_size) {
|
|
||||||
tidx_min = tidx;
|
|
||||||
min_size = tilesize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (downloadList.size() > 0) {
|
|
||||||
isDownloading = true;
|
|
||||||
downloadAll(downloadList);
|
|
||||||
for (Integer i : downloadList) {
|
|
||||||
tileStatus[i.intValue()] ^= tileStatus[i.intValue()] & MASK_SELECTED_RD5;
|
|
||||||
}
|
|
||||||
downloadList.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void downloadAll(ArrayList<Integer> downloadList) {
|
|
||||||
ArrayList<String> urlparts = new ArrayList<>();
|
|
||||||
for (Integer i : downloadList) {
|
|
||||||
urlparts.add(baseNameForTile(i.intValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
currentDownloadOperation = "Start download ...";
|
|
||||||
downloadAction = "";
|
|
||||||
downloadCanceled = false;
|
|
||||||
isDownloading = true;
|
|
||||||
|
|
||||||
//final DownloadBackground downloadTask = new DownloadBackground(getContext(), urlparts, baseDir);
|
|
||||||
//downloadTask.execute( );
|
|
||||||
Intent intent = new Intent(mActivity, DownloadService.class);
|
|
||||||
intent.putExtra("dir", baseDir.getAbsolutePath() + "/brouter/");
|
|
||||||
intent.putExtra("urlparts", urlparts);
|
|
||||||
mActivity.startService(intent);
|
|
||||||
|
|
||||||
deleteRawTracks(); // invalidate raw-tracks after data update
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void downloadDone(boolean success) {
|
|
||||||
isDownloading = false;
|
|
||||||
if (success) {
|
|
||||||
scanExistingFiles();
|
|
||||||
toggleDownload(); // keep on if no error
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setState(String txt, boolean b) {
|
|
||||||
currentDownloadOperation = txt;
|
|
||||||
downloadAction = "";
|
|
||||||
isDownloading = b;
|
|
||||||
if (!b) {
|
|
||||||
scanExistingFiles();
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int tileIndex(float x, float y) {
|
|
||||||
int ix = (int) (72.f * x / bmp.getWidth());
|
|
||||||
int iy = (int) (36.f * y / bmp.getHeight());
|
|
||||||
if (ix >= 0 && ix < 72 && iy >= 0 && iy < 36) return gridPos2Tileindex(ix, iy);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearTileSelection(int mask) {
|
|
||||||
// clear selection if zooming out
|
|
||||||
for (int ix = 0; ix < 72; ix++)
|
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
|
||||||
tileStatus[tidx] ^= tileStatus[tidx] & mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get back the current image scale
|
|
||||||
private float currentScale() {
|
|
||||||
testVector[1] = 1.f;
|
|
||||||
mat.mapVectors(testVector);
|
|
||||||
return testVector[1] / viewscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteRawTracks() {
|
|
||||||
File modeDir = new File(baseDir, "brouter/modes");
|
|
||||||
String[] fileNames = modeDir.list();
|
|
||||||
if (fileNames == null) return;
|
|
||||||
for (String fileName : fileNames) {
|
|
||||||
if (fileName.endsWith("_rawtrack.dat")) {
|
|
||||||
File f = new File(modeDir, fileName);
|
|
||||||
f.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scanExistingFiles() {
|
|
||||||
clearTileSelection(MASK_INSTALLED_RD5 | MASK_CURRENT_RD5);
|
|
||||||
|
|
||||||
scanExistingFiles(new File(baseDir, "brouter/segments4"));
|
|
||||||
|
|
||||||
File secondary = RoutingHelper.getSecondarySegmentDir(new File(baseDir, "brouter/segments4"));
|
|
||||||
if (secondary != null) {
|
|
||||||
scanExistingFiles(secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
availableSize = -1;
|
|
||||||
try {
|
|
||||||
availableSize = (long) ((BInstallerActivity) getContext()).getAvailableSpace(baseDir.getAbsolutePath());
|
|
||||||
//StatFs stat = new StatFs(baseDir.getAbsolutePath ());
|
|
||||||
//availableSize = (long)stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
|
|
||||||
} catch (Exception e) { /* ignore */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scanExistingFiles(File dir) {
|
|
||||||
String[] fileNames = dir.list();
|
|
||||||
if (fileNames == null) return;
|
|
||||||
String suffix = ".rd5";
|
|
||||||
for (String fileName : fileNames) {
|
|
||||||
if (fileName.endsWith(suffix)) {
|
|
||||||
String basename = fileName.substring(0, fileName.length() - suffix.length());
|
|
||||||
int tidx = tileForBaseName(basename);
|
|
||||||
tileStatus[tidx] |= MASK_INSTALLED_RD5;
|
|
||||||
|
|
||||||
long age = System.currentTimeMillis() - new File(dir, fileName).lastModified();
|
|
||||||
if (age < 10800000) tileStatus[tidx] |= MASK_CURRENT_RD5; // 3 hours
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Matrix mat;
|
|
||||||
private Matrix matText;
|
|
||||||
|
|
||||||
public void startInstaller() {
|
|
||||||
|
|
||||||
baseDir = ConfigHelper.getBaseDir(getContext());
|
|
||||||
segmentDir = new File(baseDir, "brouter/segments4");
|
|
||||||
try {
|
try {
|
||||||
AssetManager assetManager = getContext().getAssets();
|
AssetManager assetManager = getContext().getAssets();
|
||||||
InputStream istr = assetManager.open("world.png");
|
InputStream istr = assetManager.open("world.png");
|
||||||
|
@ -286,56 +50,111 @@ public class BInstallerView extends View {
|
||||||
}
|
}
|
||||||
|
|
||||||
tileStatus = new int[72 * 36];
|
tileStatus = new int[72 * 36];
|
||||||
scanExistingFiles();
|
|
||||||
|
|
||||||
float scaleX = imgwOrig / ((float) bmp.getWidth());
|
|
||||||
float scaley = imghOrig / ((float) bmp.getHeight());
|
|
||||||
|
|
||||||
viewscale = scaleX < scaley ? scaleX : scaley;
|
|
||||||
|
|
||||||
mat = new Matrix();
|
mat = new Matrix();
|
||||||
mat.postScale(viewscale, viewscale);
|
mGestureDetector = new GestureDetector(context, new GestureListener());
|
||||||
tilesVisible = false;
|
mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BInstallerView(Context context) {
|
public void setOnSelectListener(OnSelectListener listener) {
|
||||||
super(context);
|
mOnSelectListener = listener;
|
||||||
mActivity = (Activity) context;
|
}
|
||||||
|
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
private void setRatio(float ratio, float focusX, float focusY) {
|
||||||
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
if (currentScale() * ratio >= 1) {
|
||||||
imgwOrig = metrics.widthPixels;
|
mat.postScale(ratio, ratio, focusX, focusY);
|
||||||
imghOrig = metrics.heightPixels;
|
fitBounds();
|
||||||
int im = imgwOrig > imghOrig ? imgwOrig : imghOrig;
|
tilesVisible = currentScale() >= SCALE_GRID_VISIBLE;
|
||||||
|
|
||||||
scaleOrig = im / 480.f;
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
matText = new Matrix();
|
private void setScale(float scale, float focusX, float focusY) {
|
||||||
matText.preScale(scaleOrig, scaleOrig);
|
float ratio = scale / currentScale();
|
||||||
|
setRatio(ratio, focusX, focusY);
|
||||||
|
}
|
||||||
|
|
||||||
imgw = (int) (imgwOrig / scaleOrig);
|
public void setTileStatus(int tileIndex, int tileMask) {
|
||||||
imgh = (int) (imghOrig / scaleOrig);
|
tileStatus[tileIndex] |= tileMask;
|
||||||
|
if (mOnSelectListener != null) {
|
||||||
|
mOnSelectListener.onSelect();
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
totalSize = 0;
|
public void toggleTileStatus(int tileIndex, int tileMask) {
|
||||||
rd5Tiles = 0;
|
tileStatus[tileIndex] ^= tileMask;
|
||||||
delTiles = 0;
|
if (mOnSelectListener != null) {
|
||||||
|
mOnSelectListener.onSelect();
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearAllTilesStatus(int tileMask) {
|
||||||
|
for (int ix = 0; ix < 72; ix++) {
|
||||||
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
|
int tileIndex = gridPos2Tileindex(ix, iy);
|
||||||
|
tileStatus[tileIndex] ^= tileStatus[tileIndex] & tileMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mOnSelectListener != null) {
|
||||||
|
mOnSelectListener.onSelect();
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Integer> getSelectedTiles(int tileMask) {
|
||||||
|
ArrayList<Integer> selectedTiles = new ArrayList<>();
|
||||||
|
for (int ix = 0; ix < 72; ix++) {
|
||||||
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
|
int tileIndex = gridPos2Tileindex(ix, iy);
|
||||||
|
if ((tileStatus[tileIndex] & tileMask) != 0 && BInstallerSizes.getRd5Size(tileIndex) > 0) {
|
||||||
|
selectedTiles.add(tileIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedTiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int gridPos2Tileindex(int ix, int iy) {
|
||||||
|
return (35 - iy) * 72 + (ix >= 70 ? ix - 70 : ix + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int tileIndex(float x, float y) {
|
||||||
|
int ix = (int) (72.f * x / bmp.getWidth());
|
||||||
|
int iy = (int) (36.f * y / bmp.getHeight());
|
||||||
|
if (ix >= 0 && ix < 72 && iy >= 0 && iy < 36) return gridPos2Tileindex(ix, iy);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get back the current image scale
|
||||||
|
private float currentScale() {
|
||||||
|
testVector[0] = 1.f;
|
||||||
|
mat.mapVectors(testVector);
|
||||||
|
return testVector[0] / viewscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
}
|
|
||||||
|
|
||||||
private void toast(String msg) {
|
int imgwOrig = getWidth();
|
||||||
Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
|
int imghOrig = getHeight();
|
||||||
|
|
||||||
|
float scaleX = imgwOrig / ((float) bmp.getWidth());
|
||||||
|
float scaleY = imghOrig / ((float) bmp.getHeight());
|
||||||
|
|
||||||
|
viewscale = Math.max(scaleX, scaleY);
|
||||||
|
|
||||||
|
mat.postScale(viewscale, viewscale);
|
||||||
|
tilesVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
if (!isDownloading) {
|
canvas.setMatrix(mat);
|
||||||
canvas.setMatrix(mat);
|
canvas.drawBitmap(bmp, 0, 0, null);
|
||||||
canvas.drawBitmap(bmp, 0, 0, null);
|
|
||||||
}
|
|
||||||
// draw 5*5 lattice starting at scale=3
|
// draw 5*5 lattice starting at scale=3
|
||||||
|
|
||||||
int iw = bmp.getWidth();
|
int iw = bmp.getWidth();
|
||||||
|
@ -343,290 +162,164 @@ public class BInstallerView extends View {
|
||||||
float fw = iw / 72.f;
|
float fw = iw / 72.f;
|
||||||
float fh = ih / 36.f;
|
float fh = ih / 36.f;
|
||||||
|
|
||||||
boolean drawGrid = tilesVisible && !isDownloading;
|
if (tilesVisible) {
|
||||||
|
paintGrid.setColor(Color.GREEN);
|
||||||
if (drawGrid) {
|
paintGrid.setStyle(Paint.Style.STROKE);
|
||||||
|
for (int ix = 0; ix < 72; ix++) {
|
||||||
pnt_1.setColor(Color.GREEN);
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
|
int tidx = gridPos2Tileindex(ix, iy);
|
||||||
for (int ix = 1; ix < 72; ix++) {
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
||||||
float fx = fw * ix;
|
if (tilesize > 0) {
|
||||||
canvas.drawLine(fx, 0, fx, ih, pnt_1);
|
canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, paintGrid);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (int iy = 1; iy < 36; iy++) {
|
|
||||||
float fy = fh * iy;
|
|
||||||
canvas.drawLine(0, fy, iw, fy, pnt_1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rd5Tiles = 0;
|
|
||||||
delTiles = 0;
|
|
||||||
totalSize = 0;
|
|
||||||
int mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
|
|
||||||
int mask3 = mask2 | MASK_CURRENT_RD5;
|
|
||||||
|
|
||||||
pnt_2.setColor(Color.GRAY);
|
int mask2 = MASK_SELECTED_RD5 | MASK_DELETED_RD5 | MASK_INSTALLED_RD5;
|
||||||
pnt_2.setStrokeWidth(1);
|
int mask3 = mask2 | MASK_CURRENT_RD5;
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5, mask3, false, false, drawGrid);
|
|
||||||
pnt_2.setColor(Color.BLUE);
|
|
||||||
pnt_2.setStrokeWidth(1);
|
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3, false, false, drawGrid);
|
|
||||||
pnt_2.setColor(Color.GREEN);
|
|
||||||
pnt_2.setStrokeWidth(2);
|
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5, mask2, true, false, drawGrid);
|
|
||||||
pnt_2.setColor(Color.YELLOW);
|
|
||||||
pnt_2.setStrokeWidth(2);
|
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2, true, false, drawGrid);
|
|
||||||
pnt_2.setColor(Color.RED);
|
|
||||||
pnt_2.setStrokeWidth(2);
|
|
||||||
drawSelectedTiles(canvas, pnt_2, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2, false, true, drawGrid);
|
|
||||||
|
|
||||||
canvas.setMatrix(matText);
|
paintTiles.setStyle(Paint.Style.STROKE);
|
||||||
|
paintTiles.setColor(Color.GRAY);
|
||||||
paint.setColor(Color.RED);
|
paintTiles.setStrokeWidth(1);
|
||||||
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5, mask3);
|
||||||
long mb = 1024 * 1024;
|
paintTiles.setColor(Color.BLUE);
|
||||||
|
paintTiles.setStrokeWidth(1);
|
||||||
if (isDownloading) {
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_INSTALLED_RD5 | MASK_CURRENT_RD5, mask3);
|
||||||
String sizeHint = currentDownloadSize > 0 ? " (" + ((currentDownloadSize + mb - 1) / mb) + " MB)" : "";
|
paintTiles.setColor(Color.GREEN);
|
||||||
paint.setTextSize(30);
|
paintTiles.setStrokeWidth(2);
|
||||||
canvas.drawText(currentDownloadOperation, 30, (imgh / 3) * 2 - 30, paint);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5, mask2);
|
||||||
// canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
|
paintTiles.setColor(Color.YELLOW);
|
||||||
canvas.drawText(downloadAction, 30, (imgh / 3) * 2, paint);
|
paintTiles.setStrokeWidth(2);
|
||||||
}
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_SELECTED_RD5 | MASK_INSTALLED_RD5, mask2);
|
||||||
if (!tilesVisible && !isDownloading) {
|
paintTiles.setColor(Color.RED);
|
||||||
paint.setTextSize(35);
|
paintTiles.setStrokeWidth(2);
|
||||||
canvas.drawText("Touch region to zoom in!", 30, (imgh / 3) * 2, paint);
|
drawSelectedTiles(canvas, paintTiles, fw, fh, MASK_DELETED_RD5 | MASK_INSTALLED_RD5, mask2);
|
||||||
}
|
|
||||||
paint.setTextSize(20);
|
|
||||||
|
|
||||||
|
|
||||||
String totmb = ((totalSize + mb - 1) / mb) + " MB";
|
|
||||||
String freemb = availableSize >= 0 ? ((availableSize + mb - 1) / mb) + " MB" : "?";
|
|
||||||
canvas.drawText("Selected segments=" + rd5Tiles, 10, 25, paint);
|
|
||||||
canvas.drawText("Size=" + totmb + " Free=" + freemb, 10, 45, paint);
|
|
||||||
|
|
||||||
|
|
||||||
String btnText = null;
|
|
||||||
if (isDownloading) btnText = "Cancel Download";
|
|
||||||
else if (delTiles > 0) btnText = "Delete " + delTiles + " tiles";
|
|
||||||
else if (rd5Tiles > 0) btnText = "Start Download";
|
|
||||||
else if (tilesVisible &&
|
|
||||||
rd5Tiles == 0 &&
|
|
||||||
RoutingHelper.hasDirectoryAnyDatafiles(segmentDir)) btnText = "Update all";
|
|
||||||
|
|
||||||
if (btnText != null) {
|
|
||||||
canvas.drawLine(imgw - btnw, imgh - btnh, imgw - btnw, imgh - 2, paint);
|
|
||||||
canvas.drawLine(imgw - btnw, imgh - btnh, imgw - 2, imgh - btnh, paint);
|
|
||||||
canvas.drawLine(imgw - btnw, imgh - btnh, imgw - btnw, imgh - 2, paint);
|
|
||||||
canvas.drawLine(imgw - 2, imgh - btnh, imgw - 2, imgh - 2, paint);
|
|
||||||
canvas.drawLine(imgw - btnw, imgh - 2, imgw - 2, imgh - 2, paint);
|
|
||||||
canvas.drawText(btnText, imgw - btnw + 5, imgh - 10, paint);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int btnh = 40;
|
private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask) {
|
||||||
int btnw = 160;
|
|
||||||
|
|
||||||
|
|
||||||
float tx, ty;
|
|
||||||
|
|
||||||
private void drawSelectedTiles(Canvas canvas, Paint pnt, float fw, float fh, int status, int mask, boolean doCount, boolean cntDel, boolean doDraw) {
|
|
||||||
for (int ix = 0; ix < 72; ix++)
|
for (int ix = 0; ix < 72; ix++)
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
for (int iy = 0; iy < 36; iy++) {
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
int tidx = gridPos2Tileindex(ix, iy);
|
||||||
if ((tileStatus[tidx] & mask) == status) {
|
if ((tileStatus[tidx] & mask) == status) {
|
||||||
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
||||||
if (tilesize > 0) {
|
if (tilesize > 0) {
|
||||||
if (doCount) {
|
|
||||||
rd5Tiles++;
|
|
||||||
totalSize += BInstallerSizes.getRd5Size(tidx);
|
|
||||||
}
|
|
||||||
if (cntDel) {
|
|
||||||
delTiles++;
|
|
||||||
totalSize += BInstallerSizes.getRd5Size(tidx);
|
|
||||||
}
|
|
||||||
if (!doDraw)
|
|
||||||
continue;
|
|
||||||
// draw cross
|
// draw cross
|
||||||
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
||||||
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
|
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
|
||||||
|
|
||||||
// draw frame
|
// draw frame
|
||||||
canvas.drawLine(fw * ix, fh * iy, fw * (ix + 1), fh * iy, pnt);
|
canvas.drawRect(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * iy, pnt);
|
||||||
canvas.drawLine(fw * ix, fh * (iy + 1), fw * (ix + 1), fh * (iy + 1), pnt);
|
|
||||||
canvas.drawLine(fw * ix, fh * iy, fw * ix, fh * (iy + 1), pnt);
|
|
||||||
canvas.drawLine(fw * (ix + 1), fh * iy, fw * (ix + 1), fh * (iy + 1), pnt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteSelectedTiles() {
|
private void fitBounds() {
|
||||||
for (int ix = 0; ix < 72; ix++) {
|
float[] srcPoints = new float[]{
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
0, 0,
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
bmp.getWidth(), bmp.getHeight()
|
||||||
if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
|
};
|
||||||
new File(baseDir, "brouter/segments4/" + baseNameForTile(tidx) + ".rd5").delete();
|
float[] dstPoints = new float[srcPoints.length];
|
||||||
}
|
float transX = 0;
|
||||||
}
|
float transY = 0;
|
||||||
|
mat.mapPoints(dstPoints, srcPoints);
|
||||||
|
if (dstPoints[0] > 0) {
|
||||||
|
transX = -dstPoints[0];
|
||||||
|
} else if (dstPoints[2] < getWidth()) {
|
||||||
|
transX = getWidth() - dstPoints[2];
|
||||||
|
}
|
||||||
|
if (dstPoints[1] > 0) {
|
||||||
|
transY = -dstPoints[1];
|
||||||
|
} else if (dstPoints[3] < getHeight()) {
|
||||||
|
transY = getHeight() - dstPoints[3];
|
||||||
|
}
|
||||||
|
if (transX != 0 || transY != 0) {
|
||||||
|
mat.postTranslate(transX, transY);
|
||||||
}
|
}
|
||||||
scanExistingFiles();
|
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
|
boolean retVal = mScaleGestureDetector.onTouchEvent(event);
|
||||||
// get pointer index from the event object
|
retVal = mGestureDetector.onTouchEvent(event) || retVal;
|
||||||
int pointerIndex = event.getActionIndex();
|
return retVal || super.onTouchEvent(event);
|
||||||
|
|
||||||
// get pointer ID
|
|
||||||
int pointerId = event.getPointerId(pointerIndex);
|
|
||||||
|
|
||||||
// get masked (not specific to a pointer) action
|
|
||||||
int maskedAction = event.getActionMasked();
|
|
||||||
|
|
||||||
switch (maskedAction) {
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
|
||||||
lastDownX = event.getX();
|
|
||||||
lastDownY = event.getY();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MotionEvent.ACTION_MOVE: { // a pointer was moved
|
|
||||||
|
|
||||||
if (isDownloading) break;
|
|
||||||
int np = event.getPointerCount();
|
|
||||||
int nh = event.getHistorySize();
|
|
||||||
if (nh == 0) break;
|
|
||||||
|
|
||||||
float x0 = event.getX(0);
|
|
||||||
float y0 = event.getY(0);
|
|
||||||
float hx0 = event.getHistoricalX(0, 0);
|
|
||||||
float hy0 = event.getHistoricalY(0, 0);
|
|
||||||
|
|
||||||
if (np > 1) // multi-touch
|
|
||||||
{
|
|
||||||
float x1 = event.getX(1);
|
|
||||||
float y1 = event.getY(1);
|
|
||||||
float hx1 = event.getHistoricalX(1, 0);
|
|
||||||
float hy1 = event.getHistoricalY(1, 0);
|
|
||||||
|
|
||||||
float r = (float) Math.sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0));
|
|
||||||
float hr = (float) Math.sqrt((hx1 - hx0) * (hx1 - hx0) + (hy1 - hy0) * (hy1 - hy0));
|
|
||||||
|
|
||||||
if (hr > 0.) {
|
|
||||||
float ratio = r / hr;
|
|
||||||
|
|
||||||
float mx = (x1 + x0) / 2.f;
|
|
||||||
float my = (y1 + y0) / 2.f;
|
|
||||||
|
|
||||||
float scale = currentScale();
|
|
||||||
float newscale = scale * ratio;
|
|
||||||
|
|
||||||
if (newscale > 10.f) ratio *= (10.f / newscale);
|
|
||||||
if (newscale < 0.5f) ratio *= (0.5f / newscale);
|
|
||||||
|
|
||||||
mat.postScale(ratio, ratio, mx, my);
|
|
||||||
|
|
||||||
mat.postScale(ratio, ratio, mx, my);
|
|
||||||
|
|
||||||
boolean tilesv = currentScale() >= 3.f;
|
|
||||||
if (tilesVisible && !tilesv) {
|
|
||||||
clearTileSelection(MASK_SELECTED_RD5 | MASK_DELETED_RD5);
|
|
||||||
}
|
|
||||||
tilesVisible = tilesv;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mat.postTranslate(x0 - hx0, y0 - hy0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
|
|
||||||
long downTime = event.getEventTime() - event.getDownTime();
|
|
||||||
|
|
||||||
if (downTime < 5 || downTime > 500) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.abs(lastDownX - event.getX()) > 10 || Math.abs(lastDownY - event.getY()) > 10) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// download button?
|
|
||||||
if ((delTiles > 0 || rd5Tiles >= 0 || isDownloading) && event.getX() > imgwOrig - btnw * scaleOrig && event.getY() > imghOrig - btnh * scaleOrig) {
|
|
||||||
if (rd5Tiles == 0) {
|
|
||||||
for (int ix = 0; ix < 72; ix++) {
|
|
||||||
for (int iy = 0; iy < 36; iy++) {
|
|
||||||
int tidx = gridPos2Tileindex(ix, iy);
|
|
||||||
if (tidx != -1) {
|
|
||||||
if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
|
|
||||||
tileStatus[tidx] |= MASK_SELECTED_RD5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
toggleDownload();
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tilesVisible) {
|
|
||||||
float scale = currentScale();
|
|
||||||
if (scale > 0f && scale < 5f) {
|
|
||||||
float ratio = 5f / scale;
|
|
||||||
mat.postScale(ratio, ratio, event.getX(), event.getY());
|
|
||||||
tilesVisible = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDownloading) break;
|
|
||||||
|
|
||||||
Matrix imat = new Matrix();
|
|
||||||
if (mat.invert(imat)) {
|
|
||||||
float[] touchpoint = new float[2];
|
|
||||||
touchpoint[0] = event.getX();
|
|
||||||
touchpoint[1] = event.getY();
|
|
||||||
imat.mapPoints(touchpoint);
|
|
||||||
|
|
||||||
int tidx = tileIndex(touchpoint[0], touchpoint[1]);
|
|
||||||
if (tidx != -1) {
|
|
||||||
if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
|
|
||||||
tileStatus[tidx] ^= MASK_SELECTED_RD5;
|
|
||||||
if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
|
|
||||||
tileStatus[tidx] |= MASK_DELETED_RD5;
|
|
||||||
}
|
|
||||||
} else if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
|
|
||||||
tileStatus[tidx] ^= MASK_DELETED_RD5;
|
|
||||||
} else {
|
|
||||||
tileStatus[tidx] ^= MASK_SELECTED_RD5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tx = touchpoint[0];
|
|
||||||
ty = touchpoint[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_POINTER_UP:
|
|
||||||
case MotionEvent.ACTION_CANCEL: {
|
|
||||||
// TODO use data
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OnSelectListener {
|
||||||
|
void onSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
class GestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||||
|
if (tilesVisible) {
|
||||||
|
Matrix imat = new Matrix();
|
||||||
|
if (mat.invert(imat)) {
|
||||||
|
float[] touchPoint = {e.getX(), e.getY()};
|
||||||
|
imat.mapPoints(touchPoint);
|
||||||
|
|
||||||
|
int tidx = tileIndex(touchPoint[0], touchPoint[1]);
|
||||||
|
if (tidx != -1) {
|
||||||
|
if ((tileStatus[tidx] & MASK_SELECTED_RD5) != 0) {
|
||||||
|
toggleTileStatus(tidx, MASK_SELECTED_RD5);
|
||||||
|
if ((tileStatus[tidx] & MASK_INSTALLED_RD5) != 0) {
|
||||||
|
setTileStatus(tidx, MASK_DELETED_RD5);
|
||||||
|
}
|
||||||
|
} else if ((tileStatus[tidx] & MASK_DELETED_RD5) != 0) {
|
||||||
|
toggleTileStatus(tidx, MASK_DELETED_RD5);
|
||||||
|
} else {
|
||||||
|
toggleTileStatus(tidx, MASK_SELECTED_RD5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onDoubleTap(MotionEvent e) {
|
||||||
|
if (!tilesVisible) {
|
||||||
|
setScale(5, e.getX(), e.getY());
|
||||||
|
} else {
|
||||||
|
setScale(1, e.getX(), e.getY());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||||
|
mat.postTranslate(-distanceX, -distanceY);
|
||||||
|
fitBounds();
|
||||||
|
invalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
|
||||||
|
float focusX = scaleGestureDetector.getFocusX();
|
||||||
|
float focusY = scaleGestureDetector.getFocusY();
|
||||||
|
float ratio = scaleGestureDetector.getScaleFactor();
|
||||||
|
|
||||||
|
setRatio(ratio, focusX, focusY);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -481,7 +481,7 @@ public class DownloadService extends Service implements ProgressListener {
|
||||||
|
|
||||||
|
|
||||||
public boolean isCanceled() {
|
public boolean isCanceled() {
|
||||||
return BInstallerView.downloadCanceled;
|
return BInstallerActivity.downloadCanceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<btools.routingapp.BInstallerView
|
||||||
|
android:id="@+id/BInstallerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonDownload"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/action_select"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewSegmentSummary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:textColor="@android:color/primary_text_light"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
</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>
|
|
@ -15,7 +15,19 @@
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
|
<plurals name="numberOfSegments">
|
||||||
|
<item quantity="one">%d segment</item>
|
||||||
|
<item quantity="other">%d segments</item>
|
||||||
|
</plurals>
|
||||||
<string name="app_name">BRouter</string>
|
<string name="app_name">BRouter</string>
|
||||||
|
<string name="cancel_download">Cancel Download</string>
|
||||||
<string name="import_profile">Import Profile</string>
|
<string name="import_profile">Import Profile</string>
|
||||||
<string name="profile_filename_example">filename.brf</string>
|
<string name="profile_filename_example">filename.brf</string>
|
||||||
|
<string name="download_info_start">Starting download…</string>
|
||||||
|
<string name="download_info_cancel">Cancelling…</string>
|
||||||
|
<string name="action_download">Download %s</string>
|
||||||
|
<string name="action_delete">Delete %s</string>
|
||||||
|
<string name="action_update">Update %s</string>
|
||||||
|
<string name="action_select">Select segments</string>
|
||||||
|
<string name="summary_segments">Size=%s\nFree=%s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue