├── .gitattributes ├── .github └── workflows │ └── docker-image.yml ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── android ├── .gitignore ├── .idea │ ├── codeStyles │ │ ├── Project.xml │ │ └── codeStyleConfig.xml │ ├── dictionaries │ │ └── kruzah.xml │ ├── encodings.xml │ ├── gradle.xml │ ├── inspectionProfiles │ │ └── Project_Default.xml │ ├── jarRepositories.xml │ ├── misc.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── info │ │ │ └── varden │ │ │ └── hauk │ │ │ ├── system │ │ │ └── preferences │ │ │ │ └── PreferenceTest.java │ │ │ └── utils │ │ │ ├── ReceiverDataRegistryTest.java │ │ │ └── StringSerializerTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── info │ │ │ │ └── varden │ │ │ │ └── hauk │ │ │ │ ├── Constants.java │ │ │ │ ├── caching │ │ │ │ ├── ResumableSessions.java │ │ │ │ ├── ResumeHandler.java │ │ │ │ └── ResumePrompt.java │ │ │ │ ├── dialog │ │ │ │ ├── AdoptDialogBuilder.java │ │ │ │ ├── Buttons.java │ │ │ │ ├── CustomDialogBuilder.java │ │ │ │ ├── DialogService.java │ │ │ │ └── StopSharingConfirmationPrompt.java │ │ │ │ ├── global │ │ │ │ ├── BroadcastSessionManager.java │ │ │ │ ├── Receiver.java │ │ │ │ └── ui │ │ │ │ │ ├── AuthorizationActivity.java │ │ │ │ │ ├── DisplayShareDialogListener.java │ │ │ │ │ └── toast │ │ │ │ │ ├── GNSSStatusUpdateListenerImpl.java │ │ │ │ │ ├── SessionInitiationResponseHandlerImpl.java │ │ │ │ │ └── ShareListenerImpl.java │ │ │ │ ├── http │ │ │ │ ├── AdoptSharePacket.java │ │ │ │ ├── ConnectionParameters.java │ │ │ │ ├── ConnectionThread.java │ │ │ │ ├── FailureHandler.java │ │ │ │ ├── LocationUpdatePacket.java │ │ │ │ ├── NewLinkPacket.java │ │ │ │ ├── Packet.java │ │ │ │ ├── ServerException.java │ │ │ │ ├── SessionInitiationPacket.java │ │ │ │ ├── StopSharingPacket.java │ │ │ │ ├── parameter │ │ │ │ │ └── LocationProvider.java │ │ │ │ ├── proxy │ │ │ │ │ └── NameResolverTask.java │ │ │ │ └── security │ │ │ │ │ ├── CertificateValidationPolicy.java │ │ │ │ │ ├── InsecureHostnameVerifier.java │ │ │ │ │ └── InsecureTrustManager.java │ │ │ │ ├── manager │ │ │ │ ├── AutoResumptionPrompter.java │ │ │ │ ├── GNSSStatusUpdateListener.java │ │ │ │ ├── PromptCallback.java │ │ │ │ ├── ServiceRelauncher.java │ │ │ │ ├── SessionInitiationReason.java │ │ │ │ ├── SessionInitiationResponseHandler.java │ │ │ │ ├── SessionListener.java │ │ │ │ ├── SessionManager.java │ │ │ │ ├── ShareListener.java │ │ │ │ ├── StopSharingCallback.java │ │ │ │ └── StopSharingTask.java │ │ │ │ ├── notify │ │ │ │ ├── CopyLinkReceiver.java │ │ │ │ ├── HaukBroadcastReceiver.java │ │ │ │ ├── HaukNotification.java │ │ │ │ ├── Receiver.java │ │ │ │ ├── ReopenIntent.java │ │ │ │ ├── SharingNotification.java │ │ │ │ └── StopSharingReceiver.java │ │ │ │ ├── service │ │ │ │ ├── GNSSActiveHandler.java │ │ │ │ ├── LocationListenerBase.java │ │ │ │ ├── LocationPushService.java │ │ │ │ └── MultiTargetGNSSHandlerProxy.java │ │ │ │ ├── struct │ │ │ │ ├── AdoptabilityPreference.java │ │ │ │ ├── KeyDerivable.java │ │ │ │ ├── Session.java │ │ │ │ ├── Share.java │ │ │ │ ├── ShareMode.java │ │ │ │ └── Version.java │ │ │ │ ├── system │ │ │ │ ├── LocationPermissionsNotGrantedException.java │ │ │ │ ├── LocationServicesDisabledException.java │ │ │ │ ├── launcher │ │ │ │ │ ├── ActionLauncher.java │ │ │ │ │ ├── ComponentLauncher.java │ │ │ │ │ ├── Launcher.java │ │ │ │ │ └── OpenLinkListener.java │ │ │ │ ├── powersaving │ │ │ │ │ ├── Device.java │ │ │ │ │ ├── DeviceChecker.java │ │ │ │ │ └── WarningDialog.java │ │ │ │ ├── preferences │ │ │ │ │ ├── IndexedEnum.java │ │ │ │ │ ├── InvalidPreferenceTypeException.java │ │ │ │ │ ├── Preference.java │ │ │ │ │ ├── PreferenceAssignmentException.java │ │ │ │ │ ├── PreferenceHandler.java │ │ │ │ │ ├── PreferenceManager.java │ │ │ │ │ ├── PreferenceNotFoundException.java │ │ │ │ │ ├── indexresolver │ │ │ │ │ │ ├── NightModeStyle.java │ │ │ │ │ │ ├── ProxyTypeResolver.java │ │ │ │ │ │ └── Resolver.java │ │ │ │ │ └── ui │ │ │ │ │ │ ├── SettingsActivity.java │ │ │ │ │ │ └── listener │ │ │ │ │ │ ├── CascadeBindListener.java │ │ │ │ │ │ ├── CascadeChangeListener.java │ │ │ │ │ │ ├── FloatBoundChangeListener.java │ │ │ │ │ │ ├── HintBindListener.java │ │ │ │ │ │ ├── InputTypeBindListener.java │ │ │ │ │ │ ├── IntegerBoundChangeListener.java │ │ │ │ │ │ ├── NightModeChangeListener.java │ │ │ │ │ │ └── ProxyPreferenceChangeListener.java │ │ │ │ └── security │ │ │ │ │ ├── EncryptedData.java │ │ │ │ │ ├── EncryptionException.java │ │ │ │ │ ├── KeyStoreAlias.java │ │ │ │ │ └── KeyStoreHelper.java │ │ │ │ ├── ui │ │ │ │ ├── DialogPacketFailureHandler.java │ │ │ │ ├── GNSSStatusLabelUpdater.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── ProxyHostnameResolverImpl.java │ │ │ │ ├── RepeatingUIThreadTaskExecutor.java │ │ │ │ ├── ShareLinkLayoutManager.java │ │ │ │ ├── StopSharingUICallback.java │ │ │ │ ├── TextViewCountdownRunner.java │ │ │ │ └── listener │ │ │ │ │ ├── AddLinkClickListener.java │ │ │ │ │ ├── InitiateAdoptionClickListener.java │ │ │ │ │ ├── SelectionModeChangedListener.java │ │ │ │ │ ├── ShareLinkClickListener.java │ │ │ │ │ └── StopLinkClickListener.java │ │ │ │ └── utils │ │ │ │ ├── DeprecationMigrator.java │ │ │ │ ├── Log.java │ │ │ │ ├── ReceiverDataRegistry.java │ │ │ │ ├── StringSerializer.java │ │ │ │ ├── StringUtils.java │ │ │ │ └── TimeUtils.java │ │ └── res │ │ │ ├── drawable │ │ │ ├── ic_brightness_3.xml │ │ │ ├── ic_bug_report.xml │ │ │ ├── ic_button_copy.xml │ │ │ ├── ic_button_share.xml │ │ │ ├── ic_button_stop.xml │ │ │ ├── ic_code.xml │ │ │ ├── ic_directions_walk.xml │ │ │ ├── ic_icon.xml │ │ │ ├── ic_image.xml │ │ │ ├── ic_location_disabled.xml │ │ │ ├── ic_lock_open.xml │ │ │ ├── ic_logo.xml │ │ │ ├── ic_no_connection.xml │ │ │ ├── ic_notify.xml │ │ │ ├── ic_person.xml │ │ │ ├── ic_proxy.xml │ │ │ ├── ic_security.xml │ │ │ ├── ic_server.xml │ │ │ ├── ic_settings.xml │ │ │ └── ic_timer.xml │ │ │ ├── layout │ │ │ ├── activity_authorize_broadcast.xml │ │ │ ├── activity_main.xml │ │ │ ├── content_link.xml │ │ │ ├── dialog_create_link.xml │ │ │ └── settings_activity.xml │ │ │ ├── menu │ │ │ └── title_menu.xml │ │ │ ├── values-ca │ │ │ └── strings.xml │ │ │ ├── values-de │ │ │ └── strings.xml │ │ │ ├── values-es │ │ │ └── strings.xml │ │ │ ├── values-eu │ │ │ └── strings.xml │ │ │ ├── values-fr │ │ │ └── strings.xml │ │ │ ├── values-it │ │ │ └── strings.xml │ │ │ ├── values-nb-rNO │ │ │ └── strings.xml │ │ │ ├── values-nl │ │ │ └── strings.xml │ │ │ ├── values-nn │ │ │ └── strings.xml │ │ │ ├── values-pl │ │ │ └── strings.xml │ │ │ ├── values-ro │ │ │ └── strings.xml │ │ │ ├── values-ru │ │ │ └── strings.xml │ │ │ ├── values-tr │ │ │ └── strings.xml │ │ │ ├── values-uk │ │ │ └── strings.xml │ │ │ ├── values │ │ │ ├── arrays.xml │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ │ └── xml │ │ │ ├── backup_descriptor.xml │ │ │ ├── network_security.xml │ │ │ └── root_preferences.xml │ │ └── test │ │ └── java │ │ └── info │ │ └── varden │ │ └── hauk │ │ └── utils │ │ └── TimeUtilsTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── backend-php ├── api │ ├── adopt.php │ ├── create.php │ ├── fetch.php │ ├── new-link.php │ ├── post.php │ └── stop.php ├── dynamic.js.php └── include │ ├── .htaccess │ ├── config-sample.php │ ├── inc.php │ ├── lang │ ├── ca │ │ └── texts.php │ ├── de │ │ └── texts.php │ ├── en │ │ └── texts.php │ ├── eu │ │ └── texts.php │ ├── fr │ │ └── texts.php │ ├── it │ │ └── texts.php │ ├── ko │ │ └── texts.php │ ├── nb_NO │ │ └── texts.php │ ├── nl │ │ └── texts.php │ ├── nn │ │ └── texts.php │ ├── pl │ │ └── texts.php │ ├── ro │ │ └── texts.php │ ├── ru │ │ └── texts.php │ ├── tr │ │ └── texts.php │ └── uk │ │ └── texts.php │ └── wrapper │ ├── memcache.php │ ├── memcached.php │ └── redis.php ├── docker ├── Dockerfile.amd64 ├── Dockerfile.arm32v7 ├── Dockerfile.arm64v8 └── start.sh ├── fastlane └── metadata │ └── android │ └── en-US │ ├── changelogs │ ├── 1.txt │ ├── 10.txt │ ├── 11.txt │ ├── 12.txt │ ├── 13.txt │ ├── 14.txt │ ├── 2.txt │ ├── 3.txt │ ├── 4.txt │ ├── 5.txt │ ├── 6.txt │ ├── 7.txt │ ├── 8.txt │ └── 9.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ └── phoneScreenshots │ │ ├── 01-main.jpg │ │ ├── 02-established.jpg │ │ ├── 03-share.jpg │ │ ├── 04-notification.jpg │ │ └── 05-settings.jpg │ ├── short_description.txt │ └── title.txt ├── frontend ├── assets │ ├── controls │ │ ├── locate-active.svg │ │ ├── locate-inactive.svg │ │ ├── locate-pending.svg │ │ └── radar.svg │ ├── favicon.png │ ├── favicon.svg │ ├── lang │ │ ├── ca.json │ │ ├── de.json │ │ ├── en.json │ │ ├── eu.json │ │ ├── fr.json │ │ ├── it.json │ │ ├── ko.json │ │ ├── nb_NO.json │ │ ├── nl.json │ │ ├── nn.json │ │ ├── pl.json │ │ ├── pt_BR.json │ │ ├── ro.json │ │ ├── ru.json │ │ ├── tr.json │ │ └── uk.json │ ├── location-pending.svg │ ├── logo.svg │ └── markers │ │ ├── moving-dead.svg │ │ ├── moving-live.svg │ │ ├── moving-rough.svg │ │ ├── still-dead.svg │ │ ├── still-live.svg │ │ ├── still-rough.svg │ │ └── still-self.svg ├── index.html ├── lib │ └── leaflet │ │ └── 1.6.0 │ │ ├── leaflet.css │ │ └── leaflet.js ├── main.js └── style.css ├── hooks ├── build ├── post_push ├── pre_build └── pre_push └── install.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker image 2 | 3 | on: 4 | push: 5 | # branches: 6 | # - 'master' 7 | tags: 8 | - 'v*' 9 | pull_request: 10 | branches: 11 | - 'master' 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - # Check out repository 18 | name: Checkout 19 | uses: actions/checkout@v3 20 | - # Set up QEMU for multiarch support 21 | name: Set up QEMU 22 | uses: docker/setup-qemu-action@v1 23 | - # Set up Docker BuildX for Docker image building 24 | name: Set up Docker Buildx 25 | id: buildx 26 | uses: docker/setup-buildx-action@v1 27 | - # Set proper tags 28 | name: Docker metadata 29 | id: docker_meta # you'll use this in the next step 30 | uses: docker/metadata-action@v3 31 | with: 32 | images: bilde2910/hauk 33 | tags: | 34 | type=ref,event=branch 35 | type=ref,event=pr 36 | type=semver,pattern=v{{version}} 37 | type=semver,pattern=stable-{{major}}.x 38 | - # Log in to Docker Hub 39 | name: Login to Docker Hub 40 | if: github.event_name != 'pull_request' 41 | uses: docker/login-action@v1 42 | with: 43 | username: ${{ secrets.DOCKERHUB_USERNAME }} 44 | password: ${{ secrets.DOCKERHUB_TOKEN }} 45 | - # Build and push 46 | name: Build and push 47 | uses: docker/build-push-action@v2 48 | with: 49 | context: . 50 | platforms: linux/amd64,linux/arm/v7,linux/arm64 51 | push: ${{ github.event_name != 'pull_request' }} 52 | tags: ${{ steps.docker_meta.outputs.tags }} 53 | labels: ${{ steps.docker_meta.outputs.labels }} 54 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:apache 2 | COPY backend-php/ /var/www/html/ 3 | COPY frontend/ /var/www/html/ 4 | COPY docker/start.sh . 5 | 6 | RUN apt-get update && \ 7 | apt-get install -y memcached libmemcached-dev zlib1g-dev libldap2-dev libssl-dev && \ 8 | pecl install memcached && \ 9 | docker-php-ext-enable memcached && \ 10 | docker-php-ext-configure ldap --with-libdir=lib/*-linux-gnu*/ && \ 11 | docker-php-ext-install ldap 12 | 13 | EXPOSE 80/tcp 14 | VOLUME /etc/hauk 15 | 16 | STOPSIGNAL SIGINT 17 | RUN chmod +x ./start.sh 18 | CMD ["./start.sh"] 19 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /android/.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /android/.idea/dictionaries/kruzah.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | adoptability 5 | backends 6 | gnss 7 | hauk 8 | huawei 9 | miui 10 | oneplus 11 | powerkeeper 12 | resumable 13 | sharedpref 14 | varden 15 | xiaomi 16 | xxxx 17 | 18 | 19 | -------------------------------------------------------------------------------- /android/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /android/.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release 3 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 33 5 | defaultConfig { 6 | applicationId "info.varden.hauk" 7 | minSdkVersion 24 8 | targetSdkVersion 33 9 | versionCode 14 10 | versionName "1.6.2" 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | namespace 'info.varden.hauk' 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'androidx.appcompat:appcompat:1.6.1' 25 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 26 | implementation 'androidx.preference:preference:1.2.1' 27 | testImplementation 'junit:junit:4.13.2' 28 | androidTestImplementation 'androidx.test:runner:1.5.2' 29 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 30 | } 31 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/app/src/androidTest/java/info/varden/hauk/utils/ReceiverDataRegistryTest.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static org.hamcrest.CoreMatchers.*; 9 | import static org.junit.Assert.*; 10 | 11 | public final class ReceiverDataRegistryTest { 12 | 13 | @Test 14 | public void registryTest() { 15 | // Create a dummy object to store 16 | List testObject = new ArrayList<>(); 17 | testObject.add(48); 18 | testObject.add(19); 19 | 20 | // Store the object 21 | int index = ReceiverDataRegistry.register(testObject); 22 | 23 | // Retrieve the object 24 | Object retrieved = ReceiverDataRegistry.retrieve(index); 25 | assertThat("Retrieved object is wrong type", retrieved, is(instanceOf(testObject.getClass()))); 26 | assertThat("Retrieved object is different instance", retrieved, is(sameInstance((Object) testObject))); 27 | } 28 | } -------------------------------------------------------------------------------- /android/app/src/androidTest/java/info/varden/hauk/utils/StringSerializerTest.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | import org.junit.Test; 4 | 5 | import info.varden.hauk.struct.Version; 6 | 7 | import static org.hamcrest.CoreMatchers.*; 8 | import static org.junit.Assert.*; 9 | 10 | public final class StringSerializerTest { 11 | 12 | @Test 13 | public void stateEqual() { 14 | Version testObject = new Version("3.14.159"); 15 | 16 | String serialized = StringSerializer.serialize(testObject); 17 | Version deSerialized = StringSerializer.deserialize(serialized); 18 | 19 | assertThat("Does not serialize", deSerialized, is(not(sameInstance(testObject)))); 20 | assertThat("Does not equal", deSerialized, is(equalTo(testObject))); 21 | } 22 | } -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/caching/ResumeHandler.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.caching; 2 | 3 | import android.content.Context; 4 | 5 | import info.varden.hauk.struct.Session; 6 | import info.varden.hauk.struct.Share; 7 | 8 | /** 9 | * Handler interface that should be implemented by classes capable of processing and resuming shares 10 | * received by the server on each location update. 11 | * 12 | * @author Marius Lindvall 13 | */ 14 | public interface ResumeHandler { 15 | /** 16 | * Called if there are resumable shares. 17 | * @param ctx Android application context. 18 | * @param session The session stored in the resumption data. 19 | * @param shares A list of shares stored in the resumption data. Note - these shares do not 20 | * have an attached Session; it must be attached using setSession(). 21 | */ 22 | void onSharesFetched(Context ctx, Session session, Share[] shares); 23 | } 24 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/caching/ResumePrompt.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.caching; 2 | 3 | import android.content.Context; 4 | 5 | import info.varden.hauk.manager.PromptCallback; 6 | import info.varden.hauk.struct.Session; 7 | import info.varden.hauk.struct.Share; 8 | 9 | /** 10 | * Interface to be implemented by classes that can provide a prompt asking the user if they want to 11 | * resume a given session with an associated list of shares. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public interface ResumePrompt { 16 | /** 17 | * Called to prompt the user to resume a given list of shares. The user response should be 18 | * returned by calling accept() or deny() accordingly on the provided prompt callback. 19 | * 20 | * @param ctx Android application context. 21 | * @param session The session that can be resumed. 22 | * @param shares A list of resumable shares. 23 | * @param response The callback that should be used to indicate the user's decision. 24 | */ 25 | void promptForResumption(Context ctx, Session session, Share[] shares, PromptCallback response); 26 | } 27 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/dialog/CustomDialogBuilder.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.dialog; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | /** 9 | * A base class used as a handler to build custom Views and handle events for custom dialogs in the 10 | * DialogService. 11 | * 12 | * @author Marius Lindvall 13 | */ 14 | public interface CustomDialogBuilder { 15 | /** 16 | * Fires when the positive button is clicked in the dialog. 17 | */ 18 | void onPositive(); 19 | 20 | /** 21 | * Fires when the negative button is clicked in the dialog. 22 | */ 23 | void onNegative(); 24 | 25 | /** 26 | * A handler to build a View to display in the dialog box. 27 | * 28 | * @param ctx Android application context. 29 | * @return A View to display in the dialog box. 30 | */ 31 | @Nullable 32 | View createView(Context ctx); 33 | 34 | interface Three extends CustomDialogBuilder { 35 | /** 36 | * Fires when the neutral button is clicked in the dialog. 37 | */ 38 | void onNeutral(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/dialog/StopSharingConfirmationPrompt.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.dialog; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | import info.varden.hauk.Constants; 9 | import info.varden.hauk.manager.SessionManager; 10 | import info.varden.hauk.struct.Share; 11 | import info.varden.hauk.system.preferences.PreferenceManager; 12 | import info.varden.hauk.utils.Log; 13 | 14 | /** 15 | * Prompt that confirms with the user if they really intended to stop sharing their location. 16 | * 17 | * @author Marius Lindvall 18 | */ 19 | public final class StopSharingConfirmationPrompt implements CustomDialogBuilder.Three { 20 | private final PreferenceManager prefs; 21 | private final SessionManager manager; 22 | private final Share share; 23 | 24 | public StopSharingConfirmationPrompt(PreferenceManager prefs, SessionManager manager) { 25 | this(prefs, manager, null); 26 | } 27 | 28 | public StopSharingConfirmationPrompt(PreferenceManager prefs, SessionManager manager, Share share) { 29 | this.prefs = prefs; 30 | this.manager = manager; 31 | this.share = share; 32 | } 33 | 34 | @Override 35 | public void onNeutral() { 36 | Log.i("Disabling future confirmation prompts when stopping shares"); //NON-NLS 37 | this.prefs.set(Constants.PREF_CONFIRM_STOP, false); 38 | if (this.share == null) { 39 | this.manager.stopSharing(); 40 | } else { 41 | this.manager.stopSharing(this.share); 42 | } 43 | } 44 | 45 | @Override 46 | public void onPositive() { 47 | if (this.share == null) { 48 | this.manager.stopSharing(); 49 | } else { 50 | this.manager.stopSharing(this.share); 51 | } 52 | } 53 | 54 | @Override 55 | public void onNegative() { 56 | // Do nothing. 57 | } 58 | 59 | @Nullable 60 | @Override 61 | public View createView(Context ctx) { 62 | return null; 63 | } 64 | } -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/BroadcastSessionManager.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import info.varden.hauk.R; 7 | import info.varden.hauk.manager.SessionManager; 8 | import info.varden.hauk.manager.StopSharingCallback; 9 | 10 | /** 11 | * Session manager implementation for sessions created via the broadcast receiver. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class BroadcastSessionManager extends SessionManager { 16 | /** 17 | * Android application context. 18 | */ 19 | private final Context ctx; 20 | 21 | BroadcastSessionManager(Context ctx) { 22 | super(ctx, new StopSharingCallbackImpl(ctx)); 23 | this.ctx = ctx; 24 | } 25 | 26 | @Override 27 | protected void requestLocationPermission() { 28 | Toast.makeText(this.ctx, R.string.err_missing_perms, Toast.LENGTH_LONG).show(); 29 | } 30 | 31 | /** 32 | * Implementation of {@link StopSharingCallback} for the broadcast receiver session manager. 33 | * Displays toast notifications when sharing stops. 34 | */ 35 | private static final class StopSharingCallbackImpl implements StopSharingCallback { 36 | private final Context ctx; 37 | 38 | private StopSharingCallbackImpl(Context ctx) { 39 | this.ctx = ctx; 40 | } 41 | 42 | @Override 43 | public void onSuccess() { 44 | Toast.makeText(this.ctx, R.string.ended_message, Toast.LENGTH_LONG).show(); 45 | } 46 | 47 | @Override 48 | public void onShareNull() { 49 | } 50 | 51 | @Override 52 | public void onFailure(Exception ex) { 53 | Toast.makeText(this.ctx, ex.getMessage(), Toast.LENGTH_LONG).show(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/ui/AuthorizationActivity.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global.ui; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import info.varden.hauk.Constants; 12 | import info.varden.hauk.R; 13 | 14 | /** 15 | * Activity that is displayed to prompt the user to authorize a broadcast receiver source. 16 | * 17 | * @author Marius Lindvall 18 | */ 19 | public final class AuthorizationActivity extends AppCompatActivity { 20 | /** 21 | * A string passed along with the broadcast intent to identify the source of the broadcast. 22 | */ 23 | private String identifier; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_authorize_broadcast); 29 | this.identifier = getIntent().getStringExtra(Constants.EXTRA_BROADCAST_AUTHORIZATION_IDENTIFIER); 30 | ((TextView) findViewById(R.id.authorizeIdentifier)).setText(this.identifier); 31 | } 32 | 33 | /** 34 | * Called if the user presses the Yes button. 35 | */ 36 | public void accept(@SuppressWarnings("unused") View view) { 37 | savePreference(true); 38 | } 39 | 40 | /** 41 | * Called if the user presses the No button. 42 | */ 43 | public void deny(@SuppressWarnings("unused") View view) { 44 | savePreference(false); 45 | } 46 | 47 | /** 48 | * Saves the user's preference on whether or not the given source should be approved and closes 49 | * the dialog. 50 | * 51 | * @param preference Whether or not the source was authorized. 52 | */ 53 | private void savePreference(boolean preference) { 54 | SharedPreferences prefs = getSharedPreferences(Constants.SHARED_PREFS_AUTHORIZATIONS, Context.MODE_PRIVATE); 55 | SharedPreferences.Editor editor = prefs.edit(); 56 | editor.putBoolean(this.identifier, preference); 57 | editor.apply(); 58 | finish(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/ui/DisplayShareDialogListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global.ui; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.widget.Toast; 6 | 7 | import info.varden.hauk.Constants; 8 | import info.varden.hauk.R; 9 | import info.varden.hauk.manager.ShareListener; 10 | import info.varden.hauk.struct.Share; 11 | 12 | /** 13 | * Share listener that opens a sharing dialog for the first sharing link received from the server. 14 | * 15 | * @author Marius Lindvall 16 | */ 17 | public final class DisplayShareDialogListener implements ShareListener { 18 | /** 19 | * Android application context. 20 | */ 21 | private final Context ctx; 22 | 23 | /** 24 | * Whether or not the sharing dialog should be displayed. 25 | */ 26 | private boolean enabled = true; 27 | 28 | public DisplayShareDialogListener(Context ctx) { 29 | this.ctx = ctx; 30 | } 31 | 32 | @Override 33 | public void onShareJoined(Share share) { 34 | // Ensure that the sharing dialog is only displayed for the first link. The user could 35 | // otherwise end up having another sharing dialog displayed if the share is adopted and a 36 | // new sharing link is added that way. 37 | if (this.enabled) { 38 | this.enabled = false; 39 | 40 | Intent shareIntent = new Intent(Intent.ACTION_SEND); 41 | shareIntent.setType(Constants.INTENT_TYPE_COPY_LINK); 42 | shareIntent.putExtra(Intent.EXTRA_SUBJECT, this.ctx.getString(R.string.share_subject)); 43 | shareIntent.putExtra(Intent.EXTRA_TEXT, share.getViewURL()); 44 | 45 | Intent chooserIntent = Intent.createChooser(shareIntent, this.ctx.getString(R.string.share_via)); 46 | chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 47 | this.ctx.startActivity(chooserIntent); 48 | 49 | Toast.makeText(this.ctx, R.string.ok_message, Toast.LENGTH_LONG).show(); 50 | } 51 | } 52 | 53 | @Override 54 | public void onShareParted(Share share) { 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/ui/toast/GNSSStatusUpdateListenerImpl.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global.ui.toast; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import info.varden.hauk.R; 7 | import info.varden.hauk.manager.GNSSStatusUpdateListener; 8 | 9 | /** 10 | * GNSS status update listener that displays status updates in toast notifications. Used primarily 11 | * for starting shares via the broadcast receiver. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class GNSSStatusUpdateListenerImpl implements GNSSStatusUpdateListener { 16 | /** 17 | * Android application context. 18 | */ 19 | private final Context ctx; 20 | 21 | public GNSSStatusUpdateListenerImpl(Context ctx) { 22 | this.ctx = ctx; 23 | } 24 | 25 | @Override 26 | public void onShutdown() { 27 | Toast.makeText(this.ctx, R.string.label_status_none, Toast.LENGTH_LONG).show(); 28 | } 29 | 30 | @Override 31 | public void onStarted() { 32 | Toast.makeText(this.ctx, R.string.label_status_wait, Toast.LENGTH_LONG).show(); 33 | } 34 | 35 | @Override 36 | public void onGNSSConnectionLost() { 37 | Toast.makeText(this.ctx, R.string.label_status_lost_gnss, Toast.LENGTH_LONG).show(); 38 | } 39 | 40 | @Override 41 | public void onCoarseLocationReceived() { 42 | Toast.makeText(this.ctx, R.string.label_status_coarse, Toast.LENGTH_LONG).show(); 43 | } 44 | 45 | @Override 46 | public void onAccurateLocationReceived() { 47 | Toast.makeText(this.ctx, R.string.label_status_ok, Toast.LENGTH_LONG).show(); 48 | } 49 | 50 | @Override 51 | public void onServerConnectionLost() { 52 | // Silently ignore 53 | } 54 | 55 | @Override 56 | public void onServerConnectionRestored() { 57 | // Silently ignore 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/ui/toast/SessionInitiationResponseHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global.ui.toast; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import info.varden.hauk.Constants; 7 | import info.varden.hauk.R; 8 | import info.varden.hauk.manager.SessionInitiationResponseHandler; 9 | import info.varden.hauk.struct.ShareMode; 10 | import info.varden.hauk.struct.Version; 11 | 12 | /** 13 | * Session initiation response handler that shows the initiation status in toasts. Used primarily 14 | * for shares created via the broadcast receiver. 15 | */ 16 | public final class SessionInitiationResponseHandlerImpl implements SessionInitiationResponseHandler { 17 | /** 18 | * Android application context. 19 | */ 20 | private final Context ctx; 21 | 22 | public SessionInitiationResponseHandlerImpl(Context ctx) { 23 | this.ctx = ctx; 24 | } 25 | 26 | @Override 27 | public void onInitiating() { 28 | } 29 | 30 | @Override 31 | public void onSuccess() { 32 | } 33 | 34 | @Override 35 | public void onShareModeForciblyDowngraded(ShareMode downgradeTo, Version backendVersion) { 36 | Toast.makeText(this.ctx, String.format(this.ctx.getString(R.string.err_ver_group), Constants.VERSION_COMPAT_GROUP_SHARE, backendVersion), Toast.LENGTH_LONG).show(); 37 | } 38 | 39 | @Override 40 | public void onE2EForciblyDisabled(Version backendVersion) { 41 | Toast.makeText(this.ctx, String.format(this.ctx.getString(R.string.err_ver_e2e), Constants.VERSION_COMPAT_E2E_ENCRYPTION, backendVersion), Toast.LENGTH_LONG).show(); 42 | } 43 | 44 | @Override 45 | public void onFailure(Exception ex) { 46 | Toast.makeText(this.ctx, ex.getMessage(), Toast.LENGTH_LONG).show(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/global/ui/toast/ShareListenerImpl.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.global.ui.toast; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import info.varden.hauk.R; 7 | import info.varden.hauk.manager.ShareListener; 8 | import info.varden.hauk.struct.Share; 9 | 10 | /** 11 | * Share listener that opens a toast with the first sharing link received from the server. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class ShareListenerImpl implements ShareListener { 16 | /** 17 | * Android application context. 18 | */ 19 | private final Context ctx; 20 | 21 | /** 22 | * Whether or not the sharing dialog should be displayed. 23 | */ 24 | private boolean enabled = true; 25 | 26 | public ShareListenerImpl(Context ctx) { 27 | this.ctx = ctx; 28 | } 29 | 30 | @Override 31 | public void onShareJoined(Share share) { 32 | // Ensure that the toast is only displayed for the first link. The user could otherwise end 33 | // up having multiple toasts displayed if the share is adopted and a new sharing link is 34 | // added that way. 35 | if (this.enabled) { 36 | this.enabled = false; 37 | Toast.makeText(this.ctx, String.format(this.ctx.getString(R.string.notify_body), share.getViewURL()), Toast.LENGTH_LONG).show(); 38 | } 39 | } 40 | 41 | @Override 42 | public void onShareParted(Share share) { 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/ConnectionParameters.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import java.io.Serializable; 6 | import java.net.Proxy; 7 | import java.net.SocketAddress; 8 | 9 | import info.varden.hauk.http.security.CertificateValidationPolicy; 10 | 11 | /** 12 | * Structure used to store connection parameters for backend connections, e.g. proxy details. 13 | * 14 | * @author Marius Lindvall 15 | */ 16 | public final class ConnectionParameters implements Serializable { 17 | private static final long serialVersionUID = -6275381322711990147L; 18 | 19 | /** 20 | * The type of proxy to use for the connection. 21 | */ 22 | private final Proxy.Type proxyType; 23 | 24 | /** 25 | * The proxy endpoint address. 26 | */ 27 | private final SocketAddress proxyAddress; 28 | 29 | /** 30 | * The maximum connection timeout, in milliseconds. 31 | */ 32 | private final int connectTimeout; 33 | 34 | /** 35 | * TLS certificate validation policy for the connection. 36 | */ 37 | private final CertificateValidationPolicy tlsPolicy; 38 | 39 | public ConnectionParameters(Proxy.Type proxyType, SocketAddress proxyAddress, int connectTimeout, CertificateValidationPolicy tlsPolicy) { 40 | this.proxyType = proxyType; 41 | this.proxyAddress = proxyAddress; 42 | this.connectTimeout = connectTimeout; 43 | this.tlsPolicy = tlsPolicy; 44 | } 45 | 46 | @Nullable 47 | Proxy getProxy() { 48 | return this.proxyType == null || this.proxyAddress == null ? null : new Proxy(this.proxyType, this.proxyAddress); 49 | } 50 | 51 | int getTimeout() { 52 | return this.connectTimeout; 53 | } 54 | 55 | CertificateValidationPolicy getTLSPolicy() { 56 | return this.tlsPolicy; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return "ConnectionParameters{" 62 | + "proxyType=" + this.proxyType 63 | + ",proxyAddress=" + this.proxyAddress 64 | + ",connectTimeout=" + this.connectTimeout 65 | + ",tlsPolicy=" + this.tlsPolicy 66 | + "}"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/FailureHandler.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http; 2 | 3 | /** 4 | * A base interface for interfaces that can throw a failure state. 5 | * 6 | * @author Marius Lindvall 7 | */ 8 | public interface FailureHandler { 9 | /** 10 | * Called if a failure occurred in the previous request. 11 | * 12 | * @param ex The exception that was thrown from the request. 13 | */ 14 | void onFailure(Exception ex); 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/ServerException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * An exception thrown when there is an error in the response from the server to any packet sent 7 | * from the app. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public class ServerException extends Exception { 12 | private static final long serialVersionUID = 2879124634145201633L; 13 | 14 | /** 15 | * Create the exception with a String message. 16 | * 17 | * @param message The error message. 18 | */ 19 | ServerException(String message) { 20 | super(message); 21 | } 22 | 23 | /** 24 | * Create the exception with a resource reference. 25 | * 26 | * @param ctx Android application context. 27 | * @param message Reference to the error message in strings.xml. 28 | */ 29 | ServerException(Context ctx, @SuppressWarnings("SameParameterValue") int message) { 30 | this(ctx.getString(message)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/parameter/LocationProvider.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http.parameter; 2 | 3 | /** 4 | * An enum that identifies the currently active location provider on the device. 5 | */ 6 | public enum LocationProvider { 7 | FINE(0), 8 | COARSE(1); 9 | 10 | private final int mode; 11 | 12 | LocationProvider(int mode) { 13 | this.mode = mode; 14 | } 15 | 16 | public int getMode() { 17 | return this.mode; 18 | } 19 | 20 | @Override 21 | public String toString() { 22 | return "LocationProvider"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/security/CertificateValidationPolicy.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http.security; 2 | 3 | import info.varden.hauk.system.preferences.IndexedEnum; 4 | 5 | /** 6 | * An enum representing the various types of proxies available on the system, and their ID when 7 | * stored in preferences. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public final class CertificateValidationPolicy extends IndexedEnum { 12 | private static final long serialVersionUID = -1906017485528370776L; 13 | 14 | public static final CertificateValidationPolicy VALIDATE_ALL = new CertificateValidationPolicy(0); 15 | public static final CertificateValidationPolicy DISABLE_TRUST_ANCHOR_ONION = new CertificateValidationPolicy(1); 16 | public static final CertificateValidationPolicy DISABLE_ALL_ONION = new CertificateValidationPolicy(2); 17 | 18 | private CertificateValidationPolicy(int index) { 19 | super(index); 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "CertificateValidationPolicy{" + super.toString() + "}"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/security/InsecureHostnameVerifier.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http.security; 2 | 3 | import javax.net.ssl.HostnameVerifier; 4 | import javax.net.ssl.SSLSession; 5 | 6 | /** 7 | * Intentionally insecure hostname verifier used to ignore invalid hostnames if hostname validation 8 | * is disabled for a domain in preferences. Should be used with caution. 9 | * 10 | * @author Marius Lindvall 11 | */ 12 | public final class InsecureHostnameVerifier implements HostnameVerifier { 13 | @Override 14 | public boolean verify(String s, SSLSession sslSession) { 15 | return true; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/http/security/InsecureTrustManager.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.http.security; 2 | 3 | import java.security.KeyManagementException; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.security.SecureRandom; 6 | import java.security.cert.X509Certificate; 7 | 8 | import javax.net.ssl.SSLContext; 9 | import javax.net.ssl.SSLSocketFactory; 10 | import javax.net.ssl.TrustManager; 11 | import javax.net.ssl.X509TrustManager; 12 | 13 | import info.varden.hauk.utils.Log; 14 | 15 | /** 16 | * Intentionally insecure trust manager that accepts all trust anchors. Should be used with caution. 17 | */ 18 | public final class InsecureTrustManager implements X509TrustManager { 19 | @Override 20 | public void checkClientTrusted(X509Certificate[] x509Certificates, String s) { 21 | Log.v("Client certificate presented for %s", s); //NON-NLS 22 | } 23 | 24 | @Override 25 | public void checkServerTrusted(X509Certificate[] x509Certificates, String s) { 26 | Log.v("Server certificate presented for %s", x509Certificates[0]); //NON-NLS 27 | } 28 | 29 | @Override 30 | public X509Certificate[] getAcceptedIssuers() { 31 | Log.v("Got request for accepted issuers"); //NON-NLS 32 | return null; 33 | } 34 | 35 | public static SSLSocketFactory getSocketFactory() throws NoSuchAlgorithmException, KeyManagementException { 36 | SSLContext context = SSLContext.getInstance("TLS"); //NON-NLS 37 | context.init(null, new TrustManager[] {new InsecureTrustManager()}, new SecureRandom()); 38 | return context.getSocketFactory(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/GNSSStatusUpdateListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | /** 4 | * A listener interface for clients that want to receive state updates from the GNSS service for 5 | * active sessions. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | public interface GNSSStatusUpdateListener { 10 | /** 11 | * Called when the session and its corresponding GNSS listeners are stopped. 12 | */ 13 | void onShutdown(); 14 | 15 | /** 16 | * Called when the session is created and when the GNSS listeners have been spawned and bound. 17 | */ 18 | void onStarted(); 19 | 20 | /** 21 | * Called if the accurate GNSS location listener stops working. This implies that the coarse 22 | * location listener is now back in use and {@link #onCoarseLocationReceived()} may be called 23 | * again. 24 | */ 25 | void onGNSSConnectionLost(); 26 | 27 | /** 28 | *

