android 11 part 2
|
@ -71,7 +71,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
|
|
||||||
implementation project(':brouter-mapaccess')
|
implementation project(':brouter-mapaccess')
|
||||||
implementation project(':brouter-core')
|
implementation project(':brouter-core')
|
||||||
|
|
|
@ -8,14 +8,17 @@
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<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" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@drawable/icon"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:allowBackup="true">
|
android:allowBackup="false">
|
||||||
<activity android:name=".BRouterActivity"
|
<activity android:name=".BRouterActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
android:exported="true"
|
||||||
|
android:screenOrientation="unspecified" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
@ -24,6 +27,8 @@
|
||||||
<activity android:name=".BInstallerActivity"
|
<activity android:name=".BInstallerActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:screenOrientation="landscape"
|
android:screenOrientation="landscape"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||||
</activity>
|
</activity>
|
||||||
<service
|
<service
|
||||||
|
@ -31,6 +36,11 @@
|
||||||
android:name=".BRouterService"
|
android:name=".BRouterService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:process=":brouter_service" />
|
android:process=":brouter_service" />
|
||||||
|
<service android:name="btools.routingapp.DownloadService"
|
||||||
|
android:label="Download Service"
|
||||||
|
android:icon="@drawable/icon"
|
||||||
|
android:enabled="true"
|
||||||
|
/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
16
brouter-routing-app/src/main/assets/serverconfig.txt
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#
|
||||||
|
# data download parameter
|
||||||
|
#
|
||||||
|
# Keep in mind, when profiles downloaded they overwrite old files
|
||||||
|
# So when modifying an original profile make a copy from it
|
||||||
|
#
|
||||||
|
|
||||||
|
segment_url=https://brouter.de/brouter/segments4/
|
||||||
|
lookup_url=https://brouter.de/brouter/segments4/
|
||||||
|
profiles_url=https://brouter.de/brouter/segments4/
|
||||||
|
|
||||||
|
|
||||||
|
# these are comma separated arrays
|
||||||
|
|
||||||
|
check_lookup=lookups.dat
|
||||||
|
check_profiles=car-eco.brf,car-fast.brf,dummy.brf,fastbike.brf,fastbike-asia-pacific.brf,fastbike-lowtraffic.brf,moped.brf,shortest.brf,trekking.brf,vm-forum-liegerad-schnell.brf,vm-forum-velomobil-schnell.brf
|
BIN
brouter-routing-app/src/main/ic_launcher-playstore.png
Normal file
After Width: | Height: | Size: 23 KiB |
1
brouter-routing-app/src/main/icon_brouter.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64"><g style="line-height:1.25;-inkscape-font-specification:'Lucida Sans Typewriter, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"><path style="-inkscape-font-specification:'Lucida Sans Typewriter, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal" d="M52.445 52.51H12.609V39.836q0-3.581.376-6 .566-3.52 3.015-5.632 2.423-2.143 5.518-2.143 2.853 0 5.437 1.898 1.265.949 2.315 2.235 1.05 1.255 2.45 4.867 1.345-5.54 4.225-8.235 2.853-2.694 6.514-2.694 2.503 0 4.737 1.378 2.207 1.347 3.5 3.704 1.237 2.235 1.507 4.867.242 2.633.242 6.582zm-22.26-6.031v-2.112q0-2.572-.323-4.806-.323-2.235-1.427-3.98-.969-1.592-2.476-2.48-1.534-.918-3.23-.918-1.588 0-2.853.796-1.265.796-1.938 2.051-.969 1.714-1.157 3.95-.189 2.203-.189 4.683v2.816zm18.276 0V41.03q0-2.388-.188-4.408-.215-2.051-.996-3.275-.808-1.256-2.234-2.02-1.427-.766-2.988-.766-3.391 0-5.948 3.428-2.584 3.398-2.584 10.286v2.204z" fill="#a46843" aria-label="B" font-weight="400" font-size="58.787" font-family="Lucida Sans Typewriter" letter-spacing="0" word-spacing="0" stroke-width="2.939"/></g><g fill="#520" stroke-width="2"><g style="line-height:1.25;-inkscape-font-specification:'Lucida Sans Typewriter, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal"><path d="M20.337 46.717V7.802h9.934c3.838 0 6.605.342 8.302 1.025 1.698.684 3.026 1.815 3.985 3.392.978 1.56 1.467 3.243 1.467 5.049 0 1.875-.563 3.83-1.688 5.863-1.107 2.034-3.912 4.016-7.177 5.593l12.315 17.904-5.553.089-11.12-16.29-5.014-.354v16.644zm5.451-16.644l4.911.35c-1.762-4.727 3.502-5.083 5.107-6.766 1.605-1.7 2.408-3.69 2.408-5.969 0-1.21-.268-2.26-.803-3.155-.517-.894-1.347-1.586-2.49-2.077-1.144-.508-3.072-.763-5.784-.763h-3.349z" style="-inkscape-font-specification:'Lucida Sans Typewriter, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal" fill="#370d00" aria-label="R" font-weight="400" font-size="55.244" font-family="Lucida Sans Typewriter" letter-spacing="0" word-spacing="0"/></g><path style="mix-blend-mode:normal" d="M25.481 48.856a2.436 2.634 0 01-2.426 2.634 2.436 2.634 0 01-2.446-2.613 2.436 2.634 0 012.407-2.655 2.436 2.634 0 012.465 2.591M47.003 48.803a2.436 2.634 0 01-2.426 2.633 2.436 2.634 0 01-2.446-2.612 2.436 2.634 0 012.406-2.655 2.436 2.634 0 012.466 2.591" fill="none" stroke="#370d00"/></g></svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -6,20 +6,42 @@ import java.util.Set;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
public class BInstallerActivity extends Activity implements OnInitListener {
|
public class BInstallerActivity extends Activity implements OnInitListener {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
private BInstallerView mBInstallerView;
|
private BInstallerView mBInstallerView;
|
||||||
private PowerManager mPowerManager;
|
private PowerManager mPowerManager;
|
||||||
private WakeLock mWakeLock;
|
private WakeLock mWakeLock;
|
||||||
|
private DownloadReceiver myReceiver;
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
mBInstallerView.setState(txt, ready);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
/** Called when the activity is first created. */
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,6 +73,12 @@ public class BInstallerActivity extends Activity implements OnInitListener {
|
||||||
*/
|
*/
|
||||||
mWakeLock.acquire();
|
mWakeLock.acquire();
|
||||||
|
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(DOWNLOAD_ACTION);
|
||||||
|
|
||||||
|
myReceiver = new DownloadReceiver();
|
||||||
|
registerReceiver(myReceiver, filter);
|
||||||
|
|
||||||
// Start the download manager
|
// Start the download manager
|
||||||
mBInstallerView.startInstaller();
|
mBInstallerView.startInstaller();
|
||||||
}
|
}
|
||||||
|
@ -58,6 +86,18 @@ public class BInstallerActivity extends Activity implements OnInitListener {
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
||||||
|
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
mWakeLock.release();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
if (myReceiver != null) unregisterReceiver(myReceiver);
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,12 @@ 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.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import android.app.Activity;
|
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;
|
||||||
|
@ -23,6 +25,7 @@ import android.os.PowerManager;
|
||||||
import android.os.StatFs;
|
import android.os.StatFs;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -63,7 +66,7 @@ public class BInstallerView extends View
|
||||||
private File baseDir;
|
private File baseDir;
|
||||||
|
|
||||||
private boolean isDownloading = false;
|
private boolean isDownloading = false;
|
||||||
private volatile boolean downloadCanceled = false;
|
public static boolean downloadCanceled = false;
|
||||||
|
|
||||||
private long currentDownloadSize;
|
private long currentDownloadSize;
|
||||||
private String currentDownloadFile = "";
|
private String currentDownloadFile = "";
|
||||||
|
@ -78,6 +81,9 @@ public class BInstallerView extends View
|
||||||
Paint pnt_1 = new Paint();
|
Paint pnt_1 = new Paint();
|
||||||
Paint pnt_2 = new Paint();
|
Paint pnt_2 = new Paint();
|
||||||
Paint paint = new Paint();
|
Paint paint = new Paint();
|
||||||
|
|
||||||
|
Activity mActivity;
|
||||||
|
|
||||||
|
|
||||||
protected String baseNameForTile( int tileIndex )
|
protected String baseNameForTile( int tileIndex )
|
||||||
{
|
{
|
||||||
|
@ -133,6 +139,7 @@ public class BInstallerView extends View
|
||||||
int tidx_min = -1;
|
int tidx_min = -1;
|
||||||
int min_size = Integer.MAX_VALUE;
|
int min_size = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
ArrayList<Integer> downloadList = new ArrayList<>();
|
||||||
// prepare download list
|
// prepare download list
|
||||||
for( int ix=0; ix<72; ix++ )
|
for( int ix=0; ix<72; ix++ )
|
||||||
{
|
{
|
||||||
|
@ -142,6 +149,7 @@ public class BInstallerView extends View
|
||||||
if ( ( tileStatus[tidx] & MASK_SELECTED_RD5 ) != 0 )
|
if ( ( tileStatus[tidx] & MASK_SELECTED_RD5 ) != 0 )
|
||||||
{
|
{
|
||||||
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
int tilesize = BInstallerSizes.getRd5Size(tidx);
|
||||||
|
downloadList.add(tidx);
|
||||||
if ( tilesize > 0 && tilesize < min_size )
|
if ( tilesize > 0 && tilesize < min_size )
|
||||||
{
|
{
|
||||||
tidx_min = tidx;
|
tidx_min = tidx;
|
||||||
|
@ -150,29 +158,39 @@ public class BInstallerView extends View
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( tidx_min != -1 )
|
|
||||||
{
|
if (downloadList.size()>0) {
|
||||||
tileStatus[tidx_min] ^= tileStatus[tidx_min] & MASK_SELECTED_RD5;
|
isDownloading = true;
|
||||||
startDownload( tidx_min );
|
downloadAll(downloadList);
|
||||||
|
for (Integer i : downloadList) {
|
||||||
|
tileStatus[i.intValue()] ^= tileStatus[i.intValue()] & MASK_SELECTED_RD5;
|
||||||
|
}
|
||||||
|
downloadList.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDownload( int tileIndex )
|
private void downloadAll(ArrayList<Integer> downloadList) {
|
||||||
{
|
ArrayList<String> urlparts = new ArrayList<>();
|
||||||
|
for (Integer i: downloadList) {
|
||||||
String namebase = baseNameForTile( tileIndex );
|
urlparts.add(baseNameForTile( i.intValue() ));
|
||||||
String baseurl = "http://brouter.de/brouter/segments4/";
|
}
|
||||||
currentDownloadFile = namebase + ".rd5";
|
|
||||||
currentDownloadOperation = "Checking";
|
currentDownloadOperation = "Start download ...";
|
||||||
String url = baseurl + currentDownloadFile;
|
downloadAction = "";
|
||||||
isDownloading = true;
|
downloadCanceled = false;
|
||||||
downloadCanceled = false;
|
isDownloading = true;
|
||||||
currentDownloadSize = 0;
|
|
||||||
downloadAction = "Connecting... ";
|
//final DownloadBackground downloadTask = new DownloadBackground(getContext(), urlparts, baseDir);
|
||||||
final DownloadTask downloadTask = new DownloadTask(getContext());
|
//downloadTask.execute( );
|
||||||
downloadTask.execute( url );
|
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 )
|
public void downloadDone( boolean success )
|
||||||
{
|
{
|
||||||
isDownloading = false;
|
isDownloading = false;
|
||||||
|
@ -184,6 +202,16 @@ public class BInstallerView extends View
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setState(String txt, boolean b) {
|
||||||
|
currentDownloadOperation = txt;
|
||||||
|
downloadAction = "";
|
||||||
|
isDownloading = b;
|
||||||
|
if (!b) {
|
||||||
|
scanExistingFiles();
|
||||||
|
}
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private int tileIndex( float x, float y )
|
private int tileIndex( float x, float y )
|
||||||
{
|
{
|
||||||
int ix = (int)(72.f * x / bmp.getWidth());
|
int ix = (int)(72.f * x / bmp.getWidth());
|
||||||
|
@ -300,6 +328,7 @@ public class BInstallerView extends View
|
||||||
|
|
||||||
public BInstallerView(Context context) {
|
public BInstallerView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
mActivity = (Activity) context;
|
||||||
|
|
||||||
DisplayMetrics metrics = new DisplayMetrics();
|
DisplayMetrics metrics = new DisplayMetrics();
|
||||||
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
|
||||||
|
@ -314,10 +343,15 @@ public class BInstallerView extends View
|
||||||
|
|
||||||
imgw = (int)(imgwOrig / scaleOrig);
|
imgw = (int)(imgwOrig / scaleOrig);
|
||||||
imgh = (int)(imghOrig / scaleOrig);
|
imgh = (int)(imghOrig / scaleOrig);
|
||||||
|
|
||||||
|
totalSize = 0;
|
||||||
|
rd5Tiles = 0;
|
||||||
|
delTiles = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toast( String msg )
|
private void toast( String msg )
|
||||||
|
@ -390,10 +424,11 @@ public class BInstallerView extends View
|
||||||
{
|
{
|
||||||
String sizeHint = currentDownloadSize > 0 ? " (" + ((currentDownloadSize + mb-1)/mb) + " MB)" : "";
|
String sizeHint = currentDownloadSize > 0 ? " (" + ((currentDownloadSize + mb-1)/mb) + " MB)" : "";
|
||||||
paint.setTextSize(30);
|
paint.setTextSize(30);
|
||||||
canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
|
canvas.drawText( currentDownloadOperation, 30, (imgh/3)*2-30, paint);
|
||||||
|
// canvas.drawText( currentDownloadOperation + " " + currentDownloadFile + sizeHint, 30, (imgh/3)*2-30, paint);
|
||||||
canvas.drawText( downloadAction, 30, (imgh/3)*2, paint);
|
canvas.drawText( downloadAction, 30, (imgh/3)*2, paint);
|
||||||
}
|
}
|
||||||
if ( !tilesVisible )
|
if ( !tilesVisible && !isDownloading)
|
||||||
{
|
{
|
||||||
paint.setTextSize(35);
|
paint.setTextSize(35);
|
||||||
canvas.drawText( "Touch region to zoom in!", 30, (imgh/3)*2, paint);
|
canvas.drawText( "Touch region to zoom in!", 30, (imgh/3)*2, paint);
|
||||||
|
@ -655,209 +690,7 @@ float tx, ty;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// usually, subclasses of AsyncTask are declared inside the activity class.
|
|
||||||
// that way, you can easily modify the UI thread from here
|
|
||||||
private class DownloadTask extends AsyncTask<String, Integer, String> implements ProgressListener {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private PowerManager.WakeLock mWakeLock;
|
|
||||||
|
|
||||||
public DownloadTask(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
}
|
||||||
public void updateProgress( String progress )
|
|
||||||
{
|
|
||||||
newDownloadAction = progress;
|
|
||||||
publishProgress( 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCanceled()
|
|
||||||
{
|
|
||||||
return isDownloadCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String doInBackground(String... sUrls)
|
|
||||||
{
|
|
||||||
InputStream input = null;
|
|
||||||
OutputStream output = null;
|
|
||||||
HttpURLConnection connection = null;
|
|
||||||
String surl = sUrls[0];
|
|
||||||
File fname = null;
|
|
||||||
File tmp_file = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int slidx = surl.lastIndexOf( "segments4/" );
|
|
||||||
String name = surl.substring( slidx+10 );
|
|
||||||
String surlBase = surl.substring( 0, slidx+10 );
|
|
||||||
fname = new File (baseDir, "brouter/segments4/" + name);
|
|
||||||
|
|
||||||
boolean delta = true;
|
|
||||||
|
|
||||||
if ( fname.exists() )
|
|
||||||
{
|
|
||||||
updateProgress( "Calculating local checksum.." );
|
|
||||||
|
|
||||||
// first check for a delta file
|
|
||||||
|
|
||||||
String md5 = Rd5DiffManager.getMD5( fname );
|
|
||||||
String surlDelta = surlBase + "diff/" + name.replace( ".rd5", "/" + md5 + ".df5" );
|
|
||||||
|
|
||||||
URL urlDelta = new URL(surlDelta);
|
|
||||||
|
|
||||||
updateProgress( "Connecting.." );
|
|
||||||
|
|
||||||
connection = (HttpURLConnection) urlDelta.openConnection();
|
|
||||||
connection.connect();
|
|
||||||
|
|
||||||
// 404 kind of expected here, means there's no delta file
|
|
||||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND )
|
|
||||||
{
|
|
||||||
connection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( connection == null )
|
|
||||||
{
|
|
||||||
delta = false;
|
|
||||||
URL url = new URL(surl);
|
|
||||||
connection = (HttpURLConnection) url.openConnection();
|
|
||||||
connection.connect();
|
|
||||||
}
|
|
||||||
// expect HTTP 200 OK, so we don't mistakenly save error report
|
|
||||||
// instead of the file
|
|
||||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
|
||||||
return "Server returned HTTP " + connection.getResponseCode()
|
|
||||||
+ " " + connection.getResponseMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will be useful to display download percentage
|
|
||||||
// might be -1: server did not report the length
|
|
||||||
int fileLength = connection.getContentLength();
|
|
||||||
currentDownloadSize = fileLength;
|
|
||||||
if ( availableSize >= 0 && fileLength > availableSize ) return "not enough space on sd-card";
|
|
||||||
|
|
||||||
currentDownloadOperation = delta ? "Updating" : "Loading";
|
|
||||||
|
|
||||||
// download the file
|
|
||||||
input = connection.getInputStream();
|
|
||||||
|
|
||||||
tmp_file = new File( fname.getAbsolutePath() + ( delta ? "_diff" : "_tmp" ) );
|
|
||||||
output = new FileOutputStream( tmp_file );
|
|
||||||
|
|
||||||
byte[] data = new byte[4096];
|
|
||||||
long total = 0;
|
|
||||||
long t0 = System.currentTimeMillis();
|
|
||||||
int count;
|
|
||||||
while ((count = input.read(data)) != -1) {
|
|
||||||
if (isDownloadCanceled()) {
|
|
||||||
return "Download canceled!";
|
|
||||||
}
|
|
||||||
total += count;
|
|
||||||
// publishing the progress....
|
|
||||||
if (fileLength > 0) // only if total length is known
|
|
||||||
{
|
|
||||||
int pct = (int) (total * 100 / fileLength);
|
|
||||||
updateProgress( "Progress " + pct + "%" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updateProgress( "Progress (unnown size)" );
|
|
||||||
}
|
|
||||||
|
|
||||||
output.write(data, 0, count);
|
|
||||||
|
|
||||||
// enforce < 2 Mbit/s
|
|
||||||
long dt = t0 + total/524 - System.currentTimeMillis();
|
|
||||||
if ( dt > 0 )
|
|
||||||
{
|
|
||||||
try { Thread.sleep( dt ); } catch( InterruptedException ie ) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output.close();
|
|
||||||
output = null;
|
|
||||||
|
|
||||||
if ( delta )
|
|
||||||
{
|
|
||||||
updateProgress( "Applying delta.." );
|
|
||||||
File diffFile = tmp_file;
|
|
||||||
tmp_file = new File( fname + "_tmp" );
|
|
||||||
Rd5DiffTool.recoverFromDelta( fname, diffFile, tmp_file, this );
|
|
||||||
diffFile.delete();
|
|
||||||
}
|
|
||||||
if (isDownloadCanceled())
|
|
||||||
{
|
|
||||||
return "Canceled!";
|
|
||||||
}
|
|
||||||
if ( tmp_file != null )
|
|
||||||
{
|
|
||||||
updateProgress( "Verifying integrity.." );
|
|
||||||
String check_result = PhysicalFile.checkFileIntegrity( tmp_file );
|
|
||||||
if ( check_result != null ) return check_result;
|
|
||||||
|
|
||||||
if ( !tmp_file.renameTo( fname ) )
|
|
||||||
{
|
|
||||||
return "Could not rename to " + fname.getAbsolutePath();
|
|
||||||
}
|
|
||||||
deleteRawTracks(); // invalidate raw-tracks after data update
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return e.toString();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (output != null)
|
|
||||||
output.close();
|
|
||||||
if (input != null)
|
|
||||||
input.close();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection != null)
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if ( tmp_file != null ) tmp_file.delete(); // just to be sure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
super.onPreExecute();
|
|
||||||
// take CPU lock to prevent CPU from going off if the user
|
|
||||||
// presses the power button during download
|
|
||||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, getClass().getName());
|
|
||||||
mWakeLock.acquire();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... progress) {
|
|
||||||
if ( !newDownloadAction.equals( downloadAction ) )
|
|
||||||
{
|
|
||||||
downloadAction = newDownloadAction;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(String result) {
|
|
||||||
mWakeLock.release();
|
|
||||||
downloadDone( result == null );
|
|
||||||
|
|
||||||
if (result != null)
|
|
||||||
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
|
|
||||||
else
|
|
||||||
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // download task
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,12 +4,14 @@ import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -76,7 +78,9 @@ public class BRouterView extends View
|
||||||
private String profileName;
|
private String profileName;
|
||||||
private String sourceHint;
|
private String sourceHint;
|
||||||
private boolean waitingForSelection = false;
|
private boolean waitingForSelection = false;
|
||||||
|
private boolean waitingForMigration = false;
|
||||||
private String rawTrackPath;
|
private String rawTrackPath;
|
||||||
|
private String oldMigrationPath;
|
||||||
|
|
||||||
private boolean needsViaSelection;
|
private boolean needsViaSelection;
|
||||||
private boolean needsNogoSelection;
|
private boolean needsNogoSelection;
|
||||||
|
@ -124,8 +128,18 @@ public class BRouterView extends View
|
||||||
File brd = new File( baseDir, "brouter" );
|
File brd = new File( baseDir, "brouter" );
|
||||||
if ( brd.isDirectory() )
|
if ( brd.isDirectory() )
|
||||||
{
|
{
|
||||||
startSetup( baseDir, false );
|
if (brd.getAbsolutePath().contains("/Android/data/")) {
|
||||||
return;
|
String message = "(previous basedir " + baseDir + " has to migrate )" ;
|
||||||
|
|
||||||
|
( (BRouterActivity) getContext() ).selectBasedir( getStorageDirectories(), guessBaseDir(), message );
|
||||||
|
waitingForSelection = true;
|
||||||
|
waitingForMigration = true;
|
||||||
|
oldMigrationPath = brd.getAbsolutePath();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
startSetup( baseDir, false );
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String message = baseDir == null ? "(no basedir configured previously)" : "(previous basedir " + baseDir
|
String message = baseDir == null ? "(no basedir configured previously)" : "(previous basedir " + baseDir
|
||||||
|
@ -202,6 +216,12 @@ public class BRouterView extends View
|
||||||
File inputDir = new File (basedir, "/import");
|
File inputDir = new File (basedir, "/import");
|
||||||
assertDirectoryExists( "input directory", inputDir, null, version );
|
assertDirectoryExists( "input directory", inputDir, null, version );
|
||||||
|
|
||||||
|
// new init is done move old files
|
||||||
|
if (waitingForMigration) {
|
||||||
|
moveFolders(oldMigrationPath, basedir + "/brouter");
|
||||||
|
waitingForMigration = false;
|
||||||
|
}
|
||||||
|
|
||||||
int deviceLevel = android.os.Build.VERSION.SDK_INT;
|
int deviceLevel = android.os.Build.VERSION.SDK_INT;
|
||||||
int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
|
int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
|
||||||
canAccessSdCard = deviceLevel < 23 || targetSdkVersion == 10;
|
canAccessSdCard = deviceLevel < 23 || targetSdkVersion == 10;
|
||||||
|
@ -320,6 +340,67 @@ public class BRouterView extends View
|
||||||
waitingForSelection = true;
|
waitingForSelection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void moveFolders(String oldMigrationPath, String basedir) {
|
||||||
|
File oldDir = new File(oldMigrationPath);
|
||||||
|
File[] oldFiles = oldDir.listFiles();
|
||||||
|
for (File f: oldFiles) {
|
||||||
|
if (f.isDirectory()) {
|
||||||
|
int index = f.getAbsolutePath().lastIndexOf("/");
|
||||||
|
String tmpdir = basedir + f.getAbsolutePath().substring(index);
|
||||||
|
moveFolders(f.getAbsolutePath(), tmpdir);
|
||||||
|
} else {
|
||||||
|
if ( ! f.getName().startsWith("v1.6")) {
|
||||||
|
moveFile(oldMigrationPath, f.getName(), basedir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveFile(String inputPath, String inputFile, String outputPath) {
|
||||||
|
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
//create output directory if it doesn't exist
|
||||||
|
File dir = new File (outputPath);
|
||||||
|
if (!dir.exists())
|
||||||
|
{
|
||||||
|
dir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
in = new FileInputStream(inputPath + "/" + inputFile);
|
||||||
|
out = new FileOutputStream(outputPath + "/" + inputFile);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read;
|
||||||
|
while ((read = in.read(buffer)) != -1) {
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
in = null;
|
||||||
|
|
||||||
|
// write the output file
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
out = null;
|
||||||
|
|
||||||
|
// delete the original file
|
||||||
|
new File(inputPath + "/" + inputFile).delete();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (FileNotFoundException fnfe1) {
|
||||||
|
Log.e("tag", fnfe1.getMessage());
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Log.e("tag", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasUpToDateLookups()
|
public boolean hasUpToDateLookups()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,544 @@
|
||||||
|
package btools.routingapp;
|
||||||
|
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.TrafficStats;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.os.StatFs;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import btools.mapaccess.PhysicalFile;
|
||||||
|
import btools.mapaccess.Rd5DiffManager;
|
||||||
|
import btools.mapaccess.Rd5DiffTool;
|
||||||
|
import btools.util.ProgressListener;
|
||||||
|
|
||||||
|
public class DownloadService extends Service implements ProgressListener {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
String segmenturl = "https://brouter.de/brouter/segments4/";
|
||||||
|
String lookupurl = "https://brouter.de/brouter/segments4/";
|
||||||
|
String profilesurl = "https://brouter.de/brouter/segments4/";
|
||||||
|
String checkLookup = "lookups.dat";
|
||||||
|
String checkProfiles = "";
|
||||||
|
|
||||||
|
private NotificationHelper mNotificationHelper;
|
||||||
|
private List<String> mUrlList;
|
||||||
|
private String baseDir;
|
||||||
|
|
||||||
|
private volatile String newDownloadAction = "";
|
||||||
|
private volatile String currentDownloadOperation = "";
|
||||||
|
private long availableSize;
|
||||||
|
|
||||||
|
private Looper mServiceLooper;
|
||||||
|
private ServiceHandler mServiceHandler;
|
||||||
|
private NotificationManager mNM;
|
||||||
|
String downloadUrl;
|
||||||
|
public static boolean serviceState = false;
|
||||||
|
private boolean bIsDownloading;
|
||||||
|
|
||||||
|
// Handler that receives messages from the thread
|
||||||
|
private final class ServiceHandler extends Handler {
|
||||||
|
public ServiceHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
bIsDownloading = true;
|
||||||
|
downloadFiles();
|
||||||
|
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf(msg.arg1);
|
||||||
|
mNotificationHelper.stopNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
if (DEBUG) Log.d("SERVICE", "onCreate");
|
||||||
|
serviceState = true;
|
||||||
|
|
||||||
|
HandlerThread thread = new HandlerThread("ServiceStartArguments", 1);
|
||||||
|
thread.start();
|
||||||
|
|
||||||
|
// Get the HandlerThread's Looper and use it for our Handler
|
||||||
|
mServiceLooper = thread.getLooper();
|
||||||
|
mServiceHandler = new ServiceHandler(mServiceLooper);
|
||||||
|
|
||||||
|
availableSize = -1;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StatFs stat = new StatFs(baseDir);
|
||||||
|
availableSize = (long)stat.getAvailableBlocksLong()*stat.getBlockSizeLong();
|
||||||
|
}
|
||||||
|
catch (Exception e) { /* ignore */ }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
if (DEBUG) Log.d("SERVICE", "onStartCommand");
|
||||||
|
|
||||||
|
mNotificationHelper = new NotificationHelper(this);
|
||||||
|
Bundle extra = intent.getExtras();
|
||||||
|
if (extra != null) {
|
||||||
|
String dir = extra.getString("dir");
|
||||||
|
List<String> urlparts = extra.getStringArrayList("urlparts");
|
||||||
|
mUrlList = urlparts;
|
||||||
|
baseDir = dir;
|
||||||
|
|
||||||
|
File configFile = new File (dir, "segments4/serverconfig.txt");
|
||||||
|
if ( configFile.exists() ) {
|
||||||
|
try {
|
||||||
|
BufferedReader br = new BufferedReader( new FileReader( configFile ) );
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
String line = br.readLine();
|
||||||
|
if ( line == null ) break;
|
||||||
|
if ( line.trim().startsWith( "segment_url=" ) ) {
|
||||||
|
segmenturl = line.substring(12);
|
||||||
|
}
|
||||||
|
else if ( line.trim().startsWith( "lookup_url=" ) ) {
|
||||||
|
lookupurl = line.substring(11);
|
||||||
|
}
|
||||||
|
else if ( line.trim().startsWith( "profiles_url=" ) ) {
|
||||||
|
profilesurl = line.substring(13);
|
||||||
|
}
|
||||||
|
else if ( line.trim().startsWith( "check_lookup=" ) ) {
|
||||||
|
checkLookup = line.substring(13);
|
||||||
|
}
|
||||||
|
else if ( line.trim().startsWith( "check_profiles=" ) ) {
|
||||||
|
checkProfiles = line.substring(15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mNotificationHelper.startNotification(this);
|
||||||
|
|
||||||
|
Message msg = mServiceHandler.obtainMessage();
|
||||||
|
msg.arg1 = startId;
|
||||||
|
mServiceHandler.sendMessage(msg);
|
||||||
|
|
||||||
|
// If we get killed, after returning from here, restart
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
if (DEBUG) Log.d("SERVICE", "onDestroy");
|
||||||
|
serviceState = false;
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void downloadFiles() {
|
||||||
|
|
||||||
|
// first check lookup table and prifles
|
||||||
|
String result = checkScripts();
|
||||||
|
if ( result != null) {
|
||||||
|
if (DEBUG) Log.d("BR", "error: " + result);
|
||||||
|
bIsDownloading = false;
|
||||||
|
updateProgress( "finished " );
|
||||||
|
|
||||||
|
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int count = 1;
|
||||||
|
int size = mUrlList.size();
|
||||||
|
for (String part: mUrlList) {
|
||||||
|
String url = segmenturl + part + ".rd5";
|
||||||
|
if (DEBUG) Log.d("BR", "downlaod " + url);
|
||||||
|
|
||||||
|
result = download(count, size, url);
|
||||||
|
if (result != null) {
|
||||||
|
if (DEBUG) Log.d("BR", "" + result);
|
||||||
|
Toast.makeText(this, result, Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
updateProgress( "Download " + part + " " + count + "/"+ size + " finshed");
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bIsDownloading = false;
|
||||||
|
updateProgress( "finished " );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void updateProgress( String progress )
|
||||||
|
{
|
||||||
|
if ( !newDownloadAction.equals( progress ) )
|
||||||
|
{
|
||||||
|
if (DEBUG) Log.d("BR", "up " + progress);
|
||||||
|
Intent intent = new Intent(BInstallerActivity.DOWNLOAD_ACTION);
|
||||||
|
intent.putExtra("txt", progress);
|
||||||
|
intent.putExtra("ready", bIsDownloading);
|
||||||
|
sendBroadcast(intent);;
|
||||||
|
newDownloadAction = progress;
|
||||||
|
mNotificationHelper.progressUpdate(newDownloadAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private String download(int counter, int size, String surl)
|
||||||
|
{
|
||||||
|
InputStream input = null;
|
||||||
|
OutputStream output = null;
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
File fname = null;
|
||||||
|
File tmp_file = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TrafficStats.setThreadStatsTag(1);
|
||||||
|
|
||||||
|
int slidx = surl.lastIndexOf( "segments4/" );
|
||||||
|
String name = surl.substring( slidx+10 );
|
||||||
|
String surlBase = surl.substring( 0, slidx+10 );
|
||||||
|
fname = new File (baseDir, "segments4/" + name);
|
||||||
|
|
||||||
|
boolean delta = true;
|
||||||
|
|
||||||
|
// if (!targetFile.getParentFile().exists()) targetFile.getParentFile().mkdirs();
|
||||||
|
if ( fname.exists() )
|
||||||
|
{
|
||||||
|
updateProgress( "Calculating local checksum.." );
|
||||||
|
|
||||||
|
// first check for a delta file
|
||||||
|
String md5 = Rd5DiffManager.getMD5( fname );
|
||||||
|
String surlDelta = surlBase + "diff/" + name.replace( ".rd5", "/" + md5 + ".df5" );
|
||||||
|
|
||||||
|
URL urlDelta = new URL(surlDelta);
|
||||||
|
|
||||||
|
connection = (HttpURLConnection) urlDelta.openConnection();
|
||||||
|
connection.connect();
|
||||||
|
|
||||||
|
// 404 kind of expected here, means there's no delta file
|
||||||
|
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND )
|
||||||
|
{
|
||||||
|
connection = null;
|
||||||
|
} else {
|
||||||
|
updateProgress( "Connecting.." + surlDelta );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( connection == null )
|
||||||
|
{
|
||||||
|
updateProgress( "Connecting.." + surl );
|
||||||
|
|
||||||
|
delta = false;
|
||||||
|
URL url = new URL(surl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress( "Connecting.." + counter + "/"+size );
|
||||||
|
|
||||||
|
// expect HTTP 200 OK, so we don't mistakenly save error report
|
||||||
|
// instead of the file
|
||||||
|
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
|
return "Server returned HTTP " + connection.getResponseCode()
|
||||||
|
+ " " + connection.getResponseMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will be useful to display download percentage
|
||||||
|
// might be -1: server did not report the length
|
||||||
|
int fileLength = connection.getContentLength();
|
||||||
|
long currentDownloadSize = fileLength;
|
||||||
|
if ( availableSize >= 0 && fileLength > availableSize ) return "not enough space on sd-card";
|
||||||
|
|
||||||
|
currentDownloadOperation = delta ? "Updating" : "Loading";
|
||||||
|
updateProgress( currentDownloadOperation);
|
||||||
|
|
||||||
|
// download the file
|
||||||
|
input = connection.getInputStream();
|
||||||
|
|
||||||
|
tmp_file = new File( fname.getAbsolutePath() + ( delta ? "_diff" : "_tmp" ) );
|
||||||
|
output = new FileOutputStream( tmp_file );
|
||||||
|
|
||||||
|
byte[] data = new byte[4096];
|
||||||
|
long total = 0;
|
||||||
|
long t0 = System.currentTimeMillis();
|
||||||
|
int count;
|
||||||
|
while ((count = input.read(data)) != -1) {
|
||||||
|
if (isCanceled()) {
|
||||||
|
return "Download canceled!";
|
||||||
|
}
|
||||||
|
total += count;
|
||||||
|
// publishing the progress....
|
||||||
|
if (fileLength > 0) // only if total length is known
|
||||||
|
{
|
||||||
|
int pct = (int) (total * 100 / fileLength);
|
||||||
|
updateProgress( "Progress " + counter + "/"+size + " .. " + pct + "%" );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
updateProgress( "Progress (unnown size)" );
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write(data, 0, count);
|
||||||
|
|
||||||
|
// enforce < 2 Mbit/s
|
||||||
|
long dt = t0 + total/524 - System.currentTimeMillis();
|
||||||
|
if ( dt > 0 )
|
||||||
|
{
|
||||||
|
try { Thread.sleep( dt ); } catch( InterruptedException ie ) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.close();
|
||||||
|
output = null;
|
||||||
|
|
||||||
|
if ( delta )
|
||||||
|
{
|
||||||
|
updateProgress( "Applying delta.." );
|
||||||
|
File diffFile = tmp_file;
|
||||||
|
tmp_file = new File( fname + "_tmp" );
|
||||||
|
Rd5DiffTool.recoverFromDelta( fname, diffFile, tmp_file, this );
|
||||||
|
diffFile.delete();
|
||||||
|
}
|
||||||
|
if (isCanceled())
|
||||||
|
{
|
||||||
|
return "Canceled!";
|
||||||
|
}
|
||||||
|
if ( tmp_file != null )
|
||||||
|
{
|
||||||
|
updateProgress( "Verifying integrity.." );
|
||||||
|
String check_result = PhysicalFile.checkFileIntegrity( tmp_file );
|
||||||
|
if ( check_result != null ) {
|
||||||
|
if (check_result.startsWith("version old lookups.dat") ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return check_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !tmp_file.renameTo( fname ) )
|
||||||
|
{
|
||||||
|
return "Could not rename to " + fname.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(); ;
|
||||||
|
return e.toString();
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (output != null)
|
||||||
|
output.close();
|
||||||
|
if (input != null)
|
||||||
|
input.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null)
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ( tmp_file != null ) tmp_file.delete(); // just to be sure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkScripts() {
|
||||||
|
|
||||||
|
String[] sa = checkLookup.split(",");
|
||||||
|
for (String f: sa) {
|
||||||
|
if (f.length()>0) {
|
||||||
|
File file = new File(baseDir + "profiles2", f);
|
||||||
|
checkOrDownloadLookup(f, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sa = checkProfiles.split(",");
|
||||||
|
for (String f : sa) {
|
||||||
|
if (f.length()>0) {
|
||||||
|
File file = new File(baseDir + "profiles2", f);
|
||||||
|
if (file.exists()) {
|
||||||
|
String result = checkOrDownloadScript(f, file);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkOrDownloadLookup(String fileName, File f) {
|
||||||
|
String url = lookupurl + fileName;
|
||||||
|
return downloadScript(url, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkOrDownloadScript(String fileName, File f) {
|
||||||
|
String url = profilesurl + fileName;
|
||||||
|
return downloadScript(url, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String downloadScript(String surl, File f) {
|
||||||
|
long size = 0L;
|
||||||
|
if (f.exists()) {
|
||||||
|
size = f.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream input = null;
|
||||||
|
OutputStream output = null;
|
||||||
|
HttpURLConnection connection = null;
|
||||||
|
File tmp_file = null;
|
||||||
|
File targetFile = f;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TrafficStats.setThreadStatsTag(1);
|
||||||
|
|
||||||
|
if ( connection == null )
|
||||||
|
{
|
||||||
|
URL url = new URL(surl);
|
||||||
|
connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.connect();
|
||||||
|
}
|
||||||
|
// expect HTTP 200 OK, so we don't mistakenly save error report
|
||||||
|
// instead of the file
|
||||||
|
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
||||||
|
return "Server returned HTTP " + connection.getResponseCode()
|
||||||
|
+ " " + connection.getResponseMessage() + " " + f.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this will be useful to display download percentage
|
||||||
|
// might be -1: server did not report the length
|
||||||
|
long fileLength = (long)connection.getContentLength();
|
||||||
|
if (DEBUG) Log.d("BR", "file size " + size + " == " + fileLength + " " + f.getName());
|
||||||
|
if (fileLength != size) {
|
||||||
|
long currentDownloadSize = fileLength;
|
||||||
|
if (availableSize >= 0 && fileLength > availableSize)
|
||||||
|
return "not enough space on sd-card";
|
||||||
|
|
||||||
|
currentDownloadOperation = "Updating";
|
||||||
|
|
||||||
|
// download the file
|
||||||
|
input = connection.getInputStream();
|
||||||
|
|
||||||
|
tmp_file = new File(f.getAbsolutePath() + "_tmp");
|
||||||
|
output = new FileOutputStream(tmp_file);
|
||||||
|
|
||||||
|
byte data[] = new byte[4096];
|
||||||
|
long total = 0;
|
||||||
|
long t0 = System.currentTimeMillis();
|
||||||
|
int count;
|
||||||
|
while ((count = input.read(data)) != -1) {
|
||||||
|
if (isCanceled()) {
|
||||||
|
return "Download canceled!";
|
||||||
|
}
|
||||||
|
total += count;
|
||||||
|
// publishing the progress....
|
||||||
|
if (fileLength > 0) // only if total length is known
|
||||||
|
{
|
||||||
|
int pct = (int) (total * 100 / fileLength);
|
||||||
|
updateProgress("Progress " + pct + "%");
|
||||||
|
} else {
|
||||||
|
updateProgress("Progress (unnown size)");
|
||||||
|
}
|
||||||
|
|
||||||
|
output.write(data, 0, count);
|
||||||
|
|
||||||
|
// enforce < 2 Mbit/s
|
||||||
|
long dt = t0 + total / 524 - System.currentTimeMillis();
|
||||||
|
if (dt > 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(dt);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.close();
|
||||||
|
output = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCanceled())
|
||||||
|
{
|
||||||
|
return "Canceled!";
|
||||||
|
}
|
||||||
|
if ( tmp_file != null )
|
||||||
|
{
|
||||||
|
f.delete();
|
||||||
|
|
||||||
|
if ( !tmp_file.renameTo( f ) )
|
||||||
|
{
|
||||||
|
return "Could not rename to " + f.getName();
|
||||||
|
}
|
||||||
|
if (DEBUG) Log.d("BR", "update " + f.getName());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return e.toString() ;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (output != null)
|
||||||
|
output.close();
|
||||||
|
if (input != null)
|
||||||
|
input.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null)
|
||||||
|
connection.disconnect();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ( tmp_file != null ) tmp_file.delete(); // just to be sure
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return BInstallerView.downloadCanceled;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package btools.routingapp;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.media.AudioAttributes;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
|
|
||||||
|
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||||
|
|
||||||
|
public class NotificationHelper {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
public static String BRouterNotificationChannel1 = "brouter_channel_01";
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private int NOTIFICATION_ID = 111;
|
||||||
|
private Notification mNotification;
|
||||||
|
private NotificationManager mNotificationManager;
|
||||||
|
private PendingIntent mContentIntent;
|
||||||
|
private CharSequence mContentTitle;
|
||||||
|
|
||||||
|
public NotificationHelper(Context context)
|
||||||
|
{
|
||||||
|
if (DEBUG) Log.d("NH", "init " );
|
||||||
|
mContext = context;
|
||||||
|
createNotificationChannels();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startNotification(Service service) {
|
||||||
|
if (DEBUG) Log.d("NH", "startNotification " );
|
||||||
|
|
||||||
|
mNotification = createNotification("BRouter Download", "Download some files");
|
||||||
|
|
||||||
|
if (service != null) service.startForeground(NOTIFICATION_ID, mNotification);
|
||||||
|
|
||||||
|
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void progressUpdate(String text) {
|
||||||
|
mNotification = createNotification("BRouter Download", text);
|
||||||
|
mNotification.flags = Notification.FLAG_NO_CLEAR |
|
||||||
|
Notification.FLAG_ONGOING_EVENT;
|
||||||
|
|
||||||
|
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Notification createNotification(String title, String desc) {
|
||||||
|
|
||||||
|
Intent resultIntent = new Intent(mContext, BInstallerActivity.class);
|
||||||
|
|
||||||
|
Intent notificationIntent = new Intent();
|
||||||
|
mContentIntent = PendingIntent.getActivity(mContext, 0, resultIntent, PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
|
||||||
|
mNotificationManager = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|
||||||
|
|
||||||
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, BRouterNotificationChannel1);
|
||||||
|
builder.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(desc)
|
||||||
|
.setTicker(desc)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
|
.setContentIntent(mContentIntent);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
|
||||||
|
builder.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(desc)
|
||||||
|
.setOnlyAlertOnce(true)
|
||||||
|
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
|
.setContentIntent(mContentIntent);
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create notification channels
|
||||||
|
*/
|
||||||
|
public void createNotificationChannels() {
|
||||||
|
if (DEBUG) Log.d("NH", "createNotificationChannels " );
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
|
||||||
|
NotificationManager sNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
// Sound channel
|
||||||
|
CharSequence name = "BRouter Download";
|
||||||
|
// The user-visible description of the channel.
|
||||||
|
String description = "BRouter Download Channel"; //getString(R.string.channel_description);
|
||||||
|
|
||||||
|
NotificationChannel channel = new NotificationChannel(BRouterNotificationChannel1, name, NotificationManager.IMPORTANCE_LOW);
|
||||||
|
channel.setDescription(description);
|
||||||
|
AudioAttributes att = new AudioAttributes.Builder()
|
||||||
|
.setUsage(AudioAttributes.USAGE_UNKNOWN)
|
||||||
|
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
|
||||||
|
.build();
|
||||||
|
channel.setSound(null, null);
|
||||||
|
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
|
||||||
|
|
||||||
|
sNotificationManager.createNotificationChannel(channel);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopNotification() {
|
||||||
|
if (DEBUG) Log.d("NH", "stopNotification " );
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
mNotificationManager.deleteNotificationChannel(BRouterNotificationChannel1);
|
||||||
|
}
|
||||||
|
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="0.95"
|
||||||
|
android:scaleY="0.95"
|
||||||
|
android:translateX="2.7"
|
||||||
|
android:translateY="2.7">
|
||||||
|
<group android:scaleX="0.95"
|
||||||
|
android:scaleY="0.95"
|
||||||
|
android:translateX="2.7"
|
||||||
|
android:translateY="2.7">
|
||||||
|
<group android:scaleX="0.95"
|
||||||
|
android:scaleY="0.95"
|
||||||
|
android:translateX="2.7"
|
||||||
|
android:translateY="2.7">
|
||||||
|
<group android:scaleX="0.95"
|
||||||
|
android:scaleY="0.95"
|
||||||
|
android:translateX="2.7"
|
||||||
|
android:translateY="2.7">
|
||||||
|
<group android:scaleX="0.95"
|
||||||
|
android:scaleY="0.95"
|
||||||
|
android:translateX="2.7"
|
||||||
|
android:translateY="2.7">
|
||||||
|
<group android:scaleX="1.06"
|
||||||
|
android:scaleY="1.06">
|
||||||
|
<group android:translateX="-3.0566037"
|
||||||
|
android:translateY="-3.0566037">
|
||||||
|
<path android:fillColor="#d5d2c1"
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<group android:scaleX="1.096875"
|
||||||
|
android:scaleY="1.096875"
|
||||||
|
android:translateX="18.9"
|
||||||
|
android:translateY="18.9">
|
||||||
|
<path
|
||||||
|
android:pathData="M52.445,52.51L12.609,52.51L12.609,39.836q0,-3.581 0.376,-6 0.566,-3.52 3.015,-5.632 2.423,-2.143 5.518,-2.143 2.853,0 5.437,1.898 1.265,0.949 2.315,2.235 1.05,1.255 2.45,4.867 1.345,-5.54 4.225,-8.235 2.853,-2.694 6.514,-2.694 2.503,0 4.737,1.378 2.207,1.347 3.5,3.704 1.237,2.235 1.507,4.867 0.242,2.633 0.242,6.582zM30.185,46.479v-2.112q0,-2.572 -0.323,-4.806 -0.323,-2.235 -1.427,-3.98 -0.969,-1.592 -2.476,-2.48 -1.534,-0.918 -3.23,-0.918 -1.588,0 -2.853,0.796 -1.265,0.796 -1.938,2.051 -0.969,1.714 -1.157,3.95 -0.189,2.203 -0.189,4.683v2.816zM48.461,46.479L48.461,41.03q0,-2.388 -0.188,-4.408 -0.215,-2.051 -0.996,-3.275 -0.808,-1.256 -2.234,-2.02 -1.427,-0.766 -2.988,-0.766 -3.391,0 -5.948,3.428 -2.584,3.398 -2.584,10.286v2.204z"
|
||||||
|
android:strokeWidth="2.939"
|
||||||
|
android:fillColor="#a46843"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M20.337,46.717L20.337,7.802h9.934c3.838,0 6.605,0.342 8.302,1.025 1.698,0.684 3.026,1.815 3.985,3.392 0.978,1.56 1.467,3.243 1.467,5.049 0,1.875 -0.563,3.83 -1.688,5.863 -1.107,2.034 -3.912,4.016 -7.177,5.593l12.315,17.904 -5.553,0.089 -11.12,-16.29 -5.014,-0.354v16.644zM25.788,30.073l4.911,0.35c-1.762,-4.727 3.502,-5.083 5.107,-6.766 1.605,-1.7 2.408,-3.69 2.408,-5.969 0,-1.21 -0.268,-2.26 -0.803,-3.155 -0.517,-0.894 -1.347,-1.586 -2.49,-2.077 -1.144,-0.508 -3.072,-0.763 -5.784,-0.763h-3.349z"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#370d00"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M25.481,48.856a2.436,2.634 0,0 1,-2.426 2.634,2.436 2.634,0 0,1 -2.446,-2.613 2.436,2.634 0,0 1,2.407 -2.655,2.436 2.634,0 0,1 2.465,2.591M47.003,48.803a2.436,2.634 0,0 1,-2.426 2.633,2.436 2.634,0 0,1 -2.446,-2.612 2.436,2.634 0,0 1,2.406 -2.655,2.436 2.634,0 0,1 2.466,2.591"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#370d00"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
BIN
brouter-routing-app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 4.5 KiB |
BIN
brouter-routing-app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.8 KiB |
BIN
brouter-routing-app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 6.3 KiB |
BIN
brouter-routing-app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 9.9 KiB |
BIN
brouter-routing-app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 7 KiB |
After Width: | Height: | Size: 14 KiB |
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -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-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||||
|
|