mirror of
https://github.com/bilde2910/Hauk.git
synced 2026-01-23 02:24:09 +00:00
Merge 3af47da85a into 55d2f8b8fd
This commit is contained in:
commit
bcba1a3f6d
29 changed files with 337 additions and 97 deletions
63
.github/workflows/dockerbuild.yaml
vendored
Normal file
63
.github/workflows/dockerbuild.yaml
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
name: ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: set up qemu
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: set up docker buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
platforms: linux/amd64, linux/arm64
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
FROM php:apache
|
||||
COPY backend-php/ /var/www/html/
|
||||
COPY frontend/ /var/www/html/
|
||||
COPY docker/start.sh .
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev && \
|
||||
|
|
@ -12,7 +9,11 @@ RUN apt-get update && \
|
|||
|
||||
EXPOSE 80/tcp
|
||||
VOLUME /etc/hauk
|
||||
|
||||
STOPSIGNAL SIGINT
|
||||
|
||||
COPY docker/start.sh .
|
||||
RUN chmod +x ./start.sh
|
||||
COPY backend-php/ /var/www/html/
|
||||
COPY frontend/ /var/www/html/
|
||||
|
||||
CMD ["./start.sh"]
|
||||
|
|
|
|||
2
android/.idea/misc.xml
generated
2
android/.idea/misc.xml
generated
|
|
@ -39,7 +39,7 @@
|
|||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
namespace 'info.varden.hauk'
|
||||
|
||||
compileSdkVersion 33
|
||||
defaultConfig {
|
||||
applicationId "info.varden.hauk"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 33
|
||||
versionCode 13
|
||||
versionName "1.6.1"
|
||||
versionName "1.6.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
|
|
@ -21,10 +23,11 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="info.varden.hauk">
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
@ -31,7 +34,8 @@
|
|||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:theme="@style/HomeTheme">
|
||||
android:theme="@style/HomeTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
@ -45,7 +49,7 @@
|
|||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name=".global.Receiver"
|
||||
android:name=".global.ExportedReceiver"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedReceiver">
|
||||
<intent-filter>
|
||||
|
|
@ -53,6 +57,20 @@
|
|||
<action android:name="info.varden.hauk.START_ALONE_THEN_MAKE_TOAST" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".global.RebootReceiver"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
android:name=".notify.CopyLinkReceiver"
|
||||
android:exported="false">
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public enum Constants {
|
|||
public static final Preference<String> PREF_NICKNAME = new Preference.String("nickname", "");
|
||||
public static final Preference<Integer> PREF_DURATION_UNIT = new Preference.Integer("durUnit", Constants.DURATION_UNIT_MINUTES);
|
||||
public static final Preference<Boolean> PREF_ALLOW_ADOPTION = new Preference.Boolean("allowAdoption", true);
|
||||
public static final Preference<Boolean> PREF_RESTART_ON_BOOT = new Preference.Boolean("restartOnBoot", true);
|
||||
public static final Preference<NightModeStyle> PREF_NIGHT_MODE = new Preference.Enum<>("nightMode", NightModeStyle.FOLLOW_SYSTEM);
|
||||
public static final Preference<Boolean> PREF_CONFIRM_STOP = new Preference.Boolean("confirmStop", true);
|
||||
public static final Preference<Boolean> PREF_HIDE_LOGO = new Preference.Boolean("hideLogo", false);
|
||||
|
|
@ -122,6 +123,7 @@ public enum Constants {
|
|||
public static final String PACKET_PARAM_SPEED = "spd";
|
||||
public static final String PACKET_PARAM_TIMESTAMP = "time";
|
||||
public static final String PACKET_PARAM_USERNAME = "usr";
|
||||
public static final String PACKET_PARAM_ALTITUDE = "alt";
|
||||
|
||||
// Packet OK response header. All valid packets start with this line.
|
||||
public static final String PACKET_RESPONSE_OK = "OK";
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ import info.varden.hauk.utils.TimeUtils;
|
|||
* @since 1.3
|
||||
* @author Marius Lindvall
|
||||
*/
|
||||
public final class Receiver extends BroadcastReceiver {
|
||||
public final class ExportedReceiver extends BroadcastReceiver {
|
||||
@SuppressWarnings("HardCodedStringLiteral")
|
||||
private static final String ACTION_START_SHARING_ALONE_WITH_MENU = "info.varden.hauk.START_ALONE_THEN_SHARE_VIA";
|
||||
@SuppressWarnings("HardCodedStringLiteral")
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package info.varden.hauk.global;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import info.varden.hauk.Constants;
|
||||
import info.varden.hauk.R;
|
||||
import info.varden.hauk.global.ui.AuthorizationActivity;
|
||||
import info.varden.hauk.global.ui.DisplayShareDialogListener;
|
||||
import info.varden.hauk.global.ui.toast.GNSSStatusUpdateListenerImpl;
|
||||
import info.varden.hauk.global.ui.toast.SessionInitiationResponseHandlerImpl;
|
||||
import info.varden.hauk.global.ui.toast.ShareListenerImpl;
|
||||
import info.varden.hauk.http.ConnectionParameters;
|
||||
import info.varden.hauk.http.SessionInitiationPacket;
|
||||
import info.varden.hauk.http.security.CertificateValidationPolicy;
|
||||
import info.varden.hauk.manager.SessionManager;
|
||||
import info.varden.hauk.struct.AdoptabilityPreference;
|
||||
import info.varden.hauk.system.LocationPermissionsNotGrantedException;
|
||||
import info.varden.hauk.system.LocationServicesDisabledException;
|
||||
import info.varden.hauk.system.preferences.PreferenceManager;
|
||||
import info.varden.hauk.utils.DeprecationMigrator;
|
||||
import info.varden.hauk.utils.Log;
|
||||
import info.varden.hauk.utils.TimeUtils;
|
||||
// Reboot broadcast receiver for Hauk. If enabled resumes a share session if one exists on device restart
|
||||
public final class RebootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// Subsequent calls may result in data being read from preferences. We should ensure that
|
||||
// all deprecated preferences have been migrated before we continue.
|
||||
new DeprecationMigrator(context).migrate();
|
||||
|
||||
PreferenceManager prefs = new PreferenceManager(context);
|
||||
if(prefs.get(Constants.PREF_RESTART_ON_BOOT))
|
||||
resumeShare(context,intent);
|
||||
}
|
||||
private void resumeShare(Context context, Intent intent) {
|
||||
Log.d("Trying to resume shares...");
|
||||
new BroadcastSessionManager(context).resumeShares();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,15 @@ import android.location.Location;
|
|||
import android.util.Base64;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
||||
import info.varden.hauk.Constants;
|
||||
import info.varden.hauk.R;
|
||||
|
|
@ -48,36 +54,17 @@ public abstract class LocationUpdatePacket extends Packet {
|
|||
super(ctx, session.getServerURL(), session.getConnectionParameters(), Constants.URL_PATH_POST_LOCATION);
|
||||
setParameter(Constants.PACKET_PARAM_SESSION_ID, session.getID());
|
||||
|
||||
if (session.getDerivableE2EKey() == null) {
|
||||
// If not using end-to-end encryption, send parameters in plain text.
|
||||
setParameter(Constants.PACKET_PARAM_LATITUDE, String.valueOf(location.getLatitude()));
|
||||
setParameter(Constants.PACKET_PARAM_LONGITUDE, String.valueOf(location.getLongitude()));
|
||||
setParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, String.valueOf(accuracy.getMode()));
|
||||
setParameter(Constants.PACKET_PARAM_TIMESTAMP, String.valueOf(System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND));
|
||||
Cipher cipher = initCipher(session);
|
||||
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) setParameter(Constants.PACKET_PARAM_SPEED, String.valueOf(location.getSpeed()));
|
||||
if (location.hasAccuracy()) setParameter(Constants.PACKET_PARAM_ACCURACY, String.valueOf(location.getAccuracy()));
|
||||
} else {
|
||||
// We're using end-to-end encryption - generate an IV and encrypt all parameters.
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(Constants.E2E_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, session.getDerivableE2EKey().deriveSpec(), new SecureRandom());
|
||||
byte[] iv = cipher.getIV();
|
||||
setParameter(Constants.PACKET_PARAM_INIT_VECTOR, Base64.encodeToString(iv, Base64.DEFAULT));
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_LATITUDE, location.getLatitude(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_LONGITUDE, location.getLongitude(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, accuracy.getMode(), cipher);
|
||||
encryptAndSetParameter(Constants.PACKET_PARAM_TIMESTAMP, System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND, cipher);
|
||||
|
||||
setParameter(Constants.PACKET_PARAM_LATITUDE, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getLatitude()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_LONGITUDE, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getLongitude()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_PROVIDER_ACCURACY, Base64.encodeToString(cipher.doFinal(String.valueOf(accuracy.getMode()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
setParameter(Constants.PACKET_PARAM_TIMESTAMP, Base64.encodeToString(cipher.doFinal(String.valueOf(System.currentTimeMillis() / (double) TimeUtils.MILLIS_PER_SECOND).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) setParameter(Constants.PACKET_PARAM_SPEED, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getSpeed()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
if (location.hasAccuracy()) setParameter(Constants.PACKET_PARAM_ACCURACY, Base64.encodeToString(cipher.doFinal(String.valueOf(location.getAccuracy()).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
} catch (Exception e) {
|
||||
Log.e("Error was thrown when encrypting location data", e); //NON-NLS
|
||||
}
|
||||
}
|
||||
// Not all devices provide these parameters:
|
||||
if (location.hasSpeed()) encryptAndSetParameter(Constants.PACKET_PARAM_SPEED, location.getSpeed(), cipher);
|
||||
if (location.hasAccuracy()) encryptAndSetParameter(Constants.PACKET_PARAM_ACCURACY, location.getAccuracy(), cipher);
|
||||
if (location.hasAltitude()) encryptAndSetParameter(Constants.PACKET_PARAM_ALTITUDE, location.getAltitude(), cipher);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DesignForExtension")
|
||||
|
|
@ -113,4 +100,33 @@ public abstract class LocationUpdatePacket extends Packet {
|
|||
throw new ServerException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Cipher initCipher(Session session) {
|
||||
Cipher cipher = null;
|
||||
if (session.getDerivableE2EKey() != null) {
|
||||
try {
|
||||
cipher = Cipher.getInstance(Constants.E2E_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, session.getDerivableE2EKey().deriveSpec(), new SecureRandom());
|
||||
byte[] iv = cipher.getIV();
|
||||
setParameter(Constants.PACKET_PARAM_INIT_VECTOR, Base64.encodeToString(iv, Base64.DEFAULT));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | InvalidKeySpecException | NoSuchPaddingException exception) {
|
||||
Log.e("Error was thrown while initializing E2E encryption", exception); //NON-NLS
|
||||
}
|
||||
}
|
||||
return cipher;
|
||||
}
|
||||
|
||||
private <V> void encryptAndSetParameter(String key, V value, Cipher cipher) {
|
||||
if (cipher != null) {
|
||||
// We're using end-to-end encryption - generate an IV and encrypt all parameters.
|
||||
try {
|
||||
setParameter(key, Base64.encodeToString(cipher.doFinal(String.valueOf(value).getBytes(StandardCharsets.UTF_8)), Base64.DEFAULT));
|
||||
} catch (BadPaddingException | IllegalBlockSizeException exception) {
|
||||
Log.e("Error was thrown while encrypting location data", exception); //NON-NLS
|
||||
}
|
||||
} else {
|
||||
// If not using end-to-end encryption, send parameters in plain text.
|
||||
setParameter(key, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,6 +206,15 @@ public abstract class SessionManager {
|
|||
}
|
||||
}
|
||||
|
||||
public final void resumeShares() {
|
||||
if (pusher != null) {
|
||||
Log.d("Pusher is non-null (%s), stopping and nulling it before calling service relauncher", pusher); //NON-NLS
|
||||
this.ctx.stopService(pusher);
|
||||
pusher = null;
|
||||
}
|
||||
this.resumable.tryResumeShare(new ServiceRelauncher(this, this.resumable));
|
||||
}
|
||||
|
||||
/**
|
||||
* A preparation step for initiating sessions. Checks location services status and instantiates
|
||||
* a response handler for the session initiation packet.
|
||||
|
|
@ -448,6 +457,7 @@ public abstract class SessionManager {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The GNSS status handler that the {@link SessionManager} itself uses to receive status updates
|
||||
* from the GNSS listeners. Used to propagate events further upstream.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<resources>
|
||||
<string name="pref_cryptUsername_title">Nom d\'usuari</string>
|
||||
<string name="pref_cryptUsername_hint"><opcional></string>
|
||||
<string name="action_settings">Configuracions</string>
|
||||
<string name="pref_cryptPassword_title">Password:</string>
|
||||
<string name="label_duration">Durada de la compartició:</string>
|
||||
<string name="pref_interval_title">Interval d\'actualització (s):</string>
|
||||
|
|
@ -115,4 +116,4 @@
|
|||
\n
|
||||
\nSi activeu aquesta opció i compartiu la vostra ubicació amb un grup d’amics i algú altre també vol compartir la seva ubicació, pot crear un grup compartit i afegir-lo al seu mapa, de manera que tots dos aparegueu al mateix mapa d’ubicació compartit. Això és útil si és conduir i no es pot utilitzar el telèfon, ja que l\'adopció de la seva participació per part d\'altres persones no requereix cap interacció per part seva.</string>
|
||||
<string name="err_ver_group">El servidor no admet comparticions de grup. Les accions del grup són compatibles amb la versió %s i superiors; actualment el servidor està executant la versió %s. La vostra participació s\'ha convertit en una participació individual.</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<string name="label_heading">Kode irekiko kokaleku partekatzea</string>
|
||||
<string name="label_status_none">Kokaleku partekatzea inaktibo</string>
|
||||
<string name="btn_ok">Ados</string>
|
||||
<string name="action_settings">Ezarpenak</string>
|
||||
<string name="err_malformed_url">Sartu duzun zerbitzari URL-a baliogabea da.</string>
|
||||
<string name="req_perms_message">Aplikazio honek zure kokalekua atzitzeko baimena behar du funtzionatzeko, baina baimen hau ez da oraindik eman. Onartu hurrengo baimen eskaera eta gero sakatu hasi botoia berriro saiatzeko.</string>
|
||||
<string name="pref_cryptServer_title">Zerbitzariaren URLa</string>
|
||||
|
|
@ -93,4 +94,4 @@
|
|||
\nIraungitzea: %s
|
||||
\n
|
||||
\nSaio hau berreskuratu nahi duzu\?</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="label_nickname">Нік:</string>
|
||||
<string name="action_settings">налаштування</string>
|
||||
<string name="err_ver_group">Сервер не підтримує групові шари. Групові шари підтримуються в версії %s та вище; цей сервер зараз має версію %s. Ваша шара була перетворена на індивідуальну.</string>
|
||||
<string name="btn_link">Створити новий відстежувальний лінк</string>
|
||||
<string name="ok_message">Шарінг місцеположення активний! Нажміть кнопку поділитися щоб скопіювати публічно доступний URL для вашої шари.</string>
|
||||
|
|
@ -107,4 +108,4 @@
|
|||
<string name="authorize_question">Ви хочете дозволити в майбутньому створювати сеанси з трансляцій із цією ідентифікацією\?</string>
|
||||
<string name="err_malformed_url">URL серверу що ви ввели не вірний.</string>
|
||||
<string name="err_location_disabled">Сервіси місцеположення вимкнуті або заборонені. Будь ласка включіть високоточні сервіси місцеположення для того щоб можно було ділитися вашим положенням.</string>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -155,6 +155,10 @@
|
|||
<string name="tls_validation_no_anchor_onion">Disable trust anchor validation for .onion hosts (not recommended)</string>
|
||||
<string name="tls_validation_none_for_onion">Disable trust anchor and hostname validation for .onion hosts (not recommended)</string>
|
||||
|
||||
<string name="pref_restartOnBoot_title">Resume shares on device restart</string>
|
||||
<string name="pref_restartOnBoot_summaryOn">Resume shares on boot</string>
|
||||
<string name="pref_restartOnBoot_summaryOff">Do not resume shares on boot</string>
|
||||
|
||||
<!-- Appearance settings -->
|
||||
<string name="prefs_header_appearance">Appearance</string>
|
||||
<string name="prefs_nightMode_title">Night mode</string>
|
||||
|
|
|
|||
|
|
@ -104,6 +104,11 @@
|
|||
app:entries="@array/tls_validation_types"
|
||||
app:entryValues="@array/tls_validation_type_values" />
|
||||
|
||||
<SwitchPreference
|
||||
app:key="restartOnBoot"
|
||||
app:title="@string/pref_restartOnBoot_title"
|
||||
app:summaryOff="@string/pref_restartOnBoot_summaryOff"
|
||||
app:summaryOn="@string/pref_restartOnBoot_summaryOn" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/prefs_header_appearance">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ buildscript {
|
|||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
|
||||
|
|
|
|||
0
android/gradlew
vendored
Executable file → Normal file
0
android/gradlew
vendored
Executable file → Normal file
|
|
@ -21,45 +21,37 @@ $sid = $_POST["sid"];
|
|||
$session = new Client($memcache, $sid);
|
||||
if (!$session->exists()) die($LANG['session_expired']."\n");
|
||||
|
||||
if (!$session->isEncrypted()) {
|
||||
// Perform input validation.
|
||||
$lat = floatval($_POST["lat"]);
|
||||
$lon = floatval($_POST["lon"]);
|
||||
$time = floatval($_POST["time"]);
|
||||
if ($lat < -90 || $lat > 90 || $lon < -180 || $lon > 180) die($LANG['location_invalid']."\n");
|
||||
$lat = $_POST["lat"];
|
||||
$lon = $_POST["lon"];
|
||||
$time = $_POST["time"];
|
||||
$speed = isset($_POST["spd"]) ? $_POST["spd"] : null;
|
||||
$altitude = isset($_POST["alt"]) ? $_POST["alt"] : null;
|
||||
$accuracy = isset($_POST["acc"]) ? $_POST["acc"] : null;
|
||||
$provider = isset($_POST["prv"]) ? $_POST["prv"] : null;
|
||||
|
||||
// Not all devices report speed and accuracy, but if available, report them
|
||||
// too.
|
||||
$speed = isset($_POST["spd"]) ? floatval($_POST["spd"]) : null;
|
||||
$accuracy = isset($_POST["acc"]) ? floatval($_POST["acc"]) : null;
|
||||
$provider = isset($_POST["prv"]) && $_POST["prv"] == "1" ? 1 : 0;
|
||||
|
||||
// The location data object contains the sharing interval (i), duration (d)
|
||||
// and a location list (l). Each entry in the location list contains a
|
||||
// latitude, longitude, timestamp, provider, accuracy and speed, in that
|
||||
// order, as an array.
|
||||
$session->addPoint([$lat, $lon, $time, $provider, $accuracy, $speed])->save();
|
||||
|
||||
} else {
|
||||
// Input validation cannot be performed for end-to-end encrypted data.
|
||||
$lat = $_POST["lat"];
|
||||
$lon = $_POST["lon"];
|
||||
$time = $_POST["time"];
|
||||
$speed = isset($_POST["spd"]) ? $_POST["spd"] : null;
|
||||
$accuracy = isset($_POST["acc"]) ? $_POST["acc"] : null;
|
||||
$provider = isset($_POST["prv"]) ? $_POST["prv"] : null;
|
||||
// The location data object contains the sharing interval (i), duration (d)
|
||||
// and a location list (l). Each entry in the location list contains a
|
||||
// latitude, longitude, timestamp, provider, accuracy and speed, in that
|
||||
// order, as an array.
|
||||
$point = [$lat, $lon, $time, $provider, $accuracy, $speed, $altitude];
|
||||
|
||||
if ($session->isEncrypted()) {
|
||||
// End-to-end encrypted connections also have an IV field used to decrypt
|
||||
// the data fields.
|
||||
requirePOST("iv");
|
||||
$iv = $_POST["iv"];
|
||||
|
||||
// The IV field is prepended to the array to send to the client.
|
||||
$session->addPoint([$iv, $lat, $lon, $time, $provider, $accuracy, $speed])->save();
|
||||
array_unshift($point , $iv);
|
||||
} else {
|
||||
// Perform input validation
|
||||
if (floatval($lat) < -90 || floatval($lat) > 90 || floatval($lon) < -180 || floatval($lon) > 180) die($LANG['location_invalid']."\n");
|
||||
}
|
||||
|
||||
$session->addPoint($point)->save();
|
||||
|
||||
if ($session->hasExpired()) {
|
||||
echo $LANG['session_expired']."\n";
|
||||
} else {
|
||||
echo "OK\n".getConfig("public_url")."?%s\n".implode(",", $session->getTargetIDs())."\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -15,5 +15,8 @@ var MAX_POINTS = <?php echo json_encode(getConfig("max_shown_pts")); ?>;
|
|||
var VELOCITY_DELTA_TIME = <?php echo json_encode(getConfig("v_data_points")); ?>;
|
||||
var TRAIL_COLOR = <?php echo json_encode(getConfig("trail_color")); ?>;
|
||||
var VELOCITY_UNIT = <?php echo json_encode(getConfig("velocity_unit")); ?>;
|
||||
var ALTITUDE_UNIT = <?php echo json_encode(getConfig("altitude_unit")); ?>;
|
||||
var SHOW_VELOCITY = <?php echo json_encode(getConfig("show_velocity")); ?>;
|
||||
var SHOW_ALTITUDE_AMSL = <?php echo json_encode(getConfig("show_altitude_amsl")); ?>;
|
||||
var OFFLINE_TIMEOUT = <?php echo json_encode(getConfig("offline_timeout")); ?>;
|
||||
var REQUEST_TIMEOUT = <?php echo json_encode(getConfig("request_timeout")); ?>;
|
||||
|
|
|
|||
|
|
@ -274,6 +274,16 @@
|
|||
// KILOMETERS_PER_HOUR, MILES_PER_HOUR, METERS_PER_SECOND
|
||||
"velocity_unit" => KILOMETERS_PER_HOUR,
|
||||
|
||||
// The unit of measurement of altitude. Valid are:
|
||||
// METERS, FEET
|
||||
"altitude_unit" => METERS,
|
||||
|
||||
// Display velocity below marker
|
||||
"show_velocity" => true,
|
||||
|
||||
// Display altitude AMSL below marker
|
||||
"show_altitude_amsl"=> true,
|
||||
|
||||
// The publicly accessible URL to reach Hauk, with trailing slash.
|
||||
"public_url" => 'https://example.com/'
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,16 @@ const METERS_PER_SECOND = array(
|
|||
"mpsMultiplier" => 1,
|
||||
"unit" => "m/s"
|
||||
);
|
||||
const METERS = array(
|
||||
// Absolute distance in meters
|
||||
"metersMultiplier" => 1,
|
||||
"unit" => "m"
|
||||
);
|
||||
const FEET = array(
|
||||
// Absolute distance in feet
|
||||
"metersMultiplier" => 3.280839895,
|
||||
"unit" => "ft"
|
||||
);
|
||||
|
||||
// Load fallback language.
|
||||
include(__DIR__."/lang/en/texts.php");
|
||||
|
|
@ -149,6 +159,9 @@ const DEFAULTS = array(
|
|||
"v_data_points" => 2,
|
||||
"trail_color" => '#d80037',
|
||||
"velocity_unit" => KILOMETERS_PER_HOUR,
|
||||
"altitude_unit" => METERS,
|
||||
"show_velocity" => true,
|
||||
"show_altitude_amsl" => true,
|
||||
"public_url" => 'https://example.com/'
|
||||
|
||||
);
|
||||
|
|
|
|||
|
|
@ -700,12 +700,13 @@ function processUpdate(data, init) {
|
|||
var lastPoint = shares[user].points.length > 0 ? shares[user].points[shares[user].points.length - 1] : null;
|
||||
|
||||
for (var i = 0; i < users[user].length; i++) {
|
||||
var lat = users[user][i][0];
|
||||
var lon = users[user][i][1];
|
||||
var time = users[user][i][2];
|
||||
var prov = users[user][i][3];
|
||||
var acc = users[user][i][4];
|
||||
var spd = users[user][i][5];
|
||||
var lat = getValueOrNull(users[user][i],0);
|
||||
var lon = getValueOrNull(users[user][i],1);
|
||||
var time = getValueOrNull(users[user][i],2);
|
||||
var prov = getValueOrNull(users[user][i],3);
|
||||
var acc = getValueOrNull(users[user][i],4);
|
||||
var spd = getValueOrNull(users[user][i],5);
|
||||
var alt = getValueOrNull(users[user][i],6);
|
||||
|
||||
// Default to "Fine" provider for older clients.
|
||||
if (prov === null) prov = LOC_PROVIDER_FINE;
|
||||
|
|
@ -726,14 +727,23 @@ function processUpdate(data, init) {
|
|||
'<div class="arrow still-' + shares[user].state + '" id="arrow-' + shares[user].id + '"></div>' +
|
||||
'<p class="' + shares[user].state + '" id="label-' + shares[user].id + '">' +
|
||||
'<span id="nickname-' + shares[user].id + '"></span>' +
|
||||
'<span class="velocity">' +
|
||||
'<span id="velocity-' + shares[user].id + '">0.0</span> ' +
|
||||
VELOCITY_UNIT.unit +
|
||||
'</span><span class="offline" id="last-seen-' + shares[user].id + '">' +
|
||||
'<span class="metric' + (SHOW_VELOCITY ? "" : " hidden") + '">' +
|
||||
'<span class="metric-label">vel:</span>' +
|
||||
'<span class="metric-value"><span id="velocity-' + shares[user].id + '">0.0</span> ' +
|
||||
VELOCITY_UNIT.unit + '</span>' +
|
||||
'<br>' +
|
||||
'</span>' +
|
||||
'<span id="altitude-container-' + shares[user].id + '" class="metric'+ (SHOW_ALTITUDE_AMSL ? "" : " hidden") + '">' +
|
||||
'<span class="metric-label">alt:</span>' +
|
||||
'<span class="metric-value"><span id="altitude-' + shares[user].id + '">0.0</span> ' +
|
||||
ALTITUDE_UNIT.unit + '</span>' +
|
||||
'</span>' +
|
||||
'<span class="offline" id="last-seen-' + shares[user].id + '">' +
|
||||
'</span>' +
|
||||
'</p>' +
|
||||
'</div>',
|
||||
iconAnchor: [33, 18]
|
||||
// FIXME: hard-coded and dependend on style.css .marker
|
||||
iconAnchor: [48, 18]
|
||||
});
|
||||
shares[user].marker = L.marker([lat, lon], {icon: shares[user].icon}).on("click", function() {
|
||||
follow(this.haukUser);
|
||||
|
|
@ -754,7 +764,7 @@ function processUpdate(data, init) {
|
|||
shares[user].circle.setLatLng([lat, lon]);
|
||||
if (acc !== null) shares[user].circle.setRadius(acc);
|
||||
}
|
||||
shares[user].points.push({lat: lat, lon: lon, line: line, time: time, spd: spd, acc: acc});
|
||||
shares[user].points.push({lat: lat, lon: lon, line: line, time: time, spd: spd, acc: acc, alt: alt});
|
||||
lastPoint = shares[user].points[shares[user].points.length - 1];
|
||||
}
|
||||
}
|
||||
|
|
@ -797,6 +807,20 @@ function processUpdate(data, init) {
|
|||
vel = velocity(dist, time);
|
||||
eVelocity.textContent = vel.toFixed(1);;
|
||||
}
|
||||
|
||||
// Altitude (If available)
|
||||
var eAltitude = document.getElementById("altitude-" + shares[user].id);
|
||||
var alt = 0;
|
||||
var eAltitudeContainer = document.getElementById("altitude-container-" + shares[user].id);
|
||||
if (lastPoint !== null && lastPoint.alt !== null && eAltitude !== null) {
|
||||
alt = lastPoint.alt * ALTITUDE_UNIT.metersMultiplier;
|
||||
eAltitude.textContent = alt.toFixed(1);
|
||||
if (SHOW_ALTITUDE_AMSL) {
|
||||
eAltitudeContainer.classList.remove("hidden");
|
||||
}
|
||||
} else {
|
||||
eAltitudeContainer.classList.add("hidden");
|
||||
}
|
||||
|
||||
// Flag that the first location has been received, for map centering.
|
||||
if (lastPoint !== null && !hasReceivedFirst) {
|
||||
|
|
@ -906,6 +930,14 @@ function processUpdate(data, init) {
|
|||
}
|
||||
}
|
||||
|
||||
function getValueOrNull(points,idx) {
|
||||
var value = null;
|
||||
if (idx < points.length) {
|
||||
value = points[idx];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Calculates the distance between two points on a sphere using the Haversine
|
||||
// algorithm.
|
||||
function distance(from, to) {
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ a:last-child > .store-icon {
|
|||
|
||||
/* The outer marker div. */
|
||||
.marker {
|
||||
width: 66px;
|
||||
width: 96px;
|
||||
height: 62px;
|
||||
}
|
||||
|
||||
|
|
@ -312,8 +312,8 @@ a:last-child > .store-icon {
|
|||
width: 100%;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
padding: 2px 0;
|
||||
line-height: 100%;
|
||||
padding: 5px 0 5px;
|
||||
line-height: 125%;
|
||||
font-family: sans-serif;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
|
@ -334,7 +334,7 @@ a:last-child > .store-icon {
|
|||
background-color: rgba(165,0,42,0.5);
|
||||
}
|
||||
|
||||
.marker p.dead > span.velocity {
|
||||
.marker p.dead > span.metric {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
@ -343,3 +343,20 @@ a:last-child > .store-icon {
|
|||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
display:inline-block;
|
||||
width: 30%;
|
||||
text-align:right;
|
||||
vertical-align: bottom;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
display:inline-block;
|
||||
width: 70%;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
|
|
|||
0
hooks/build
Executable file → Normal file
0
hooks/build
Executable file → Normal file
0
hooks/post_push
Executable file → Normal file
0
hooks/post_push
Executable file → Normal file
0
hooks/pre_build
Executable file → Normal file
0
hooks/pre_build
Executable file → Normal file
0
hooks/pre_push
Executable file → Normal file
0
hooks/pre_push
Executable file → Normal file
0
install.sh
Executable file → Normal file
0
install.sh
Executable file → Normal file
Loading…
Add table
Add a link
Reference in a new issue