Called on first reception of low-accuracy location data from the network. Should be 29 | * available almost instantly if the user device has network-based or other non-GNSS location 30 | * sources available and enabled when the session is started.

31 | * 32 | *

Note that this method will not be called if fine location data is received first.

33 | */ 34 | void onCoarseLocationReceived(); 35 | 36 | /** 37 | * Called on first reception of high-accuracy location data from GNSS. This may take a while to 38 | * be available, or may not be available at all if the user is in a location that does not have 39 | * adequate GNSS signal reception. 40 | */ 41 | void onAccurateLocationReceived(); 42 | 43 | /** 44 | * Called if the backend server is unreachable. 45 | */ 46 | void onServerConnectionLost(); 47 | 48 | /** 49 | * Called if the backend server was unreachable, but is now reachable again. 50 | */ 51 | void onServerConnectionRestored(); 52 | } 53 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/PromptCallback.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | /** 4 | * A callback interface that should be passed to all user prompts. The prompt calls either {@code 5 | * accept()} or {@code deny()} depending on the user's response to the prompt. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | public interface PromptCallback { 10 | /** 11 | * Called if the user accepted the prompt. 12 | */ 13 | void accept(); 14 | 15 | /** 16 | * Called if the user denied the prompt. 17 | */ 18 | void deny(); 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/ServiceRelauncher.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import android.content.Context; 4 | 5 | import info.varden.hauk.caching.ResumableSessions; 6 | import info.varden.hauk.caching.ResumeHandler; 7 | import info.varden.hauk.struct.Session; 8 | import info.varden.hauk.struct.Share; 9 | import info.varden.hauk.utils.Log; 10 | 11 | /** 12 | * {@link ResumeHandler} implementation used by {@link SessionManager} to automatically resume 13 | * sessions following a service relaunch. This can happen if the main activity is terminated, but 14 | * the share itself keeps running in the background. 15 | * 16 | * @author Marius Lindvall 17 | */ 18 | public final class ServiceRelauncher implements ResumeHandler { 19 | /** 20 | * The session manager to call to resume the shares. 21 | */ 22 | private final SessionManager manager; 23 | 24 | /** 25 | * The manager's resumption handler. This is used to clear the resumption data before the shares 26 | * are resumed by the session manager, as the session manager will re-flag the shares as 27 | * resumable when it adds them to its internal share list. 28 | */ 29 | private final ResumableSessions resumptionHandler; 30 | 31 | ServiceRelauncher(SessionManager manager, ResumableSessions resumptionHandler) { 32 | this.manager = manager; 33 | this.resumptionHandler = resumptionHandler; 34 | } 35 | 36 | @Override 37 | public void onSharesFetched(Context ctx, Session session, Share[] shares) { 38 | Log.i("Resuming %s share(s) automatically found for session %s", shares.length, session); //NON-NLS 39 | // The shares provided by ResumableSessions do not have a session attached to them. Attach 40 | // it to the shares so that they can be shown properly by the prompt and so that the updates 41 | // have a backend to be broadcast to when the shares are resumed. 42 | this.resumptionHandler.clearResumableSession(); 43 | for (Share share : shares) { 44 | share.setSession(session); 45 | this.manager.shareLocation(share, SessionInitiationReason.SERVICE_RELAUNCH); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/SessionInitiationReason.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import info.varden.hauk.struct.Session; 4 | import info.varden.hauk.struct.Share; 5 | 6 | /** 7 | * Describes the reason a session was initiated. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public enum SessionInitiationReason { 12 | /** 13 | * The user requested to start a new sharing session. 14 | */ 15 | USER_STARTED, 16 | 17 | /** 18 | * The user requested to resume a previous sharing session. 19 | */ 20 | USER_RESUMED, 21 | 22 | /** 23 | * The sharing session is automatically resumed as a result of a relaunch of the location 24 | * sharing service. 25 | */ 26 | SERVICE_RELAUNCH, 27 | 28 | /** 29 | * The session was created because a share was added to it. This should never be received by 30 | * {@link SessionListener#onSessionCreated(Session, Share, SessionInitiationReason)} under any 31 | * normal circumstances. 32 | */ 33 | SHARE_ADDED 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/SessionInitiationResponseHandler.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import info.varden.hauk.http.FailureHandler; 4 | import info.varden.hauk.struct.ShareMode; 5 | import info.varden.hauk.struct.Version; 6 | 7 | /** 8 | * A response handler interface that must be provided when starting a new session in order to 9 | * receive callbacks when the session initiation packet has been responded to. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public interface SessionInitiationResponseHandler extends FailureHandler { 14 | /** 15 | * Called when the session initiation packet is being sent. 16 | */ 17 | void onInitiating(); 18 | 19 | /** 20 | * Called if the session was successfully initiated. 21 | */ 22 | void onSuccess(); 23 | 24 | /** 25 | * Called if the session was successfully initiated, but the backend is not compatible with the 26 | * share mode that was requested by the user. {@code onSuccess()} will still be called after 27 | * this callback in the event that this happens. 28 | * 29 | * @param downgradeTo The sharing mode that was picked instead of the requested mode. 30 | * @param backendVersion The version of the Hauk backend server. 31 | */ 32 | void onShareModeForciblyDowngraded(ShareMode downgradeTo, Version backendVersion); 33 | 34 | /** 35 | * Called if the session was successfully initiated, but the backend does not support end-to-end 36 | * encryption. {@code onSuccess()} will still be called after this callback in the event that 37 | * this happens. 38 | * 39 | * @param backendVersion The version of the Hauk backend server. 40 | */ 41 | void onE2EForciblyDisabled(Version backendVersion); 42 | } 43 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/SessionListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import info.varden.hauk.struct.Session; 4 | import info.varden.hauk.struct.Share; 5 | 6 | /** 7 | * Callback interface that {@link SessionManager} handlers can attach to receive status updates 8 | * about session creation. 9 | * 10 | * @author Marius Lindvall 11 | */ 12 | public interface SessionListener { 13 | /** 14 | * Called whenever a new session is created. 15 | * 16 | * @param session The session that was created. 17 | * @param share The share that the session was created for. 18 | * @param reason The reason the session was created. 19 | */ 20 | void onSessionCreated(Session session, Share share, SessionInitiationReason reason); 21 | 22 | /** 23 | * Called if the session could not be initiated due to missing location permissions. 24 | */ 25 | void onSessionCreationFailedDueToPermissions(); 26 | } 27 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/ShareListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import info.varden.hauk.struct.Share; 4 | 5 | /** 6 | * Callback interface that {@link SessionManager} handlers can attach to receive status updates 7 | * about share attachment. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public interface ShareListener { 12 | /** 13 | * Called when a share has been joined on the server or locally. 14 | * 15 | * @param share The share that was joined. 16 | */ 17 | void onShareJoined(Share share); 18 | 19 | /** 20 | * Called when a share has been left. 21 | * 22 | * @param share The share that was left. 23 | */ 24 | void onShareParted(Share share); 25 | } 26 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/manager/StopSharingCallback.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.manager; 2 | 3 | import info.varden.hauk.http.FailureHandler; 4 | 5 | /** 6 | * Callback interface that is called upon when the user requests to stop the share. 7 | * 8 | * @author Marius Lindvall 9 | */ 10 | public interface StopSharingCallback extends FailureHandler { 11 | /** 12 | * Called if the share was successfully stopped. 13 | */ 14 | void onSuccess(); 15 | 16 | /** 17 | * Called if the share could not be stopped because the share or session did not exist. 18 | */ 19 | void onShareNull(); 20 | } 21 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/notify/CopyLinkReceiver.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.notify; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | 7 | import info.varden.hauk.R; 8 | import info.varden.hauk.utils.Log; 9 | 10 | /** 11 | * A broadcast receiver for the "Copy link" action on the persistent Hauk notification. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class CopyLinkReceiver extends HaukBroadcastReceiver { 16 | 17 | @SuppressWarnings("HardCodedStringLiteral") 18 | private static final String ACTION_ID = "info.varden.hauk.COPY_LINK"; 19 | 20 | public CopyLinkReceiver() { 21 | super(ACTION_ID); 22 | } 23 | 24 | @Override 25 | public void handle(Context ctx, String data) { 26 | // Copy the link to the clipboard. 27 | ClipData clip = ClipData.newPlainText(ctx.getString(R.string.action_copied), data); 28 | ClipboardManager clipMan = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE); 29 | if (clipMan != null) { 30 | Log.i("Copying %s to clipboard", data); //NON-NLS 31 | clipMan.setPrimaryClip(clip); 32 | } else { 33 | Log.e("Could not copy %s to clipboard because the clipboard manager is null", data); //NON-NLS 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/notify/HaukBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.notify; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import info.varden.hauk.Constants; 8 | import info.varden.hauk.utils.Log; 9 | import info.varden.hauk.utils.ReceiverDataRegistry; 10 | 11 | /** 12 | * A superclass for broadcast receivers, used together with the Receiver class to handle callbacks 13 | * from users clicking buttons in Hauk notifications. Passes the data object from Receiver to the 14 | * subclass of this class for processing. 15 | * 16 | * @author Marius Lindvall 17 | * @param The type of data this broadcast receiver is capable of processing. 18 | */ 19 | public abstract class HaukBroadcastReceiver extends BroadcastReceiver { 20 | private final String actionID; 21 | 22 | /** 23 | * Creates the receiver. 24 | * 25 | * @param actionID A unique broadcast action ID that this receiver should handle. 26 | */ 27 | protected HaukBroadcastReceiver(String actionID) { 28 | this.actionID = actionID; 29 | } 30 | 31 | /** 32 | * Callback for handling the broadcast data. 33 | * 34 | * @param ctx Android application context. 35 | * @param data The data object provided to the Receiver class. 36 | */ 37 | protected abstract void handle(Context ctx, T data); 38 | 39 | public final String getActionID() { 40 | return this.actionID; 41 | } 42 | 43 | @Override 44 | public final void onReceive(Context context, Intent intent) { 45 | // Retrieve the registry index of the data stored for this receiver, then pass that data on 46 | // to the subclass. 47 | int index = intent.getIntExtra(Constants.EXTRA_BROADCAST_RECEIVER_REGISTRY_INDEX, -1); 48 | //noinspection unchecked 49 | T data = (T) ReceiverDataRegistry.retrieve(index, true); 50 | Log.v("Received broadcast for class %s; fetched stored data of type %s; calling handler", getClass().getName(), data.getClass().getName()); //NON-NLS 51 | handle(context, data); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/notify/ReopenIntent.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.notify; 2 | 3 | import android.app.Activity; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | 8 | /** 9 | * A intent for tapping the persistent Hauk notification to return to the app. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | final class ReopenIntent { 14 | private final Context ctx; 15 | private final Class activity; 16 | 17 | /** 18 | * Creates an intent that can be used to return to the app when the notification is tapped. 19 | * 20 | * @param ctx Android application context. 21 | * @param activity The class of the activity to return to. 22 | */ 23 | ReopenIntent(Context ctx, @SuppressWarnings("SameParameterValue") Class activity) { 24 | this.ctx = ctx; 25 | this.activity = activity; 26 | } 27 | 28 | /** 29 | * Creates an intent for the reopen task and converts it to a PendingIntent for use in 30 | * notification builders. 31 | */ 32 | PendingIntent toPending() { 33 | Intent intent = new Intent(this.ctx, this.activity); 34 | intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 35 | return PendingIntent.getActivity(this.ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/notify/StopSharingReceiver.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.notify; 2 | 3 | import android.content.Context; 4 | 5 | import info.varden.hauk.manager.StopSharingTask; 6 | import info.varden.hauk.utils.Log; 7 | 8 | /** 9 | * A broadcast receiver for the "Stop sharing" button on the persistent Hauk notification. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class StopSharingReceiver extends HaukBroadcastReceiver { 14 | 15 | @SuppressWarnings("HardCodedStringLiteral") 16 | private static final String ACTION_ID = "info.varden.hauk.STOP_SHARING"; 17 | 18 | public StopSharingReceiver() { 19 | super(ACTION_ID); 20 | } 21 | 22 | @Override 23 | public void handle(Context ctx, StopSharingTask data) { 24 | // Run the stop sharing task to end location sharing. 25 | Log.i("User requested to stop sharing via notification (broadcast)"); //NON-NLS 26 | data.run(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/service/GNSSActiveHandler.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.service; 2 | 3 | /** 4 | * Interface template for handling UI updates when location data is received. 5 | * 6 | * @author Marius Lindvall 7 | */ 8 | public interface GNSSActiveHandler { 9 | /** 10 | * Called when the fine location provider times out and the coarse location provider is rebound. 11 | */ 12 | void onCoarseRebound(); 13 | 14 | /** 15 | * Called when the initial low-accuracy GNSS fix has been obtained. 16 | */ 17 | void onCoarseLocationReceived(); 18 | 19 | /** 20 | * Called when the initial high-accuracy GNSS fix has been obtained. 21 | */ 22 | void onAccurateLocationReceived(); 23 | 24 | /** 25 | * Called if the backend server is unreachable. 26 | */ 27 | void onServerConnectionLost(); 28 | 29 | /** 30 | * Called if the backend server was unreachable, but is now reachable again. 31 | */ 32 | void onServerConnectionRestored(); 33 | 34 | /** 35 | * Called when a list of shares the client is contributing to has been received from the server. 36 | * 37 | * @param linkFormat A format-able string used as a template to get view links. 38 | * @param shareIDs A list of shares the client is contributing to. May be null. 39 | */ 40 | void onShareListReceived(String linkFormat, String[] shareIDs); 41 | } 42 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/service/LocationListenerBase.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.service; 2 | 3 | import android.location.LocationListener; 4 | import android.location.LocationManager; 5 | import android.os.Bundle; 6 | 7 | import info.varden.hauk.utils.Log; 8 | 9 | /** 10 | * Location listener base class for Hauk. The purpose of this class is to remove unnecessary empty 11 | * function bodies from LocationPushService's source code. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | abstract class LocationListenerBase implements LocationListener { 16 | @Override 17 | public final void onStatusChanged(String provider, int status, Bundle bundle) { 18 | Log.v("Location status changed for provider %s, status=%s", provider, status); //NON-NLS 19 | } 20 | 21 | @Override 22 | public final void onProviderEnabled(String provider) { 23 | Log.i("Location provider %s was enabled", provider); //NON-NLS 24 | } 25 | 26 | @Override 27 | public final void onProviderDisabled(String provider) { 28 | Log.w("Location provider %s was disabled", provider); //NON-NLS 29 | } 30 | 31 | /** 32 | * Request location updates from the given location manager. 33 | * 34 | * @param manager The location manager to request location updates from. 35 | * @return true if successful, false otherwise. 36 | * @throws SecurityException if location permission has not been granted. 37 | */ 38 | abstract boolean request(LocationManager manager) throws SecurityException; 39 | } 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/service/MultiTargetGNSSHandlerProxy.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.service; 2 | 3 | /** 4 | * Proxy class that forwards GNSS activity events to multiple upstream {@link GNSSActiveHandler}s. 5 | * 6 | * @author Marius Lindvall 7 | */ 8 | final class MultiTargetGNSSHandlerProxy implements GNSSActiveHandler { 9 | private final GNSSActiveHandler[] upstream; 10 | 11 | MultiTargetGNSSHandlerProxy(GNSSActiveHandler... upstream) { 12 | this.upstream = upstream.clone(); 13 | } 14 | 15 | @Override 16 | public void onCoarseRebound() { 17 | for (GNSSActiveHandler up : this.upstream) up.onCoarseRebound(); 18 | } 19 | 20 | @Override 21 | public void onCoarseLocationReceived() { 22 | for (GNSSActiveHandler up : this.upstream) up.onCoarseLocationReceived(); 23 | } 24 | 25 | @Override 26 | public void onAccurateLocationReceived() { 27 | for (GNSSActiveHandler up : this.upstream) up.onAccurateLocationReceived(); 28 | } 29 | 30 | @Override 31 | public void onServerConnectionLost() { 32 | for (GNSSActiveHandler up : this.upstream) up.onServerConnectionLost(); 33 | } 34 | 35 | @Override 36 | public void onServerConnectionRestored() { 37 | for (GNSSActiveHandler up : this.upstream) up.onServerConnectionRestored(); 38 | } 39 | 40 | @Override 41 | public void onShareListReceived(String linkFormat, String[] shareIDs) { 42 | for (GNSSActiveHandler up : this.upstream) up.onShareListReceived(linkFormat, shareIDs); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/struct/AdoptabilityPreference.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.struct; 2 | 3 | /** 4 | * An enum used to flag whether or not a solo share should be adoptable by others. 5 | */ 6 | public enum AdoptabilityPreference { 7 | ALLOW_ADOPTION, 8 | DISALLOW_ADOPTION 9 | } 10 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/struct/ShareMode.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.struct; 2 | 3 | import java.io.Serializable; 4 | 5 | import info.varden.hauk.R; 6 | 7 | /** 8 | * An enum describing various ways a share may be created when starting a sharing session. 9 | * 10 | * @author Marius Lindvall 11 | */ 12 | public enum ShareMode implements Serializable { 13 | 14 | CREATE_ALONE(0, R.string.link_type_solo), 15 | CREATE_GROUP(1, R.string.link_type_group_host), 16 | JOIN_GROUP(2, R.string.link_type_group_member); 17 | 18 | /** 19 | * Resolves a sharing mode by its index. 20 | * 21 | * @param index The index of the sharing mode. 22 | * @return A sharing mode enum member. 23 | * @throws EnumConstantNotPresentException if there is no matching mode for the given index. 24 | */ 25 | public static ShareMode fromMode(int index) throws EnumConstantNotPresentException { 26 | for (ShareMode mode : ShareMode.values()) { 27 | if (mode.getIndex() == index) return mode; 28 | } 29 | throw new EnumConstantNotPresentException(ShareMode.class, "index=" + index); 30 | } 31 | 32 | /** 33 | * The index of the sharing mode. Used as the mode identifier when starting a session on the 34 | * server. Also used as the index of the sharing mode in the share mode dropdown in the UI, 35 | * hence the name of this field. 36 | */ 37 | private final int index; 38 | 39 | /** 40 | * A string resource ID for displaying the type of share in the list of active sharing links. 41 | */ 42 | @SuppressWarnings("FieldNotUsedInToString") 43 | private final int descriptorResource; 44 | 45 | ShareMode(int index, int descriptorResource) { 46 | this.index = index; 47 | this.descriptorResource = descriptorResource; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "ShareMode{index=" + this.index + "}"; 53 | } 54 | 55 | public int getIndex() { 56 | return this.index; 57 | } 58 | 59 | /** 60 | * Returns whether or not this sharing mode is a group share variant. 61 | */ 62 | public boolean isGroupType() { 63 | return this == CREATE_GROUP || this == JOIN_GROUP; 64 | } 65 | 66 | public int getDescriptorResource() { 67 | return this.descriptorResource; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/struct/Version.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.struct; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * A class for comparing version strings. 9 | * 10 | * @author Marius Lindvall 11 | */ 12 | public final class Version implements Comparable, Serializable { 13 | private static final long serialVersionUID = 3855294306995801704L; 14 | 15 | /** 16 | * The version number in question. 17 | */ 18 | private final String ver; 19 | 20 | public Version(String ver) { 21 | // Version defaults to 1.0 if null, as 1.0.x backends did not return an X-Hauk-Version 22 | // header. 23 | this.ver = ver != null ? ver : "1.0"; 24 | } 25 | 26 | /** 27 | * Checks whether or not this version number is equal to or greater than the version number 28 | * passed as the argument to this function. 29 | * 30 | * @param other The version to compare to. 31 | */ 32 | public boolean isAtLeast(Version other) { 33 | return this.compareTo(other) >= 0; 34 | } 35 | 36 | @Override 37 | public int compareTo(Version other) { 38 | String[] thisParts = this.ver.split("\\."); 39 | String[] otherParts = other.toString().split("\\."); 40 | 41 | // Compare each segment of the version to determine if the version is newer, older or the 42 | // same as the current version. 43 | int maxLen = Math.max(thisParts.length, otherParts.length); 44 | for (int i = 0; i < maxLen; i++) { 45 | int thisPart = i < thisParts.length ? Integer.parseInt(thisParts[i]) : 0; 46 | int otherPart = i < otherParts.length ? Integer.parseInt(otherParts[i]) : 0; 47 | if (thisPart < otherPart) return -1; 48 | if (thisPart > otherPart) return 1; 49 | } 50 | 51 | return 0; 52 | } 53 | 54 | @Override 55 | public boolean equals(Object obj) { 56 | if (!(obj instanceof Version)) return false; 57 | return compareTo((Version) obj) == 0; 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return this.ver.hashCode(); 63 | } 64 | 65 | @NonNull 66 | @Override 67 | public String toString() { 68 | return this.ver; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/LocationPermissionsNotGrantedException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system; 2 | 3 | /** 4 | * An exception that is thrown when a sharing session is started, but location permissions have not 5 | * been granted yet. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | public class LocationPermissionsNotGrantedException extends Exception { 10 | private static final long serialVersionUID = -5852712724878248514L; 11 | 12 | public LocationPermissionsNotGrantedException() { 13 | super("Location permissions have not been granted"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/LocationServicesDisabledException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system; 2 | 3 | /** 4 | * An exception that is thrown if location services are disabled when attempting to start a sharing 5 | * session. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | public class LocationServicesDisabledException extends Exception { 10 | private static final long serialVersionUID = -345262642944634900L; 11 | 12 | public LocationServicesDisabledException() { 13 | super("Location services are disabled"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/launcher/ActionLauncher.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.launcher; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | 6 | /** 7 | * Activity starter that launches an intent by an action name. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public final class ActionLauncher implements Launcher { 12 | /** 13 | * The name of the action to launch. 14 | */ 15 | private final String action; 16 | 17 | public ActionLauncher(String action) { 18 | this.action = action; 19 | } 20 | 21 | @Override 22 | public void launch(Context ctx) { 23 | Intent intent = new Intent(this.action); 24 | ctx.startActivity(intent); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "ActionLauncher{" 30 | + "action=" + this.action 31 | + "}"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/launcher/ComponentLauncher.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.launcher; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | /** 8 | * Activity starter that launches an intent based on a component name. 9 | * 10 | * @author Marius Lindvall 11 | */ 12 | public final class ComponentLauncher implements Launcher { 13 | /** 14 | * The package of the activity class to launch. 15 | */ 16 | private final String packageName; 17 | 18 | /** 19 | * The activity class to launch. 20 | */ 21 | private final String className; 22 | 23 | public ComponentLauncher(String packageName, String className) { 24 | this.packageName = packageName; 25 | this.className = className.startsWith(".") ? packageName + className : className; 26 | } 27 | 28 | @Override 29 | public void launch(Context ctx) { 30 | Intent intent = new Intent(); 31 | intent.setComponent(new ComponentName(this.packageName, this.className)); 32 | ctx.startActivity(intent); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "ComponentLauncher{" 38 | + "packageName=" + this.packageName 39 | + ",className=" + this.className 40 | + "}"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/launcher/Launcher.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.launcher; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Base launcher spec used to start an activity. 7 | * 8 | * @author Marius Lindvall 9 | */ 10 | @SuppressWarnings("ClassNamePrefixedWithPackageName") 11 | public interface Launcher { 12 | /** 13 | * Starts the activity. 14 | * 15 | * @param ctx Android application context. 16 | */ 17 | void launch(Context ctx); 18 | } 19 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/launcher/OpenLinkListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.launcher; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.view.View; 7 | 8 | import androidx.preference.Preference; 9 | 10 | /** 11 | * Listener that opens a URI in the browser on click. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class OpenLinkListener implements Preference.OnPreferenceClickListener, View.OnClickListener { 16 | private final Context ctx; 17 | private final Uri uri; 18 | 19 | /** 20 | * Creates the click listener. 21 | * 22 | * @param ctx Android application context. 23 | * @param uriResource A string resource representing the link to open. 24 | */ 25 | public OpenLinkListener(Context ctx, int uriResource) { 26 | this(ctx, Uri.parse(ctx.getString(uriResource))); 27 | } 28 | 29 | private OpenLinkListener(Context ctx, Uri uri) { 30 | this.ctx = ctx; 31 | this.uri = uri; 32 | } 33 | 34 | @Override 35 | public boolean onPreferenceClick(Preference preference) { 36 | this.ctx.startActivity(new Intent(Intent.ACTION_VIEW, this.uri)); 37 | return false; 38 | } 39 | 40 | @Override 41 | public void onClick(View view) { 42 | this.ctx.startActivity(new Intent(Intent.ACTION_VIEW, this.uri)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/powersaving/WarningDialog.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.powersaving; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.view.View; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import info.varden.hauk.Constants; 10 | import info.varden.hauk.dialog.CustomDialogBuilder; 11 | 12 | /** 13 | * A dialog that prompts the user to open system settings to ignore power savings for Hauk. 14 | * 15 | * @author Marius Lindvall 16 | */ 17 | public final class WarningDialog implements CustomDialogBuilder { 18 | /** 19 | * Android application context. 20 | */ 21 | private final Context ctx; 22 | 23 | /** 24 | * Preference object to save warning acknowledgement state in. 25 | */ 26 | private final SharedPreferences prefs; 27 | 28 | /** 29 | * The power saving device that has been identified. 30 | */ 31 | private final Device device; 32 | 33 | WarningDialog(Context ctx, SharedPreferences prefs, Device device) { 34 | this.ctx = ctx; 35 | this.prefs = prefs; 36 | this.device = device; 37 | } 38 | 39 | /** 40 | * Saves the fact that the user has acknowledged the warning, so they are not warned again 41 | * later. 42 | */ 43 | private void saveAcknowledgement() { 44 | SharedPreferences.Editor editor = this.prefs.edit(); 45 | editor.putInt(Constants.DEVICE_PREF_WARNED_BATTERY_SAVINGS, this.device.getID()); 46 | editor.apply(); 47 | } 48 | 49 | @Override 50 | public void onPositive() { 51 | // Dismiss button clicked. 52 | saveAcknowledgement(); 53 | } 54 | 55 | @Override 56 | public void onNegative() { 57 | // Open settings button clicked. 58 | saveAcknowledgement(); 59 | this.device.launch(this.ctx); 60 | } 61 | 62 | @Nullable 63 | @Override 64 | public View createView(Context ctx) { 65 | return null; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/InvalidPreferenceTypeException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences; 2 | 3 | /** 4 | * An exception that is thrown when attempting to read a setting that is not compatible with the 5 | * given type on the settings screen. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | class InvalidPreferenceTypeException extends RuntimeException { 10 | private static final long serialVersionUID = -1966346602996946755L; 11 | 12 | InvalidPreferenceTypeException(Object value, Class target) { 13 | super(String.format("Cannot direct-cast %s to %s", value.toString(), target.getName())); //NON-NLS 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/PreferenceAssignmentException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences; 2 | 3 | /** 4 | * An exception that is thrown if assignment ot a value to a preference fails due to a downstream 5 | * exception. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | class PreferenceAssignmentException extends RuntimeException { 10 | private static final long serialVersionUID = 8233494694851033874L; 11 | 12 | PreferenceAssignmentException(Exception parent) { 13 | super(parent); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/PreferenceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences; 2 | 3 | /** 4 | * Exception that is thrown when {@link info.varden.hauk.system.preferences.ui.SettingsActivity} 5 | * tries to read a setting that does not exist in Hauk. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | class PreferenceNotFoundException extends RuntimeException { 10 | private static final long serialVersionUID = 6201186189243885309L; 11 | 12 | PreferenceNotFoundException(String key) { 13 | super(String.format("Preference %s was requested but does not exist in Hauk", key)); //NON-NLS 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/indexresolver/NightModeStyle.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.indexresolver; 2 | 3 | import androidx.appcompat.app.AppCompatDelegate; 4 | 5 | /** 6 | * An enum preference that maps night mode styles for the app. 7 | * 8 | * @author Marius Lindvall 9 | */ 10 | @SuppressWarnings("unused") 11 | public final class NightModeStyle extends Resolver { 12 | private static final long serialVersionUID = 1926796368584326815L; 13 | 14 | public static final NightModeStyle FOLLOW_SYSTEM = new NightModeStyle(0, AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); 15 | public static final NightModeStyle AUTO_BATTERY = new NightModeStyle(1, AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); 16 | public static final NightModeStyle ALWAYS_DARK = new NightModeStyle(2, AppCompatDelegate.MODE_NIGHT_YES); 17 | public static final NightModeStyle NEVER_DARK = new NightModeStyle(3, AppCompatDelegate.MODE_NIGHT_NO); 18 | 19 | private NightModeStyle(int index, Integer mapping) { 20 | super(index, mapping); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/indexresolver/ProxyTypeResolver.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.indexresolver; 2 | 3 | import java.net.Proxy; 4 | 5 | /** 6 | * An enum representing the various types of proxies available on the system, and their ID when 7 | * stored in preferences. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | @SuppressWarnings("unused") 12 | public final class ProxyTypeResolver extends Resolver { 13 | private static final long serialVersionUID = -2687503543989317320L; 14 | 15 | public static final ProxyTypeResolver SYSTEM_DEFAULT = new ProxyTypeResolver(0, null); 16 | public static final ProxyTypeResolver DIRECT = new ProxyTypeResolver(1, Proxy.Type.DIRECT); 17 | public static final ProxyTypeResolver HTTP = new ProxyTypeResolver(2, Proxy.Type.HTTP); 18 | public static final ProxyTypeResolver SOCKS = new ProxyTypeResolver(3, Proxy.Type.SOCKS); 19 | 20 | private ProxyTypeResolver(int index, Proxy.Type mapping) { 21 | super(index, mapping); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/indexresolver/Resolver.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.indexresolver; 2 | 3 | import java.io.Serializable; 4 | 5 | import info.varden.hauk.system.preferences.IndexedEnum; 6 | 7 | /** 8 | * A class that provides mappings between an index and a specific type of object for easy 9 | * translation when reading preferences. 10 | * 11 | * @param The type of class implementing this class. 12 | * @param The class that each entry in the parent class should map to. 13 | */ 14 | public abstract class Resolver, T2 extends Serializable> extends IndexedEnum { 15 | private static final long serialVersionUID = 1829235445367254385L; 16 | 17 | private final T2 mapping; 18 | 19 | protected Resolver(int index, T2 mapping) { 20 | super(index); 21 | this.mapping = mapping; 22 | } 23 | 24 | public final T2 resolve() { 25 | return this.mapping; 26 | } 27 | 28 | @Override 29 | public final String toString() { 30 | return getClass().getSimpleName() + "{mapping=" + this.mapping + "," + super.toString() + "}"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/CascadeBindListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import android.widget.EditText; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.preference.EditTextPreference; 7 | 8 | /** 9 | * Edit text bind listener that cascades the bind event to several 10 | * {@link androidx.preference.EditTextPreference.OnBindEditTextListener}s. 11 | * 12 | * @author Marius Lindvall 13 | */ 14 | public final class CascadeBindListener implements EditTextPreference.OnBindEditTextListener { 15 | private final EditTextPreference.OnBindEditTextListener[] listeners; 16 | 17 | public CascadeBindListener(EditTextPreference.OnBindEditTextListener[] listeners) { 18 | this.listeners = listeners.clone(); 19 | } 20 | 21 | @Override 22 | public void onBindEditText(@NonNull EditText editText) { 23 | for (EditTextPreference.OnBindEditTextListener listener : this.listeners) { 24 | listener.onBindEditText(editText); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/CascadeChangeListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import androidx.preference.Preference; 4 | 5 | /** 6 | * Preference change listener that cascades the change event to several 7 | * {@link androidx.preference.Preference.OnPreferenceChangeListener}s. 8 | * 9 | * @author Marius Lindvall 10 | */ 11 | public final class CascadeChangeListener implements Preference.OnPreferenceChangeListener { 12 | private final Preference.OnPreferenceChangeListener[] listeners; 13 | 14 | public CascadeChangeListener(Preference.OnPreferenceChangeListener[] listeners) { 15 | this.listeners = listeners.clone(); 16 | } 17 | 18 | @Override 19 | public boolean onPreferenceChange(Preference preference, Object newValue) { 20 | for (Preference.OnPreferenceChangeListener listener : this.listeners) { 21 | if (!listener.onPreferenceChange(preference, newValue)) return false; 22 | } 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/FloatBoundChangeListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import androidx.preference.Preference; 4 | 5 | import info.varden.hauk.utils.Log; 6 | 7 | /** 8 | * Bounds checking preference change listener that ensures the given value is between two floating 9 | * point values (inclusive). 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class FloatBoundChangeListener implements Preference.OnPreferenceChangeListener { 14 | private final float min; 15 | private final float max; 16 | 17 | public FloatBoundChangeListener(float min, float max) { 18 | this.min = min; 19 | this.max = max; 20 | } 21 | 22 | @Override 23 | public boolean onPreferenceChange(Preference preference, Object newValue) { 24 | try { 25 | float value = Float.parseFloat((String) newValue); 26 | return value >= this.min && value <= this.max; 27 | } catch (NumberFormatException ex) { 28 | Log.e("Number %s is not a valid float", ex, newValue); //NON-NLS 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/HintBindListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import android.widget.EditText; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.preference.EditTextPreference; 7 | 8 | /** 9 | * Edit text bind listener that sets the hint of an {@link EditTextPreference}. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class HintBindListener implements EditTextPreference.OnBindEditTextListener { 14 | private final int hintResource; 15 | 16 | public HintBindListener(int hintResource) { 17 | this.hintResource = hintResource; 18 | } 19 | 20 | @Override 21 | public void onBindEditText(@NonNull EditText editText) { 22 | editText.setHint(this.hintResource); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/InputTypeBindListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import android.widget.EditText; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.preference.EditTextPreference; 7 | 8 | /** 9 | * Edit text bind listener that sets the input type of an {@link EditTextPreference}. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class InputTypeBindListener implements EditTextPreference.OnBindEditTextListener { 14 | private final int inputType; 15 | 16 | public InputTypeBindListener(int inputType) { 17 | this.inputType = inputType; 18 | } 19 | 20 | @Override 21 | public void onBindEditText(@NonNull EditText editText) { 22 | editText.setInputType(this.inputType); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/IntegerBoundChangeListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import androidx.preference.Preference; 4 | 5 | import info.varden.hauk.utils.Log; 6 | 7 | /** 8 | * Bounds checking preference change listener that ensures the given value is between two integer 9 | * values (inclusive). 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class IntegerBoundChangeListener implements Preference.OnPreferenceChangeListener { 14 | private final int min; 15 | private final int max; 16 | 17 | public IntegerBoundChangeListener(int min, int max) { 18 | this.min = min; 19 | this.max = max; 20 | } 21 | 22 | @Override 23 | public boolean onPreferenceChange(Preference preference, Object newValue) { 24 | try { 25 | int value = Integer.parseInt((String) newValue); 26 | return value >= this.min && value <= this.max; 27 | } catch (NumberFormatException ex) { 28 | Log.e("Number %s is not a valid integer", ex, newValue); //NON-NLS 29 | } 30 | return false; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/NightModeChangeListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import androidx.appcompat.app.AppCompatDelegate; 4 | import androidx.preference.Preference; 5 | 6 | import info.varden.hauk.system.preferences.IndexedEnum; 7 | import info.varden.hauk.system.preferences.indexresolver.NightModeStyle; 8 | import info.varden.hauk.utils.Log; 9 | 10 | /** 11 | * Value change listener for the night mode preference that sets the new night mode style on 12 | * selection. 13 | * 14 | * @author Marius Lindvall 15 | */ 16 | public final class NightModeChangeListener implements Preference.OnPreferenceChangeListener { 17 | 18 | @Override 19 | public boolean onPreferenceChange(Preference preference, Object newValue) { 20 | try { 21 | // Resolve the night mode (an instance of NightModeStyle is required for this). 22 | int mode = IndexedEnum.fromIndex(NightModeStyle.class, Integer.valueOf((String) newValue)).resolve(); 23 | Log.i("Setting night mode %s", mode); //NON-NLS 24 | AppCompatDelegate.setDefaultNightMode(mode); 25 | return true; 26 | } catch (Exception e) { 27 | Log.e("Could not determine night mode style for value %s", e, newValue); //NON-NLS 28 | return false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/preferences/ui/listener/ProxyPreferenceChangeListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.preferences.ui.listener; 2 | 3 | import androidx.preference.Preference; 4 | 5 | import info.varden.hauk.system.preferences.indexresolver.ProxyTypeResolver; 6 | 7 | /** 8 | * Value change listener for the proxy type selection preference that disables the other proxy 9 | * settings if a selection is made to the type that makes the other proxy settings unnecessary. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class ProxyPreferenceChangeListener implements Preference.OnPreferenceChangeListener { 14 | private final Preference[] prefsToDisable; 15 | 16 | public ProxyPreferenceChangeListener(Preference[] prefsToDisable) { 17 | this.prefsToDisable = prefsToDisable.clone(); 18 | } 19 | 20 | @Override 21 | public boolean onPreferenceChange(Preference preference, Object newValue) { 22 | int choice = Integer.valueOf((String) newValue); 23 | boolean enable = choice != ProxyTypeResolver.SYSTEM_DEFAULT.getIndex() && choice != ProxyTypeResolver.DIRECT.getIndex(); 24 | for (Preference pref : this.prefsToDisable) { 25 | pref.setEnabled(enable); 26 | } 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/security/EncryptedData.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.security; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Structure that contains encrypted data along with an initialization vector. 7 | * 8 | * @author Marius Lindvall 9 | */ 10 | public final class EncryptedData implements Serializable { 11 | private static final long serialVersionUID = -6247689274316948477L; 12 | 13 | private final byte[] iv; 14 | private final byte[] data; 15 | 16 | /** 17 | * Creates an encrypted data instance. 18 | * 19 | * @param iv An encryption initialization vector. 20 | * @param data Encrypted binary data. 21 | */ 22 | EncryptedData(byte[] iv, byte[] data) { 23 | this.iv = iv; 24 | this.data = data; 25 | } 26 | 27 | byte[] getIV() { 28 | return this.iv.clone(); 29 | } 30 | 31 | byte[] getMessage() { 32 | return this.data.clone(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/security/EncryptionException.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.security; 2 | 3 | /** 4 | * A wrapper exception that is thrown if errors happen during encryption or decryption in 5 | * {@link KeyStoreHelper}. 6 | * 7 | * @author Marius Lindvall 8 | */ 9 | public final class EncryptionException extends Exception { 10 | private static final long serialVersionUID = 1413652344744489876L; 11 | 12 | EncryptionException(Exception ex) { 13 | super(ex); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/system/security/KeyStoreAlias.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.system.security; 2 | 3 | /** 4 | * A list of pre-defined encryption aliases for use in encrypting data with {@link KeyStoreHelper}. 5 | * 6 | * @author Marius Lindvall 7 | */ 8 | public enum KeyStoreAlias { 9 | /** 10 | * Key store alias for use in encrypting and decrypting shared preferences. 11 | */ 12 | @SuppressWarnings("HardCodedStringLiteral") 13 | PREFERENCES("sharedPrefs"); 14 | 15 | /** 16 | * The alias of the key in the key store. 17 | */ 18 | private final String alias; 19 | 20 | KeyStoreAlias(String alias) { 21 | this.alias = alias; 22 | } 23 | 24 | String getAlias() { 25 | return this.alias; 26 | } 27 | 28 | @SuppressWarnings("NullableProblems") 29 | @Override 30 | public String toString() { 31 | return this.alias; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/ui/RepeatingUIThreadTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.ui; 2 | 3 | import android.os.Handler; 4 | 5 | import androidx.annotation.UiThread; 6 | 7 | import java.util.Timer; 8 | import java.util.TimerTask; 9 | 10 | /** 11 | * UI timer class that repeatedly runs a given task on the UI thread. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | abstract class RepeatingUIThreadTaskExecutor { 16 | /** 17 | * Android handler for task posting. 18 | */ 19 | private final Handler handler; 20 | 21 | /** 22 | * Timer that repeatedly posts to the handler. 23 | */ 24 | private Timer timer = null; 25 | 26 | /** 27 | * Called from the UI thread on every tick of the timer. 28 | */ 29 | protected abstract void onTick(); 30 | 31 | @UiThread 32 | RepeatingUIThreadTaskExecutor() { 33 | this.handler = new Handler(); 34 | } 35 | 36 | /** 37 | * Starts the timer with the given delay and interval. 38 | * 39 | * @param delay The delay before first run, in milliseconds. 40 | * @param interval The interval between each tick, in milliseconds. 41 | */ 42 | @SuppressWarnings("SameParameterValue") // to ensure future extensibility 43 | final void start(long delay, long interval) { 44 | this.timer = new Timer(); 45 | this.timer.scheduleAtFixedRate(new RepeatingTask(), delay, interval); 46 | } 47 | 48 | /** 49 | * Stops the timer and prevents it from ticking further. 50 | */ 51 | final void stop() { 52 | if (this.timer != null) { 53 | this.timer.cancel(); 54 | this.timer.purge(); 55 | this.timer = null; 56 | } 57 | } 58 | 59 | /** 60 | * The timer task that is executed by the timer. 61 | */ 62 | private final class RepeatingTask extends TimerTask { 63 | @Override 64 | public void run() { 65 | RepeatingUIThreadTaskExecutor.this.handler.post(new Task()); 66 | } 67 | } 68 | 69 | /** 70 | * The runnable that is executed by the {@link Handler}. Calls the tick function. 71 | */ 72 | private final class Task implements Runnable { 73 | @Override 74 | public void run() { 75 | onTick(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/ui/TextViewCountdownRunner.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.ui; 2 | 3 | import android.widget.TextView; 4 | 5 | import androidx.annotation.UiThread; 6 | 7 | import info.varden.hauk.utils.TimeUtils; 8 | 9 | /** 10 | * Utility that displays a countdown in a {@link TextView}. Used to display a countdown on the stop 11 | * button on the UI when a share is running. 12 | * 13 | * @author Marius Lindvall 14 | */ 15 | public final class TextViewCountdownRunner extends RepeatingUIThreadTaskExecutor { 16 | /** 17 | * The view whose text to update on every tick. 18 | */ 19 | private final TextView view; 20 | 21 | /** 22 | * A string format template that represents the text to be put in the text view. 23 | */ 24 | private final String formatString; 25 | 26 | /** 27 | * Remaining seconds counter. 28 | */ 29 | private long counter; 30 | 31 | @UiThread 32 | public TextViewCountdownRunner(TextView view, String formatString) { 33 | this.view = view; 34 | this.formatString = formatString; 35 | } 36 | 37 | /** 38 | * Starts the countdown. 39 | * 40 | * @param duration The number of seconds to count down for. 41 | */ 42 | public void start(long duration) { 43 | this.counter = duration; 44 | super.start(0L, TimeUtils.MILLIS_PER_SECOND); 45 | } 46 | 47 | @Override 48 | public void onTick() { 49 | if (this.counter >= 0) { 50 | this.view.setText(String.format(this.formatString, TimeUtils.secondsToTime(this.counter))); 51 | } 52 | this.counter--; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/ui/listener/ShareLinkClickListener.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.ui.listener; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.view.View; 6 | 7 | import info.varden.hauk.Constants; 8 | import info.varden.hauk.R; 9 | import info.varden.hauk.struct.Share; 10 | import info.varden.hauk.utils.Log; 11 | 12 | /** 13 | * On-click listener for the button that opens a share menu for a link in the list of active links 14 | * on the UI, spawned using ShareLinkLayoutManager. 15 | * 16 | * @see info.varden.hauk.ui.MainActivity 17 | * @author Marius Lindvall 18 | */ 19 | public final class ShareLinkClickListener implements View.OnClickListener { 20 | /** 21 | * Android application context. 22 | */ 23 | private final Context ctx; 24 | 25 | /** 26 | * The share to share the link for. 27 | */ 28 | private final Share share; 29 | 30 | public ShareLinkClickListener(Context ctx, Share share) { 31 | this.ctx = ctx; 32 | this.share = share; 33 | } 34 | 35 | @Override 36 | public void onClick(View view) { 37 | Log.i("User requested to share %s", this.share); //NON-NLS 38 | Intent shareIntent = new Intent(Intent.ACTION_SEND); 39 | shareIntent.setType(Constants.INTENT_TYPE_COPY_LINK); 40 | shareIntent.putExtra(Intent.EXTRA_SUBJECT, this.ctx.getString(R.string.share_subject)); 41 | shareIntent.putExtra(Intent.EXTRA_TEXT, this.share.getViewURL()); 42 | this.ctx.startActivity(Intent.createChooser(shareIntent, this.ctx.getString(R.string.share_via))); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/utils/DeprecationMigrator.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | import android.content.Context; 4 | 5 | import info.varden.hauk.Constants; 6 | import info.varden.hauk.system.preferences.PreferenceManager; 7 | 8 | /** 9 | * Helper utility to migrate old, deprecated settings saved in shared preferences to modern storage. 10 | * 11 | * @author Marius Lindvall 12 | */ 13 | public final class DeprecationMigrator { 14 | /** 15 | * Android application context. 16 | */ 17 | private final Context ctx; 18 | 19 | public DeprecationMigrator(Context ctx) { 20 | this.ctx = ctx; 21 | } 22 | 23 | /** 24 | * Checks if there are un-migrated settings, and migrates these. 25 | */ 26 | public void migrate() { 27 | PreferenceManager prefs = new PreferenceManager(this.ctx); 28 | if (prefs.has(Constants.PREF_SERVER)) { 29 | Log.i("Encrypting previously stored server"); //NON-NLS 30 | String server = prefs.get(Constants.PREF_SERVER); 31 | prefs.set(Constants.PREF_SERVER_ENCRYPTED, server); 32 | prefs.clear(Constants.PREF_SERVER); 33 | } 34 | if (prefs.has(Constants.PREF_USERNAME)) { 35 | Log.i("Encrypting previously stored username"); //NON-NLS 36 | String user = prefs.get(Constants.PREF_USERNAME); 37 | prefs.set(Constants.PREF_USERNAME_ENCRYPTED, user); 38 | prefs.clear(Constants.PREF_USERNAME); 39 | } 40 | if (prefs.has(Constants.PREF_PASSWORD)) { 41 | Log.i("Encrypting previously stored password"); //NON-NLS 42 | String pass = prefs.get(Constants.PREF_PASSWORD); 43 | prefs.set(Constants.PREF_PASSWORD_ENCRYPTED, pass); 44 | prefs.clear(Constants.PREF_PASSWORD); 45 | } 46 | if (!prefs.has(Constants.PREF_ENABLE_E2E)) { 47 | boolean enableE2E = !prefs.get(Constants.PREF_E2E_PASSWORD).isEmpty(); 48 | Log.i("Setting E2E enabled preference to %s based on stored preferences", String.valueOf(enableE2E)); //NON-NLS 49 | prefs.set(Constants.PREF_ENABLE_E2E, enableE2E); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/utils/ReceiverDataRegistry.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | import android.util.SparseArray; 4 | 5 | import java.util.Random; 6 | 7 | /** 8 | * Receiver classes and services are instantiated by Android itself, and we cannot pass arbitrary 9 | * objects to them during construction. They receive Intents, where we can put some basic data, but 10 | * for transferring custom objects, the receivers must retrieve the objects themselves from 11 | * elsewhere. The purpose of this class is to act as a registry where objects can be placed and 12 | * substituted with an index, which can be set in the Intent data. The receiver then uses that index 13 | * to retrieve the object itself from the registry, allowing transfers of complex objects to 14 | * receiver classes. 15 | * 16 | * @author Marius Lindvall 17 | */ 18 | public enum ReceiverDataRegistry { 19 | ; 20 | 21 | private static final SparseArray data = new SparseArray<>(); 22 | private static final Random random = new Random(); 23 | 24 | /** 25 | * Registers the given object in the registry. 26 | * 27 | * @param obj The object to register. 28 | * @return An index which can be used to retrieve the object later using retrieve(). 29 | */ 30 | public static int register(Object obj) { 31 | int index = random.nextInt(); 32 | data.put(index, obj); 33 | return index; 34 | } 35 | 36 | /** 37 | * Retrieves an object from the registry given its index and deletes the object. 38 | * 39 | * @param index The index obtained when registering the object using register(). 40 | * @return The object that was stored in the registry. 41 | */ 42 | public static Object retrieve(int index) { 43 | return retrieve(index, false); 44 | } 45 | 46 | /** 47 | * Retrieves an object from the registry given its index. 48 | * 49 | * @param index The index obtained when registering the object using register(). 50 | * @param keep Whether or not to keep the object in the registry after retrieval. 51 | * @return The object that was stored in the registry. 52 | */ 53 | public static Object retrieve(int index, boolean keep) { 54 | Object obj = data.get(index); 55 | if (!keep) data.remove(index); 56 | return obj; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/utils/StringSerializer.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | import android.util.Base64; 4 | 5 | import androidx.annotation.Nullable; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.IOException; 10 | import java.io.ObjectInputStream; 11 | import java.io.ObjectOutputStream; 12 | import java.io.Serializable; 13 | 14 | /** 15 | * Helper class that serializes a serializable class to and from Base64-encoded strings for storage 16 | * in Android shared preferences. 17 | */ 18 | public enum StringSerializer { 19 | ; 20 | 21 | /** 22 | * Serializes an object instance. 23 | * 24 | * @param obj The object to serialize. 25 | * 26 | * @return A Base64-encoded representation of the object. 27 | */ 28 | public static String serialize(Serializable obj) { 29 | @SuppressWarnings("SpellCheckingInspection") 30 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 31 | try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { 32 | oos.writeObject(obj); 33 | } catch (IOException e) { 34 | Log.e(String.format("Exception thrown when serializing instance of %s", obj.getClass().getName()), e); //NON-NLS 35 | } 36 | return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); 37 | } 38 | 39 | /** 40 | * Deserialize an object instance. 41 | * 42 | * @param pref The Base64-encoded representation of the object. 43 | * @param The serializable class type to cast to when de-serializing. 44 | * @return The de-serialized object. 45 | */ 46 | @Nullable 47 | public static T deserialize(String pref) { 48 | if (pref == null) return null; 49 | T obj = null; 50 | @SuppressWarnings("SpellCheckingInspection") 51 | ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(pref, Base64.DEFAULT)); 52 | try (ObjectInputStream ois = new ObjectInputStream(bais)) { 53 | //noinspection unchecked 54 | obj = (T) ois.readObject(); 55 | } catch (Exception e) { 56 | Log.e("Exception thrown when de-serializing a serializable instance", e); //NON-NLS 57 | } 58 | return obj; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /android/app/src/main/java/info/varden/hauk/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package info.varden.hauk.utils; 2 | 3 | /** 4 | * Utility class to process strings. 5 | * 6 | * @author Marius Lindvall 7 | */ 8 | public enum StringUtils { 9 | ; 10 | 11 | // Byte array to hex string function by maybeWeCouldStealAVan 12 | // https://stackoverflow.com/a/9855338 13 | 14 | @SuppressWarnings("HardCodedStringLiteral") 15 | private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 16 | 17 | @SuppressWarnings("MagicNumber") 18 | public static String bytesToHex(byte[] bytes) { 19 | StringBuilder sb = new StringBuilder(); 20 | char[] hexChars = new char[bytes.length * 2]; 21 | for (int j = 0; j < bytes.length; j++) { 22 | int v = bytes[j] & 0xFF; 23 | hexChars[j * 2] = HEX_ARRAY[v >>> 4]; 24 | hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; 25 | } 26 | return new String(hexChars); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_brightness_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_bug_report.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_button_copy.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_button_share.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_button_stop.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_code.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_directions_walk.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_location_disabled.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_lock_open.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_no_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_person.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_proxy.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_security.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_server.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_timer.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/content_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 15 | 16 | 17 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 |