├── .github
└── workflows
│ └── android.yml
├── .gitignore
├── LICENSE
├── README.md
├── SupportedDevices.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher_alpha-playstore.png
│ ├── java
│ ├── com
│ │ └── fpvout
│ │ │ └── digiview
│ │ │ ├── DataCollectionAgreementPopupActivity.java
│ │ │ ├── H264Extractor.java
│ │ │ ├── InputStreamBufferedDataSource.java
│ │ │ ├── InputStreamDataSource.java
│ │ │ ├── MainActivity.java
│ │ │ ├── OverlayStatus.java
│ │ │ ├── OverlayView.java
│ │ │ ├── PerformancePreset.java
│ │ │ ├── SettingsActivity.java
│ │ │ ├── UsbDeviceBroadcastReceiver.java
│ │ │ ├── UsbDeviceListener.java
│ │ │ ├── UsbMaskConnection.java
│ │ │ ├── VersionPreference.java
│ │ │ └── VideoReaderExoplayer.java
│ └── usb
│ │ ├── AndroidUSBInputStream.java
│ │ ├── AndroidUSBOutputStream.java
│ │ └── CircularByteBuffer.java
│ └── res
│ ├── drawable-hdpi
│ ├── ic_goggles.png
│ ├── ic_goggles_disconnected_red.png
│ ├── ic_goggles_disconnected_white.png
│ ├── ic_goggles_white.png
│ └── ic_splash.png
│ ├── drawable-mdpi
│ ├── ic_goggles.png
│ ├── ic_goggles_disconnected_red.png
│ ├── ic_goggles_disconnected_white.png
│ ├── ic_goggles_white.png
│ └── ic_splash.png
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable-xhdpi
│ ├── ic_goggles.png
│ ├── ic_goggles_disconnected_red.png
│ ├── ic_goggles_disconnected_white.png
│ ├── ic_goggles_white.png
│ └── ic_splash.png
│ ├── drawable-xxhdpi
│ ├── ic_goggles.png
│ ├── ic_goggles_disconnected_red.png
│ ├── ic_goggles_disconnected_white.png
│ ├── ic_goggles_white.png
│ ├── ic_gogles_disconnected_white.png
│ └── ic_splash.png
│ ├── drawable-xxxhdpi
│ ├── ic_goggles.png
│ ├── ic_goggles_disconnected_red.png
│ ├── ic_goggles_disconnected_white.png
│ ├── ic_goggles_white.png
│ └── ic_splash.png
│ ├── drawable
│ ├── ic_launcher_background.xml
│ └── splash.xml
│ ├── font
│ └── gidolinya.otf
│ ├── layout
│ ├── activity_main.xml
│ ├── backdrop_view.xml
│ ├── datacollection_agreement_popup.xml
│ └── settings_activity.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ ├── ic_launcher_foreground.png
│ └── ic_launcher_round.png
│ ├── values-de
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-night
│ └── themes.xml
│ ├── values-pt
│ └── strings.xml
│ ├── values-zh
│ └── strings.xml
│ ├── values
│ ├── arrays.xml
│ ├── colors.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── themes.xml
│ └── xml
│ ├── device_filter.xml
│ └── root_preferences.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.github/workflows/android.yml:
--------------------------------------------------------------------------------
1 | name: Android CI
2 |
3 | on:
4 | push:
5 | branches: [ master, dev ]
6 | pull_request:
7 | branches: [ master, dev ]
8 | workflow_dispatch:
9 |
10 |
11 |
12 | jobs:
13 | apk:
14 | name: Generate APK
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v1
19 | - name: set up JDK 1.8
20 | uses: actions/setup-java@v1
21 | with:
22 | java-version: 1.8
23 | - name: Build debug APK
24 | run: bash ./gradlew assembleDebug --stacktrace
25 | - name: Upload APK
26 | uses: actions/upload-artifact@v1
27 | with:
28 | name: app
29 | path: app/build/outputs/apk/debug/app-debug.apk
30 |
--------------------------------------------------------------------------------
/.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 | .idea
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | local.properties
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 fpvout
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://play.google.com/store/apps/details?id=com.fpvout.digiview)
2 |
3 | DigiView is an Android app that allows you to get a live preview from your DJI FPV Goggles (V1 & V2).
4 |
5 | Working with Android 7+ and devices supporting USB Host mode.
6 |
7 | [Is my device Compatible?](SupportedDevices.md) (non-exhaustive)
8 |
9 | [DigiView Wiki](https://github.com/fpvout/fpvout.com/wiki)
10 |
11 | ## Download
12 | You can download the **Beta** Release [from Google Play Store](https://play.google.com/store/apps/details?id=com.fpvout.digiview). If you need an apk, you can get one [here](https://github.com/fpvout/DigiView-Android/releases/download/v1.0.0-beta/DigiView_1.0.0_Beta.apk).
13 |
14 | **Note:** App is still in development so you might run into some bugs or even a crash.
15 |
16 | ## Instructions
17 | - Power on your goggles.
18 | - Power on your drone.
19 | - Plug the USB cable from your goggles into your phone (if using an OTG adapter, that goes into your phone).
20 | - Automatic launch is enabled so app should launch. If not, just launch app manually.
21 | - Wait a bit and video should start streaming.
22 | - If there is no video after a few seconds, it's frozen or slowed down, try to unplug the USB cable and plug it back in.
23 |
24 | If there is any other issues, please check our [Discord server](https://discord.gg/uGYMNByeTH), some people might help you there.
25 |
26 | ## Known Issues
27 | - **You might need to set Auto temp control off on your Vista/Air unit (under Settings > Device in goggle menu) to get it to work**.
28 | - Some people reported it's working best in 50mbps mode
29 | - Concerning hardware, USB-C to USB-C cables sometimes don't work depending on the phone you're using. in that case, an USB OTG adapter is the best option.
30 | - You *may* need to activate USB OTG in your Android settings to get the fpv goggles detected.
31 | - See [open issues](https://github.com/fpvout/DigiView-Android/issues) for more or to report a new issue.
32 |
33 | ## Development
34 | - We will put more info here in the near future.
35 |
36 | ## Contributing
37 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Most PRs should be based on and target `dev` branch.
38 |
39 | ## They deserve some credits!
40 | - [jlucidar](https://github.com/jlucidar) - Lead developer - [Donate](https://paypal.me/jlucidar) - [Youtube](https://www.youtube.com/channel/UCBbyqtxntnlF6Cn_8ezkTLQ)
41 | - [omouren](https://github.com/omouren) - Some really appreciated stability fixes! - [Donate](https://paypal.me/omouren) - [Youtube](https://www.youtube.com/channel/UCJi-wllO8GY5f9k8gB_VGTg)
42 | - [vinayselvaraj](https://github.com/vinayselvaraj) - stopped the screen from sleeping!
43 | - [Joonas](https://fpv.wtf/) - Found the secret packet - [Donate](https://www.buymeacoffee.com/fpv.wtf)
44 | - [D3VL](https://d3vl.com) - Who is orchestrating all that stuff - [Donate](https://www.buymeacoffee.com/d3vl)
45 |
46 | Please make sure to check these guys out! And feel free to donate ;)
47 |
48 | ## Made with love using:
49 | - [Exoplayer](https://exoplayer.dev/)
50 | - [Gidolinya font](https://github.com/larsenwork/Gidole)
51 |
52 | ## License
53 | [MIT](https://choosealicense.com/licenses/mit/)
54 |
--------------------------------------------------------------------------------
/SupportedDevices.md:
--------------------------------------------------------------------------------
1 | | Brand | Model | Android Version | v1 Goggles | v2 Goggles | 25mbit | 50mbit | Latency | Notes |
2 | |:---------:|:--------------|:-----------------:|:-------------:|:-------------:|:---------:|:---------:|:---------:|:-----:|
3 | | Essential | PH-1 | 10 | 🟢 | ❔ | 🔴 | 🟢 | 200-2000ms||
4 | | Fairphone | 3 | 10 | ❔ | 🟢 | 🟢 | ❔ | 5+ seconds||
5 | | Google | Pixel 4XL | 11 | 🟢 | ❔ | ❔ | 🟢 | ~150ms ||
6 | | Google | Nexus 7 (2012)| 7.1.2 | ❔ | 🟢 | 🟢 | 🟢 | ~1000+ms | Very high latency but still good enough to show spectators. |
7 | | Huawei | P30 Pro | 10.1 | 🟢 | ❔ | 🔴 | 🔴 | ❔ ||
8 | | LG | Nexus 5x | 8.1 | ❔ | 🟢 | ❔ | 🟢 | 600+ms ||
9 | | Nokia | 5.1 Plus | 10 | 🟢 | ❔ | 🟢 | 🟢 | ~200ms ||
10 | | OnePlus | Nord N10 5G | 10 | 🟢 | ❔ | 🟢 | 🟢 | 100-200ms ||
11 | | OnePlus | 8 Pro | 11 | ❔ | 🟢 | 🔴 | 🟢 | ~180ms ||
12 | | OnePlus | 7T | 10 | 🟢 | ❔ | ❔ | 🟢 | ~150ms | Sometimes it works very well. Most of the times it shows a few frames at high speed and then freezes or shows frames at a very low rate. Changing cables didn't seem to help it.|
13 | | Samsung | Galaxy S20 | Latest | 🟢 | ❔ | 🔴 | 🟢 | Fast ||
14 | | Samsung | S7 Edge | 8.0.0 | ❔ | 🟢 | ❔ | 🟢 | ~220ms ||
15 | | Samsung | S20 5G | 11 | 🟢 | ❔ | 🔴 | 🟢 | 200-300ms ||
16 | | Samsung | Galaxy S8 | ❔ | 🟢 | ❔ | ❔ | 🟢 | ❔ ||
17 | | Samsung | Galaxy S9 | 10 | 🟢 | ❔ | ❔ | 🟢 | High, then low | Occasionally freezes
18 | | Samsung | Note 9 | 10 | 🟢 | ❔ | ❔ | 🟢 | ~150ms||
19 | | Samsung | S10+ 5G | 11 | 🟢 | ❔ | 🟢 | 🟢 | ~500ms ||
20 | | Samsung | Galaxy S7 (not edge)| 8.0.0 | ❔ | 🟢 | ❔ | 🟢 | ~200ms ||
21 | | Samsung | S7 | 8.0 | 🟢 | ❔ | 🟢 | 🟢 | <1s||
22 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | signingConfigs {
7 | digiview {
8 | try{
9 | storeFile file(digiviewStoreFile)
10 | storePassword digiviewStorePassword
11 | keyPassword digiviewKeyPassword
12 | keyAlias digiviewKeyAlias
13 | }
14 | catch (ex) {
15 | println("You should define mStoreFile, mStorePassword, mKeyPassword and mKeyAlias in ~/.gradle/gradle.properties.")
16 | }
17 | }
18 | }
19 |
20 | compileSdkVersion 30
21 | buildToolsVersion "30.0.3"
22 |
23 | defaultConfig {
24 | applicationId "com.fpvout.digiview"
25 | minSdkVersion 21
26 | targetSdkVersion 30
27 | versionCode 3
28 | versionName '1.0.0'
29 | resConfigs "en", "de", "fr", "es", "zh", "pt"
30 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
31 | }
32 |
33 | buildTypes {
34 | debug {
35 | applicationIdSuffix '.debug'
36 | versionNameSuffix '-debug'
37 | signingConfig debug.signingConfig
38 | debuggable true
39 | }
40 | alpha {
41 | //applicationIdSuffix '.alpha'
42 | versionNameSuffix '-alpha'
43 | signingConfig signingConfigs.digiview
44 | }
45 | beta {
46 | //applicationIdSuffix '.beta'
47 | minifyEnabled false
48 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
49 | versionNameSuffix '-beta'
50 | signingConfig signingConfigs.digiview
51 | }
52 | release {
53 | minifyEnabled false
54 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
55 | signingConfig signingConfigs.digiview
56 | }
57 |
58 | }
59 |
60 | compileOptions {
61 | sourceCompatibility JavaVersion.VERSION_1_8
62 | targetCompatibility JavaVersion.VERSION_1_8
63 | }
64 | }
65 |
66 | dependencies {
67 |
68 | implementation 'androidx.appcompat:appcompat:1.2.0'
69 | implementation 'com.google.android.material:material:1.3.0'
70 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
71 | implementation 'com.google.android.exoplayer:exoplayer:2.13.3'
72 | implementation 'io.sentry:sentry-android:4.3.0'
73 | implementation 'androidx.preference:preference:1.1.1'
74 |
75 | testImplementation 'junit:junit:4.+'
76 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
78 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
17 |
21 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
43 |
44 |
46 |
47 |
48 |
49 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher_alpha-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/ic_launcher_alpha-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/DataCollectionAgreementPopupActivity.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.os.Bundle;
7 |
8 | import androidx.appcompat.app.AppCompatActivity;
9 | import androidx.preference.PreferenceManager;
10 |
11 | public class DataCollectionAgreementPopupActivity extends AppCompatActivity {
12 | private SharedPreferences preferences;
13 | private AlertDialog.Builder builder;
14 |
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | preferences = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext());
18 | builder = new AlertDialog.Builder(this);
19 | initializeUI();
20 | }
21 |
22 | private void initializeUI() {
23 | builder.setTitle(R.string.data_collection_header)
24 | .setMessage(R.string.data_collection_text)
25 | .setPositiveButton(R.string.data_collection_agree_button, (dialog, which) -> confirmDataCollection())
26 | .setNegativeButton(R.string.data_collection_deny_button, (dialog, which) -> cancelDataCollection());
27 | AlertDialog dialog = builder.create();
28 | dialog.show();
29 | }
30 |
31 | private void cancelDataCollection() {
32 | preferences.edit()
33 | .putBoolean("dataCollectionAccepted", false)
34 | .putBoolean("dataCollectionReplied", true).apply();
35 |
36 | Intent intent = new Intent();
37 | setResult(RESULT_OK, intent);
38 |
39 | finish();
40 | }
41 |
42 | private void confirmDataCollection() {
43 | preferences.edit()
44 | .putBoolean("dataCollectionAccepted", true)
45 | .putBoolean("dataCollectionReplied", true).apply();
46 | Intent intent = new Intent();
47 | setResult(RESULT_OK, intent);
48 | finish();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/H264Extractor.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import com.google.android.exoplayer2.C;
4 | import com.google.android.exoplayer2.Format;
5 | import com.google.android.exoplayer2.extractor.Extractor;
6 | import com.google.android.exoplayer2.extractor.ExtractorInput;
7 | import com.google.android.exoplayer2.extractor.ExtractorOutput;
8 | import com.google.android.exoplayer2.extractor.ExtractorsFactory;
9 | import com.google.android.exoplayer2.extractor.PositionHolder;
10 | import com.google.android.exoplayer2.extractor.SeekMap;
11 | import com.google.android.exoplayer2.extractor.ts.H264Reader;
12 | import com.google.android.exoplayer2.extractor.ts.TsPayloadReader;
13 | import com.google.android.exoplayer2.util.ParsableByteArray;
14 | import com.google.android.exoplayer2.extractor.ts.SeiReader;
15 |
16 | import java.io.IOException;
17 | import java.util.ArrayList;
18 |
19 | import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR;
20 | /**
21 | * Extracts data from H264 bitstreams.
22 | */
23 | public final class H264Extractor implements Extractor {
24 | /** Factory for {@link H264Extractor} instances. */
25 | public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new H264Extractor()};
26 |
27 | private static int MAX_SYNC_FRAME_SIZE = 131072;
28 |
29 | private long firstSampleTimestampUs;
30 | private static long sampleTime = 10000; // todo: try to lower this. it directly infer on speed and latency. this should be equal to 16666 to reach 60fps but works better with lower value
31 | private final H264Reader reader;
32 | private final ParsableByteArray sampleData;
33 |
34 | private boolean startedPacket;
35 |
36 | public H264Extractor() {
37 | this(0);
38 | }
39 |
40 | public H264Extractor(int mMaxSyncFrameSize, int mSampleTime) {
41 | this(0, mMaxSyncFrameSize, mSampleTime);
42 | }
43 |
44 | public H264Extractor(long firstSampleTimestampUs) {
45 | this(firstSampleTimestampUs, MAX_SYNC_FRAME_SIZE, (int) sampleTime);
46 | }
47 |
48 | public H264Extractor(long firstSampleTimestampUs, int mMaxSyncFrameSize, int mSampleTime) {
49 | MAX_SYNC_FRAME_SIZE = mMaxSyncFrameSize;
50 | sampleTime = mSampleTime;
51 | this.firstSampleTimestampUs = firstSampleTimestampUs;
52 | reader = new H264Reader(new SeiReader(new ArrayList()),false,true);
53 | sampleData = new ParsableByteArray(MAX_SYNC_FRAME_SIZE);
54 | }
55 |
56 | // Extractor implementation.
57 | @Override
58 | public boolean sniff(ExtractorInput input) throws IOException {
59 | return true;
60 | }
61 |
62 | @Override
63 | public void init(ExtractorOutput output) {
64 | reader.createTracks(output, new TsPayloadReader.TrackIdGenerator(0, 1));
65 | output.endTracks();
66 | output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
67 | }
68 |
69 | @Override
70 | public void seek(long position, long timeUs) {
71 | startedPacket = false;
72 | reader.seek();
73 | }
74 |
75 | @Override
76 | public void release() {
77 | // Do nothing.
78 | }
79 |
80 | @Override
81 | public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException {
82 | int bytesRead = input.read(sampleData.getData(), 0, MAX_SYNC_FRAME_SIZE);
83 | if (bytesRead == C.RESULT_END_OF_INPUT) {
84 | return RESULT_END_OF_INPUT;
85 | }
86 |
87 | // Feed whatever data we have to the reader, regardless of whether the read finished or not.
88 | sampleData.setPosition(0);
89 | sampleData.setLimit(bytesRead);
90 | if (!startedPacket) {
91 | // Pass data to the reader as though it's contained within a single infinitely long packet.
92 | reader.packetStarted(firstSampleTimestampUs, FLAG_DATA_ALIGNMENT_INDICATOR);
93 | startedPacket = true;
94 | }
95 | firstSampleTimestampUs+=sampleTime;
96 | reader.packetStarted(firstSampleTimestampUs, FLAG_DATA_ALIGNMENT_INDICATOR);
97 | reader.consume(sampleData);
98 | return RESULT_CONTINUE;
99 | }
100 |
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 |
6 | import com.google.android.exoplayer2.C;
7 | import com.google.android.exoplayer2.upstream.DataSource;
8 | import com.google.android.exoplayer2.upstream.DataSpec;
9 | import com.google.android.exoplayer2.upstream.TransferListener;
10 |
11 | import java.io.EOFException;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 |
15 | import usb.CircularByteBuffer;
16 |
17 | public class InputStreamBufferedDataSource implements DataSource {
18 | private static final int READ_BUFFER_SIZE = 50 * 1024 * 1024;
19 | private static final String ERROR_THREAD_NOT_INITIALIZED = "Read thread not initialized, call first 'startReadThread()'";
20 | private static final long READ_TIMEOUT = 200;
21 |
22 | private Context context;
23 | private DataSpec dataSpec;
24 | private InputStream inputStream;
25 | private long bytesRemaining;
26 | private boolean opened;
27 |
28 | private CircularByteBuffer readBuffer;
29 | private Thread receiveThread;
30 | private boolean working;
31 |
32 |
33 | public InputStreamBufferedDataSource(Context context, DataSpec dataSpec, InputStream inputStream) {
34 | this.context = context;
35 | this.dataSpec = dataSpec;
36 | this.inputStream = inputStream;
37 | startReadThread();
38 | }
39 |
40 | @Override
41 | public void addTransferListener(TransferListener transferListener) {
42 |
43 | }
44 |
45 | @Override
46 | public long open(DataSpec dataSpec) throws IOException {
47 | try {
48 | long skipped = inputStream.skip(dataSpec.position);
49 | if (skipped < dataSpec.position)
50 | throw new EOFException();
51 |
52 | if (dataSpec.length != C.LENGTH_UNSET) {
53 | bytesRemaining = dataSpec.length;
54 | } else {
55 | bytesRemaining = C.LENGTH_UNSET;
56 | }
57 | } catch (IOException e) {
58 | throw new IOException(e);
59 | }
60 |
61 | opened = true;
62 | return bytesRemaining;
63 | }
64 |
65 | @Override
66 | public int read(byte[] buffer, int offset, int readLength) throws IOException {
67 | if (readBuffer == null)
68 | throw new IOException(ERROR_THREAD_NOT_INITIALIZED);
69 |
70 | long deadLine = System.currentTimeMillis() + READ_TIMEOUT;
71 | int readBytes = 0;
72 | while (System.currentTimeMillis() < deadLine && readBytes <= 0)
73 | readBytes = readBuffer.read(buffer, offset, readLength);
74 | if (readBytes <= 0)
75 | return readBytes;
76 | return readBytes;
77 | }
78 |
79 | public void startReadThread(){
80 | if (!working) {
81 | working = true;
82 | readBuffer = new CircularByteBuffer(READ_BUFFER_SIZE);
83 | receiveThread = new Thread() {
84 | @Override
85 | public void run() {
86 | while (working) {
87 | byte[] buffer = new byte[1024];
88 | int receivedBytes = 0;
89 | try {
90 | receivedBytes = inputStream.read(buffer, 0, buffer.length);
91 | } catch (IOException e) {
92 | e.printStackTrace();
93 | }
94 | if (receivedBytes > 0) {
95 | readBuffer.write(buffer, 0, receivedBytes);
96 | }
97 | }
98 | }
99 | };
100 | receiveThread.start();
101 | }
102 | }
103 |
104 | @Override
105 | public Uri getUri() {
106 | return dataSpec.uri;
107 | }
108 |
109 | @Override
110 | public void close() throws IOException {
111 | working = false;
112 | if (receiveThread != null){
113 | receiveThread.interrupt();
114 | }
115 | try {
116 | if (inputStream != null) {
117 | inputStream.close();
118 | }
119 | } catch (IOException e) {
120 | throw new IOException(e);
121 | } finally {
122 | inputStream = null;
123 | if (opened) {
124 | opened = false;
125 | }
126 | }
127 | }
128 |
129 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 |
6 | import com.google.android.exoplayer2.C;
7 | import com.google.android.exoplayer2.upstream.DataSource;
8 | import com.google.android.exoplayer2.upstream.DataSpec;
9 | import com.google.android.exoplayer2.upstream.TransferListener;
10 |
11 | import java.io.EOFException;
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 |
15 | public class InputStreamDataSource implements DataSource {
16 | private Context context;
17 | private DataSpec dataSpec;
18 | private InputStream inputStream;
19 | private long bytesRemaining;
20 | private boolean opened;
21 |
22 | public InputStreamDataSource(Context context, DataSpec dataSpec, InputStream inputStream) {
23 | this.context = context;
24 | this.dataSpec = dataSpec;
25 | this.inputStream = inputStream;
26 | }
27 |
28 | @Override
29 | public void addTransferListener(TransferListener transferListener) {
30 |
31 | }
32 |
33 | @Override
34 | public long open(DataSpec dataSpec) throws IOException {
35 | try {
36 | long skipped = inputStream.skip(dataSpec.position);
37 | if (skipped < dataSpec.position)
38 | throw new EOFException();
39 |
40 | if (dataSpec.length != C.LENGTH_UNSET) {
41 | bytesRemaining = dataSpec.length;
42 | } else {
43 | bytesRemaining = C.LENGTH_UNSET;
44 | }
45 | } catch (IOException e) {
46 | throw new IOException(e);
47 | }
48 |
49 | opened = true;
50 | return bytesRemaining;
51 | }
52 |
53 | @Override
54 | public int read(byte[] buffer, int offset, int readLength) throws IOException {
55 | return inputStream.read(buffer, offset, readLength);
56 | }
57 |
58 | @Override
59 | public Uri getUri() {
60 | return dataSpec.uri;
61 | }
62 |
63 | @Override
64 | public void close() throws IOException {
65 | try {
66 | if (inputStream != null) {
67 | inputStream.close();
68 | }
69 | } catch (IOException e) {
70 | throw new IOException(e);
71 | } finally {
72 | inputStream = null;
73 | if (opened) {
74 | opened = false;
75 | }
76 | }
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.LayoutTransition;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.content.SharedPreferences;
11 | import android.hardware.usb.UsbDevice;
12 | import android.hardware.usb.UsbManager;
13 | import android.os.Bundle;
14 | import android.os.Handler;
15 | import android.util.Log;
16 | import android.view.GestureDetector;
17 | import android.view.MotionEvent;
18 | import android.view.ScaleGestureDetector;
19 | import android.view.SurfaceView;
20 | import android.view.View;
21 | import android.view.ViewGroup;
22 | import android.view.WindowManager;
23 |
24 | import androidx.appcompat.app.ActionBar;
25 | import androidx.appcompat.app.AppCompatActivity;
26 | import androidx.preference.PreferenceManager;
27 |
28 | import java.util.HashMap;
29 |
30 | import io.sentry.SentryLevel;
31 | import io.sentry.android.core.SentryAndroid;
32 |
33 | import static com.fpvout.digiview.VideoReaderExoplayer.VideoZoomedIn;
34 |
35 | public class MainActivity extends AppCompatActivity implements UsbDeviceListener {
36 | private static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION";
37 | private static final String TAG = "DIGIVIEW";
38 | private static final int VENDOR_ID = 11427;
39 | private static final int PRODUCT_ID = 31;
40 | private int shortAnimationDuration;
41 | private float buttonAlpha = 1;
42 | private View settingsButton;
43 | private View watermarkView;
44 | private OverlayView overlayView;
45 | PendingIntent permissionIntent;
46 | UsbDeviceBroadcastReceiver usbDeviceBroadcastReceiver;
47 | UsbManager usbManager;
48 | UsbDevice usbDevice;
49 | UsbMaskConnection mUsbMaskConnection;
50 | VideoReaderExoplayer mVideoReader;
51 | boolean usbConnected = false;
52 | SurfaceView fpvView;
53 | private GestureDetector gestureDetector;
54 | private ScaleGestureDetector scaleGestureDetector;
55 | private SharedPreferences sharedPreferences;
56 | private static final String ShowWatermark = "ShowWatermark";
57 |
58 | @Override
59 | protected void onCreate(Bundle savedInstanceState) {
60 | super.onCreate(savedInstanceState);
61 | Log.d(TAG, "APP - On Create");
62 | setContentView(R.layout.activity_main);
63 |
64 | // check Data Collection agreement
65 | checkDataCollectionAgreement();
66 |
67 | // Hide top bar and status bar
68 | View decorView = getWindow().getDecorView();
69 | decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
70 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
71 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
72 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
73 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
74 | | View.SYSTEM_UI_FLAG_FULLSCREEN);
75 | ActionBar actionBar = getSupportActionBar();
76 | if (actionBar != null) {
77 | actionBar.hide();
78 | }
79 |
80 | // Prevent screen from sleeping
81 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
82 |
83 | usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
84 | permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
85 | usbDeviceBroadcastReceiver = new UsbDeviceBroadcastReceiver(this);
86 |
87 | IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
88 | registerReceiver(usbDeviceBroadcastReceiver, filter);
89 | IntentFilter filterDetached = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
90 | registerReceiver(usbDeviceBroadcastReceiver, filterDetached);
91 |
92 | shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
93 | watermarkView = findViewById(R.id.watermarkView);
94 | overlayView = findViewById(R.id.overlayView);
95 | fpvView = findViewById(R.id.fpvView);
96 |
97 | settingsButton = findViewById(R.id.settingsButton);
98 | settingsButton.setOnClickListener(new View.OnClickListener() {
99 | @Override
100 | public void onClick(View v) {
101 | Intent intent = new Intent(v.getContext(), SettingsActivity.class);
102 | v.getContext().startActivity(intent);
103 | }
104 | });
105 |
106 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
107 |
108 | // Enable resizing animations
109 | ((ViewGroup) findViewById(R.id.mainLayout)).getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
110 |
111 | setupGestureDetectors();
112 |
113 | mUsbMaskConnection = new UsbMaskConnection();
114 | Handler videoReaderEventListener = new Handler(this.getMainLooper(), msg -> onVideoReaderEvent((VideoReaderExoplayer.VideoReaderEventMessageCode) msg.obj));
115 |
116 | mVideoReader = new VideoReaderExoplayer(fpvView, this, videoReaderEventListener);
117 |
118 | if (!usbConnected) {
119 | if (searchDevice()) {
120 | connect();
121 | } else {
122 | showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Disconnected);
123 | }
124 | }
125 | }
126 |
127 | private void setupGestureDetectors() {
128 | gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
129 | @Override
130 | public boolean onSingleTapConfirmed(MotionEvent e) {
131 | toggleSettingsButton();
132 | return super.onSingleTapConfirmed(e);
133 | }
134 |
135 | @Override
136 | public boolean onDoubleTap(MotionEvent e) {
137 | mVideoReader.toggleZoom();
138 | return super.onDoubleTap(e);
139 | }
140 | });
141 |
142 | scaleGestureDetector = new ScaleGestureDetector(this, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
143 | @Override
144 | public void onScaleEnd(ScaleGestureDetector detector) {
145 | if (detector.getScaleFactor() < 1) {
146 | mVideoReader.zoomOut();
147 | } else {
148 | mVideoReader.zoomIn();
149 | }
150 | }
151 | });
152 | }
153 |
154 | @Override
155 | public boolean onTouchEvent(MotionEvent event) {
156 | gestureDetector.onTouchEvent(event);
157 | scaleGestureDetector.onTouchEvent(event);
158 |
159 | return super.onTouchEvent(event);
160 | }
161 |
162 | private void updateWatermark() {
163 | if (overlayView.getVisibility() == View.VISIBLE) {
164 | watermarkView.setAlpha(0);
165 | return;
166 | }
167 |
168 | if (sharedPreferences.getBoolean(ShowWatermark, true)) {
169 | watermarkView.setAlpha(0.3F);
170 | } else {
171 | watermarkView.setAlpha(0F);
172 | }
173 | }
174 |
175 | private void updateVideoZoom() {
176 | if (sharedPreferences.getBoolean(VideoZoomedIn, true)) {
177 | mVideoReader.zoomIn();
178 | } else {
179 | mVideoReader.zoomOut();
180 | }
181 | }
182 |
183 | private void cancelButtonAnimation() {
184 | Handler handler = settingsButton.getHandler();
185 | if (handler != null) {
186 | settingsButton.getHandler().removeCallbacksAndMessages(null);
187 | }
188 | }
189 |
190 | private void showSettingsButton() {
191 | cancelButtonAnimation();
192 |
193 | if (overlayView.getVisibility() == View.VISIBLE) {
194 | buttonAlpha = 1;
195 | settingsButton.setAlpha(1);
196 | }
197 | }
198 |
199 | private void toggleSettingsButton() {
200 | if (buttonAlpha == 1 && overlayView.getVisibility() == View.VISIBLE) return;
201 |
202 | // cancel any pending delayed animations first
203 | cancelButtonAnimation();
204 |
205 | if (buttonAlpha == 1) {
206 | buttonAlpha = 0;
207 | } else {
208 | buttonAlpha = 1;
209 | }
210 |
211 | settingsButton.animate()
212 | .alpha(buttonAlpha)
213 | .setDuration(shortAnimationDuration)
214 | .setListener(new AnimatorListenerAdapter() {
215 | @Override
216 | public void onAnimationEnd(Animator animation) {
217 | autoHideSettingsButton();
218 | }
219 | });
220 | }
221 |
222 | private void autoHideSettingsButton() {
223 | if (overlayView.getVisibility() == View.VISIBLE) return;
224 | if (buttonAlpha == 0) return;
225 |
226 | settingsButton.postDelayed(new Runnable() {
227 | @Override
228 | public void run() {
229 | buttonAlpha = 0;
230 | settingsButton.animate()
231 | .alpha(0)
232 | .setDuration(shortAnimationDuration);
233 | }
234 | }, 3000);
235 | }
236 |
237 | @Override
238 | public void usbDeviceApproved(UsbDevice device) {
239 | Log.i(TAG, "USB - usbDevice approved");
240 | usbDevice = device;
241 | showOverlay(R.string.usb_device_approved, OverlayStatus.Connected);
242 | connect();
243 | }
244 |
245 | @Override
246 | public void usbDeviceDetached() {
247 | Log.i(TAG, "USB - usbDevice detached");
248 | showOverlay(R.string.usb_device_detached_waiting, OverlayStatus.Disconnected);
249 | this.onStop();
250 | }
251 |
252 | private boolean searchDevice() {
253 | HashMap deviceList = usbManager.getDeviceList();
254 | if (deviceList.size() <= 0) {
255 | usbDevice = null;
256 | return false;
257 | }
258 |
259 | for (UsbDevice device : deviceList.values()) {
260 | if (device.getVendorId() == VENDOR_ID && device.getProductId() == PRODUCT_ID) {
261 | if (usbManager.hasPermission(device)) {
262 | Log.i(TAG, "USB - usbDevice attached");
263 | showOverlay(R.string.usb_device_found, OverlayStatus.Connected);
264 | usbDevice = device;
265 | return true;
266 | }
267 |
268 | usbManager.requestPermission(device, permissionIntent);
269 | }
270 | }
271 |
272 | return false;
273 | }
274 |
275 | private void connect() {
276 | usbConnected = true;
277 | mUsbMaskConnection.setUsbDevice(usbManager.openDevice(usbDevice), usbDevice);
278 | mVideoReader.setUsbMaskConnection(mUsbMaskConnection);
279 | overlayView.hide();
280 | mVideoReader.start();
281 | updateWatermark();
282 | autoHideSettingsButton();
283 | showOverlay(R.string.waiting_for_video, OverlayStatus.Connected);
284 | }
285 |
286 | @Override
287 | public void onResume() {
288 | super.onResume();
289 | Log.d(TAG, "APP - On Resume");
290 |
291 | View decorView = getWindow().getDecorView();
292 | decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
293 | | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
294 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
295 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
296 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
297 | | View.SYSTEM_UI_FLAG_FULLSCREEN);
298 | ActionBar actionBar = getSupportActionBar();
299 | if (actionBar != null) {
300 | actionBar.hide();
301 | }
302 |
303 | if (!usbConnected) {
304 | if (searchDevice()) {
305 | Log.d(TAG, "APP - On Resume usbDevice device found");
306 | connect();
307 | } else {
308 | showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Connected);
309 | }
310 | }
311 |
312 | settingsButton.setAlpha(1);
313 | autoHideSettingsButton();
314 | updateWatermark();
315 | updateVideoZoom();
316 | }
317 |
318 | private boolean onVideoReaderEvent(VideoReaderExoplayer.VideoReaderEventMessageCode m) {
319 | if (VideoReaderExoplayer.VideoReaderEventMessageCode.WAITING_FOR_VIDEO.equals(m)) {
320 | Log.d(TAG, "event: WAITING_FOR_VIDEO");
321 | showOverlay(R.string.waiting_for_video, OverlayStatus.Connected);
322 | } else if (VideoReaderExoplayer.VideoReaderEventMessageCode.VIDEO_PLAYING.equals(m)) {
323 | Log.d(TAG, "event: VIDEO_PLAYING");
324 | hideOverlay();
325 | }
326 | return false; // false to continue listening
327 | }
328 |
329 | private void showOverlay(int textId, OverlayStatus connected) {
330 | overlayView.show(textId, connected);
331 | updateWatermark();
332 | showSettingsButton();
333 | }
334 |
335 | private void hideOverlay() {
336 | overlayView.hide();
337 | updateWatermark();
338 | showSettingsButton();
339 | autoHideSettingsButton();
340 | }
341 |
342 | @Override
343 | protected void onStop() {
344 | super.onStop();
345 | Log.d(TAG, "APP - On Stop");
346 |
347 | mUsbMaskConnection.stop();
348 | mVideoReader.stop();
349 | usbConnected = false;
350 | }
351 |
352 | @Override
353 | protected void onPause() {
354 | super.onPause();
355 | Log.d(TAG, "APP - On Pause");
356 |
357 | mUsbMaskConnection.stop();
358 | mVideoReader.stop();
359 | usbConnected = false;
360 | }
361 |
362 | @Override
363 | protected void onDestroy() {
364 | super.onDestroy();
365 | Log.d(TAG, "APP - On Destroy");
366 |
367 | mUsbMaskConnection.stop();
368 | mVideoReader.stop();
369 | usbConnected = false;
370 | }
371 |
372 | @Override
373 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
374 | super.onActivityResult(requestCode, resultCode, data);
375 |
376 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
377 | boolean dataCollectionAccepted = preferences.getBoolean("dataCollectionAccepted", false);
378 |
379 | if (requestCode == 1) { // Data Collection agreement Activity
380 | if (resultCode == RESULT_OK && dataCollectionAccepted) {
381 | SentryAndroid.init(this, options -> options.setBeforeSend((event, hint) -> {
382 | if (SentryLevel.DEBUG.equals(event.getLevel()))
383 | return null;
384 | else
385 | return event;
386 | }));
387 | }
388 |
389 | }
390 | } //onActivityResult
391 |
392 | private void checkDataCollectionAgreement() {
393 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
394 | boolean dataCollectionAccepted = preferences.getBoolean("dataCollectionAccepted", false);
395 | boolean dataCollectionReplied = preferences.getBoolean("dataCollectionReplied", false);
396 | if (!dataCollectionReplied) {
397 | Intent intent = new Intent(this, DataCollectionAgreementPopupActivity.class);
398 | startActivityForResult(intent, 1);
399 | } else if (dataCollectionAccepted) {
400 | SentryAndroid.init(this, options -> options.setBeforeSend((event, hint) -> {
401 | if (SentryLevel.DEBUG.equals(event.getLevel()))
402 | return null;
403 | else
404 | return event;
405 | }));
406 | }
407 |
408 | }
409 |
410 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/OverlayStatus.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | public enum OverlayStatus {
4 | Connected,
5 | Disconnected,
6 | Error
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/OverlayView.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.ImageView;
7 | import android.widget.TextView;
8 |
9 | import androidx.constraintlayout.widget.ConstraintLayout;
10 |
11 | public class OverlayView extends ConstraintLayout {
12 | private final TextView textView;
13 | private final ImageView imageView;
14 |
15 | public OverlayView(Context context, AttributeSet attrs) {
16 | super(context, attrs);
17 |
18 | inflate(getContext(),R.layout.backdrop_view,this);
19 |
20 | textView = findViewById(R.id.backdrop_text);
21 |
22 | imageView = findViewById(R.id.backdrop_image);
23 | }
24 |
25 | public void hide(){
26 | setVisibility(View.GONE);
27 | }
28 |
29 | public void show(int textResourceId, OverlayStatus status){
30 | showInfo(getContext().getString(textResourceId), status);
31 | }
32 |
33 | private void showInfo(String text, OverlayStatus status){
34 |
35 | setVisibility(View.VISIBLE);
36 |
37 | textView.setText(text);
38 |
39 | int image = R.drawable.ic_goggles_white;
40 | switch(status){
41 | case Disconnected:
42 | image = R.drawable.ic_goggles_disconnected_white;
43 | break;
44 | case Error:
45 | image = R.drawable.ic_goggles_disconnected_red;
46 | break;
47 | }
48 |
49 | imageView.setImageResource(image);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/PerformancePreset.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | public class PerformancePreset {
4 | int h264ReaderMaxSyncFrameSize = 131072;
5 | int h264ReaderSampleTime = 10000;
6 | int exoPlayerMinBufferMs = 500;
7 | int exoPlayerMaxBufferMs = 2000;
8 | int exoPlayerBufferForPlaybackMs = 17;
9 | int exoPlayerBufferForPlaybackAfterRebufferMs = 17;
10 | DataSourceType dataSourceType = DataSourceType.INPUT_STREAM;
11 |
12 | private PerformancePreset(){
13 |
14 | }
15 |
16 | private PerformancePreset(int mH264ReaderMaxSyncFrameSize, int mH264ReaderSampleTime, int mExoPlayerMinBufferMs, int mExoPlayerMaxBufferMs, int mExoPlayerBufferForPlaybackMs, int mExoPlayerBufferForPlaybackAfterRebufferMs, DataSourceType mDataSourceType){
17 | h264ReaderMaxSyncFrameSize = mH264ReaderMaxSyncFrameSize;
18 | h264ReaderSampleTime = mH264ReaderSampleTime;
19 | exoPlayerMinBufferMs = mExoPlayerMinBufferMs;
20 | exoPlayerMaxBufferMs = mExoPlayerMaxBufferMs;
21 | exoPlayerBufferForPlaybackMs = mExoPlayerBufferForPlaybackMs;
22 | exoPlayerBufferForPlaybackAfterRebufferMs = mExoPlayerBufferForPlaybackAfterRebufferMs;
23 | dataSourceType = mDataSourceType;
24 | }
25 |
26 | static PerformancePreset getPreset(PresetType p) {
27 | switch (p) {
28 | case CONSERVATIVE:
29 | return new PerformancePreset(131072, 14000, 500, 2000, 34, 34, DataSourceType.INPUT_STREAM);
30 | case AGGRESSIVE:
31 | return new PerformancePreset(131072, 7000, 50, 2000, 17, 17, DataSourceType.INPUT_STREAM);
32 | case LEGACY:
33 | return new PerformancePreset(30720, 200, 32768, 65536, 0, 0, DataSourceType.BUFFERED_INPUT_STREAM);
34 | case LEGACY_BUFFERED:
35 | return new PerformancePreset(30720, 300, 32768, 65536, 34, 34, DataSourceType.BUFFERED_INPUT_STREAM);
36 | case DEFAULT:
37 | default:
38 | return new PerformancePreset(131072, 10000, 500, 2000, 17, 17, DataSourceType.INPUT_STREAM);
39 | }
40 | }
41 |
42 | public enum DataSourceType {
43 | INPUT_STREAM,
44 | BUFFERED_INPUT_STREAM
45 | }
46 |
47 | static PerformancePreset getPreset(String p) {
48 | switch (p) {
49 | case "conservative":
50 | return getPreset(PresetType.CONSERVATIVE);
51 | case "aggressive":
52 | return getPreset(PresetType.AGGRESSIVE);
53 | case "legacy":
54 | return getPreset(PresetType.LEGACY);
55 | case "new_legacy":
56 | return getPreset(PresetType.LEGACY_BUFFERED);
57 | case "default":
58 | default:
59 | return getPreset(PresetType.DEFAULT);
60 | }
61 | }
62 |
63 | public enum PresetType {
64 | DEFAULT,
65 | CONSERVATIVE,
66 | AGGRESSIVE,
67 | LEGACY,
68 | LEGACY_BUFFERED
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return "PerformancePreset{" +
74 | "h264ReaderMaxSyncFrameSize=" + h264ReaderMaxSyncFrameSize +
75 | ", h264ReaderSampleTime=" + h264ReaderSampleTime +
76 | ", exoPlayerMinBufferMs=" + exoPlayerMinBufferMs +
77 | ", exoPlayerMaxBufferMs=" + exoPlayerMaxBufferMs +
78 | ", exoPlayerBufferForPlaybackMs=" + exoPlayerBufferForPlaybackMs +
79 | ", exoPlayerBufferForPlaybackAfterRebufferMs=" + exoPlayerBufferForPlaybackAfterRebufferMs +
80 | ", dataSourceType=" + dataSourceType +
81 | '}';
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.os.Bundle;
4 | import android.view.MenuItem;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.appcompat.app.ActionBar;
8 | import androidx.appcompat.app.AppCompatActivity;
9 | import androidx.preference.PreferenceFragmentCompat;
10 |
11 | public class SettingsActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.settings_activity);
17 | if (savedInstanceState == null) {
18 | getSupportFragmentManager()
19 | .beginTransaction()
20 | .replace(R.id.settings, new SettingsFragment())
21 | .commit();
22 | }
23 | ActionBar actionBar = getSupportActionBar();
24 | if (actionBar != null) {
25 | actionBar.setDisplayHomeAsUpEnabled(true);
26 | }
27 | }
28 |
29 | @Override
30 | public boolean onOptionsItemSelected(@NonNull MenuItem item) {
31 |
32 | if (item.getItemId() == android.R.id.home) {
33 | this.finish();
34 | return true;
35 | }
36 |
37 | return super.onOptionsItemSelected(item);
38 | }
39 |
40 | public static class SettingsFragment extends PreferenceFragmentCompat {
41 | @Override
42 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
43 | setPreferencesFromResource(R.xml.root_preferences, rootKey);
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/UsbDeviceBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.hardware.usb.UsbDevice;
7 | import android.hardware.usb.UsbManager;
8 |
9 | public class UsbDeviceBroadcastReceiver extends BroadcastReceiver {
10 | private static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION";
11 | private final UsbDeviceListener listener;
12 |
13 | public UsbDeviceBroadcastReceiver(UsbDeviceListener listener ){
14 | this.listener = listener;
15 | }
16 |
17 | @Override
18 | public void onReceive(Context context, Intent intent) {
19 | String action = intent.getAction();
20 | if (ACTION_USB_PERMISSION.equals(action)) {
21 | UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
22 |
23 | if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
24 | if(device != null){
25 | listener.usbDeviceApproved(device);
26 | }
27 | }
28 | }
29 |
30 | if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
31 | listener.usbDeviceDetached();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/UsbDeviceListener.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.hardware.usb.UsbDevice;
4 |
5 | public interface UsbDeviceListener {
6 | void usbDeviceApproved(UsbDevice device);
7 | void usbDeviceDetached();
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.hardware.usb.UsbDevice;
4 | import android.hardware.usb.UsbDeviceConnection;
5 | import android.hardware.usb.UsbInterface;
6 |
7 | import java.io.IOException;
8 |
9 | import usb.AndroidUSBInputStream;
10 | import usb.AndroidUSBOutputStream;
11 |
12 | public class UsbMaskConnection {
13 |
14 | private final byte[] magicPacket = "RMVT".getBytes();
15 | private UsbDeviceConnection usbConnection;
16 | private UsbDevice device;
17 | private UsbInterface usbInterface;
18 | AndroidUSBInputStream mInputStream;
19 | AndroidUSBOutputStream mOutputStream;
20 | private boolean ready = false;
21 |
22 | public UsbMaskConnection() {
23 | }
24 |
25 | public void setUsbDevice(UsbDeviceConnection c, UsbDevice d) {
26 | usbConnection = c;
27 | device = d;
28 | usbInterface = device.getInterface(3);
29 |
30 | usbConnection.claimInterface(usbInterface,true);
31 |
32 | mOutputStream = new AndroidUSBOutputStream(usbInterface.getEndpoint(0), usbConnection);
33 | mInputStream = new AndroidUSBInputStream(usbInterface.getEndpoint(1), usbInterface.getEndpoint(0), usbConnection);
34 | ready = true;
35 | }
36 |
37 | public void start(){
38 | mOutputStream.write(magicPacket);
39 | }
40 |
41 | public void stop() {
42 | ready = false;
43 | try {
44 | if (mInputStream != null)
45 | mInputStream.close();
46 |
47 | if (mOutputStream != null)
48 | mOutputStream.close();
49 | } catch (IOException e) {
50 | e.printStackTrace();
51 | }
52 |
53 | if (usbConnection != null) {
54 | usbConnection.releaseInterface(usbInterface);
55 | usbConnection.close();
56 | }
57 | }
58 |
59 | public boolean isReady() {
60 | return ready;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/VersionPreference.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.util.AttributeSet;
7 |
8 | import androidx.preference.Preference;
9 |
10 | // From https://stackoverflow.com/a/20157577/877465
11 | public class VersionPreference extends Preference {
12 | public VersionPreference(Context context, AttributeSet attrs) {
13 | super(context, attrs);
14 | String versionName;
15 | final PackageManager packageManager = context.getPackageManager();
16 | if (packageManager != null) {
17 | try {
18 | PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
19 | versionName = packageInfo.versionName;
20 | } catch (PackageManager.NameNotFoundException e) {
21 | versionName = null;
22 | }
23 | setSummary(versionName);
24 | }
25 |
26 | setSelectable(false);
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java:
--------------------------------------------------------------------------------
1 | package com.fpvout.digiview;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.net.Uri;
6 | import android.os.Handler;
7 | import android.os.Looper;
8 | import android.os.Message;
9 | import android.util.Log;
10 | import android.view.SurfaceView;
11 |
12 | import androidx.constraintlayout.widget.ConstraintLayout;
13 | import androidx.preference.PreferenceManager;
14 |
15 | import com.google.android.exoplayer2.C;
16 | import com.google.android.exoplayer2.DefaultLoadControl;
17 | import com.google.android.exoplayer2.ExoPlaybackException;
18 | import com.google.android.exoplayer2.ExoPlayer;
19 | import com.google.android.exoplayer2.Format;
20 | import com.google.android.exoplayer2.MediaItem;
21 | import com.google.android.exoplayer2.Player;
22 | import com.google.android.exoplayer2.SimpleExoPlayer;
23 | import com.google.android.exoplayer2.extractor.Extractor;
24 | import com.google.android.exoplayer2.extractor.ExtractorsFactory;
25 | import com.google.android.exoplayer2.source.MediaSource;
26 | import com.google.android.exoplayer2.source.ProgressiveMediaSource;
27 | import com.google.android.exoplayer2.upstream.DataSource;
28 | import com.google.android.exoplayer2.upstream.DataSpec;
29 | import com.google.android.exoplayer2.util.NonNullApi;
30 | import com.google.android.exoplayer2.video.VideoListener;
31 |
32 | import usb.AndroidUSBInputStream;
33 |
34 | public class VideoReaderExoplayer {
35 | private static final String TAG = "DIGIVIEW";
36 | private Handler videoReaderEventListener;
37 | private SimpleExoPlayer mPlayer;
38 | static final String VideoPreset = "VideoPreset";
39 | private final SurfaceView surfaceView;
40 | private AndroidUSBInputStream inputStream;
41 | private UsbMaskConnection mUsbMaskConnection;
42 | private boolean zoomedIn;
43 | private final Context context;
44 | private PerformancePreset performancePreset = PerformancePreset.getPreset(PerformancePreset.PresetType.DEFAULT);
45 | static final String VideoZoomedIn = "VideoZoomedIn";
46 | private final SharedPreferences sharedPreferences;
47 |
48 | VideoReaderExoplayer(SurfaceView videoSurface, Context c) {
49 | surfaceView = videoSurface;
50 | context = c;
51 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c);
52 | }
53 |
54 | VideoReaderExoplayer(SurfaceView videoSurface, Context c, Handler v) {
55 | this(videoSurface, c);
56 | videoReaderEventListener = v;
57 | }
58 |
59 | public void setUsbMaskConnection(UsbMaskConnection connection) {
60 | mUsbMaskConnection = connection;
61 | inputStream = mUsbMaskConnection.mInputStream;
62 | }
63 |
64 | public void start() {
65 | zoomedIn = sharedPreferences.getBoolean(VideoZoomedIn, true);
66 | performancePreset = PerformancePreset.getPreset(sharedPreferences.getString(VideoPreset, "default"));
67 |
68 | DefaultLoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(performancePreset.exoPlayerMinBufferMs, performancePreset.exoPlayerMaxBufferMs, performancePreset.exoPlayerBufferForPlaybackMs, performancePreset.exoPlayerBufferForPlaybackAfterRebufferMs).build();
69 | mPlayer = new SimpleExoPlayer.Builder(context).setLoadControl(loadControl).build();
70 | mPlayer.setVideoSurfaceView(surfaceView);
71 | mPlayer.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
72 | mPlayer.setWakeMode(C.WAKE_MODE_LOCAL);
73 |
74 | DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, C.LENGTH_UNSET);
75 |
76 | Log.d(TAG, "preset: " + performancePreset);
77 |
78 | DataSource.Factory dataSourceFactory = () -> {
79 | switch (performancePreset.dataSourceType){
80 | case INPUT_STREAM:
81 | return (DataSource) new InputStreamDataSource(context, dataSpec, inputStream);
82 | case BUFFERED_INPUT_STREAM:
83 | default:
84 | return (DataSource) new InputStreamBufferedDataSource(context, dataSpec, inputStream);
85 | }
86 | };
87 |
88 | ExtractorsFactory extractorsFactory = () ->new Extractor[] {new H264Extractor(performancePreset.h264ReaderMaxSyncFrameSize, performancePreset.h264ReaderSampleTime)};
89 | MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory).createMediaSource(MediaItem.fromUri(Uri.EMPTY));
90 | mPlayer.setMediaSource(mediaSource);
91 |
92 | mPlayer.prepare();
93 | mPlayer.play();
94 | mPlayer.addListener(new ExoPlayer.EventListener() {
95 | @Override
96 | @NonNullApi
97 | public void onPlayerError(ExoPlaybackException error) {
98 | switch (error.type) {
99 | case ExoPlaybackException.TYPE_SOURCE:
100 | Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage());
101 | (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000);
102 | break;
103 | case ExoPlaybackException.TYPE_REMOTE:
104 | Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getSourceException().getMessage());
105 | break;
106 | case ExoPlaybackException.TYPE_RENDERER:
107 | Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getSourceException().getMessage());
108 | break;
109 | case ExoPlaybackException.TYPE_UNEXPECTED:
110 | Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getSourceException().getMessage());
111 | break;
112 | }
113 | }
114 |
115 | @Override
116 | public void onPlaybackStateChanged(@NonNullApi int state) {
117 | switch (state) {
118 | case Player.STATE_IDLE:
119 | case Player.STATE_READY:
120 | case Player.STATE_BUFFERING:
121 | break;
122 | case Player.STATE_ENDED:
123 | Log.d(TAG, "PLAYER_STATE - ENDED");
124 | sendEvent(VideoReaderEventMessageCode.WAITING_FOR_VIDEO); // let MainActivity know so it can hide watermark/show settings button
125 | (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000);
126 | break;
127 | }
128 | }
129 | });
130 |
131 | mPlayer.addVideoListener(new VideoListener() {
132 | @Override
133 | public void onRenderedFirstFrame() {
134 | Log.d(TAG, "PLAYER_RENDER - FIRST FRAME");
135 | sendEvent(VideoReaderEventMessageCode.VIDEO_PLAYING); // let MainActivity know so it can hide watermark/show settings button
136 | }
137 |
138 | @Override
139 | public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
140 | if (!zoomedIn) {
141 | ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams();
142 | params.dimensionRatio = width + ":" + height;
143 | surfaceView.setLayoutParams(params);
144 | }
145 | }
146 | });
147 | }
148 |
149 | private void sendEvent(VideoReaderEventMessageCode eventCode) {
150 | if (videoReaderEventListener != null) { // let MainActivity know so it can hide watermark/show settings button
151 | Message videoReaderEventMessage = new Message();
152 | videoReaderEventMessage.obj = eventCode;
153 | videoReaderEventListener.sendMessage(videoReaderEventMessage);
154 | }
155 | }
156 |
157 | public void toggleZoom() {
158 | zoomedIn = !zoomedIn;
159 |
160 | SharedPreferences.Editor preferencesEditor = sharedPreferences.edit();
161 | preferencesEditor.putBoolean(VideoZoomedIn, zoomedIn);
162 | preferencesEditor.apply();
163 |
164 | ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams();
165 |
166 | if (zoomedIn) {
167 | params.dimensionRatio = "";
168 | } else {
169 | if (mPlayer == null) return;
170 | Format videoFormat = mPlayer.getVideoFormat();
171 | if (videoFormat == null) return;
172 |
173 | params.dimensionRatio = videoFormat.width + ":" + videoFormat.height;
174 | }
175 |
176 | surfaceView.setLayoutParams(params);
177 | }
178 |
179 | public void zoomIn() {
180 | if (!zoomedIn) {
181 | toggleZoom();
182 | }
183 | }
184 |
185 | public void zoomOut() {
186 | if (zoomedIn) {
187 | toggleZoom();
188 | }
189 | }
190 |
191 | public void restart() {
192 | mPlayer.release();
193 |
194 | if (mUsbMaskConnection.isReady()) {
195 | mUsbMaskConnection.start();
196 | start();
197 | }
198 | }
199 |
200 | public void stop() {
201 | if (mPlayer != null)
202 | mPlayer.release();
203 | }
204 |
205 | public enum VideoReaderEventMessageCode {WAITING_FOR_VIDEO, VIDEO_PLAYING}
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/usb/AndroidUSBInputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019, Digi International Inc.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, you can obtain one at http://mozilla.org/MPL/2.0/.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | package usb;
17 |
18 | import java.io.IOException;
19 | import java.io.InputStream;
20 |
21 | import android.hardware.usb.UsbDeviceConnection;
22 | import android.hardware.usb.UsbEndpoint;
23 | import android.util.Log;
24 |
25 | /**
26 | * This class acts as a wrapper to read data from the USB Interface in Android
27 | * behaving like an {@code InputputStream} class.
28 | */
29 | public class AndroidUSBInputStream extends InputStream {
30 |
31 | private final String TAG = "USBInputStream";
32 | // Constants.
33 | private static final int OFFSET = 0;
34 | private static final int READ_TIMEOUT = 100;
35 |
36 | // Variables.
37 | private UsbDeviceConnection usbConnection;
38 |
39 | private UsbEndpoint receiveEndPoint;
40 | private final UsbEndpoint sendEndPoint;
41 |
42 | private boolean working = false;
43 |
44 |
45 | /**
46 | * Class constructor. Instantiates a new {@code AndroidUSBInputStream}
47 | * object with the given parameters.
48 | *
49 | * @param readEndpoint The USB end point to use to read data from.
50 | * @param connection The USB connection to use to read data from.
51 | *
52 | * @see UsbDeviceConnection
53 | * @see UsbEndpoint
54 | */
55 | public AndroidUSBInputStream( UsbEndpoint readEndpoint, UsbEndpoint sendEndpoint, UsbDeviceConnection connection) {
56 | this.usbConnection = connection;
57 | this.receiveEndPoint = readEndpoint;
58 | this.sendEndPoint = sendEndpoint;
59 | }
60 |
61 | @Override
62 | public int read() throws IOException {
63 | byte[] buffer = new byte[131072];
64 | return read(buffer, 0, buffer.length);
65 | }
66 |
67 | @Override
68 | public int read(byte[] buffer, int offset, int length) throws IOException {
69 | int receivedBytes = usbConnection.bulkTransfer(receiveEndPoint, buffer, buffer.length, READ_TIMEOUT);
70 | if (receivedBytes <= 0) {
71 | // send magic packet again; Would be great to handle this in UsbMaskConnection directly...
72 | Log.d(TAG, "received buffer empty, sending magic packet again...");
73 | usbConnection.bulkTransfer(sendEndPoint, "RMVT".getBytes(), "RMVT".getBytes().length, 2000);
74 | receivedBytes = usbConnection.bulkTransfer(receiveEndPoint, buffer, buffer.length, READ_TIMEOUT);
75 | }
76 | return receivedBytes;
77 | }
78 |
79 |
80 | @Override
81 | public void close() throws IOException {}
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/usb/AndroidUSBOutputStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019, Digi International Inc.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, you can obtain one at http://mozilla.org/MPL/2.0/.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | package usb;
17 |
18 | import android.hardware.usb.UsbDeviceConnection;
19 | import android.hardware.usb.UsbEndpoint;
20 |
21 | import java.io.IOException;
22 | import java.io.OutputStream;
23 | import java.util.concurrent.LinkedBlockingQueue;
24 |
25 | /**
26 | * This class acts as a wrapper to write data to the USB Interface in Android
27 | * behaving like an {@code OutputStream} class.
28 | */
29 | public class AndroidUSBOutputStream extends OutputStream {
30 |
31 | // Constants.
32 | private static final int WRITE_TIMEOUT = 2000;
33 |
34 | // Variables.
35 | private final UsbDeviceConnection usbConnection;
36 |
37 | private final UsbEndpoint sendEndPoint;
38 |
39 | private LinkedBlockingQueue writeQueue;
40 |
41 | private final boolean streamOpen = true;
42 |
43 | /**
44 | * Class constructor. Instantiates a new {@code AndroidUSBOutputStream}
45 | * object with the given parameters.
46 | *
47 | * @param writeEndpoint The USB end point to use to write data to.
48 | * @param connection The USB connection to use to write data to.
49 | *
50 | * @see UsbDeviceConnection
51 | * @see UsbEndpoint
52 | */
53 | public AndroidUSBOutputStream(UsbEndpoint writeEndpoint, UsbDeviceConnection connection) {
54 | this.usbConnection = connection;
55 | this.sendEndPoint = writeEndpoint;
56 | }
57 |
58 | /*
59 | * (non-Javadoc)
60 | * @see java.io.OutputStream#write(int)
61 | */
62 | @Override
63 | public void write(int oneByte) {
64 | write(new byte[] {(byte)oneByte});
65 | }
66 |
67 | /*
68 | * (non-Javadoc)
69 | * @see java.io.OutputStream#write(byte[])
70 | */
71 | @Override
72 | public void write(byte[] buffer) {
73 | write(buffer, 0, buffer.length);
74 | }
75 |
76 | /*
77 | * (non-Javadoc)
78 | * @see java.io.OutputStream#write(byte[], int, int)
79 | */
80 | @Override
81 | public void write(byte[] buffer, int offset, int count) {
82 | usbConnection.bulkTransfer(sendEndPoint, buffer, count, WRITE_TIMEOUT);
83 |
84 | }
85 |
86 |
87 | @Override
88 | public void close() throws IOException {
89 | super.close();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/usb/CircularByteBuffer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019, Digi International Inc.
3 | *
4 | * This Source Code Form is subject to the terms of the Mozilla Public
5 | * License, v. 2.0. If a copy of the MPL was not distributed with this
6 | * file, you can obtain one at http://mozilla.org/MPL/2.0/.
7 | *
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 | */
16 | package usb;
17 |
18 | /**
19 | * Helper class used to store data bytes as a circular buffer.
20 | */
21 | public class CircularByteBuffer {
22 |
23 | // Variables.
24 | private byte[] buffer;
25 |
26 | private int readIndex;
27 | private int writeIndex;
28 |
29 | private boolean empty = true;
30 |
31 | /**
32 | * Instantiates a new {@code CircularByteBuffer} with the given capacity
33 | * in bytes.
34 | *
35 | * @param size Circular byte buffer size in bytes.
36 | *
37 | * @throws IllegalArgumentException if {@code size < 1}.
38 | */
39 | public CircularByteBuffer(int size) {
40 | if (size < 1)
41 | throw new IllegalArgumentException("Buffer size must be greater than 0.");
42 |
43 | buffer = new byte[size];
44 | readIndex = 0;
45 | writeIndex = 0;
46 | }
47 |
48 | /**
49 | * Writes the given amount of bytes to the circular byte buffer.
50 | *
51 | * @param data Bytes to write.
52 | * @param offset Offset inside data where bytes to write start.
53 | * @param numBytes Number of bytes to write.
54 | * @return The number of bytes actually written.
55 | *
56 | * @throws IllegalArgumentException if {@code offset < 0} or
57 | * if {@code numBytes < 1}.
58 | * @throws NullPointerException if {@code data == null}.
59 | *
60 | * @see #read(byte[], int, int)
61 | * @see #skip(int)
62 | */
63 | public synchronized int write(byte[] data, int offset, int numBytes) {
64 | if (data == null)
65 | throw new NullPointerException("Data cannot be null.");
66 | if (offset < 0)
67 | throw new IllegalArgumentException("Offset cannot be negative.");
68 | if (numBytes < 1)
69 | throw new IllegalArgumentException("Number of bytes to write must be greater than 0.");
70 |
71 | // Check if there are enough bytes to write.
72 | int availableBytes = data.length - offset;
73 | if (numBytes > availableBytes)
74 | numBytes = availableBytes;
75 |
76 | // Check where we should start writing.
77 | if (numBytes < buffer.length - getWriteIndex()) {
78 | System.arraycopy(data, offset, buffer, getWriteIndex(), numBytes);
79 | writeIndex = getWriteIndex() + numBytes;
80 | } else {
81 | System.arraycopy(data, offset, buffer, getWriteIndex(), buffer.length - getWriteIndex());
82 | System.arraycopy(data, offset + buffer.length-getWriteIndex(), buffer, 0, numBytes - (buffer.length - getWriteIndex()));
83 | writeIndex = numBytes - (buffer.length-getWriteIndex());
84 | if (getReadIndex() < getWriteIndex())
85 | readIndex = getWriteIndex();
86 | }
87 |
88 | // Check if we were able to write all the bytes.
89 | if (numBytes > getCapacity())
90 | numBytes = getCapacity();
91 |
92 | empty = false;
93 | return numBytes;
94 | }
95 |
96 | /**
97 | * Reads the given amount of bytes to the given array from the circular byte
98 | * buffer.
99 | *
100 | * @param data Byte buffer to place read bytes in.
101 | * @param offset Offset inside data to start placing read bytes in.
102 | * @param numBytes Number of bytes to read.
103 | * @return The number of bytes actually read.
104 | *
105 | * @throws IllegalArgumentException if {@code offset < 0} or
106 | * if {@code numBytes < 1}.
107 | * @throws NullPointerException if {@code data == null}.
108 | *
109 | * @see #skip(int)
110 | * @see #write(byte[], int, int)
111 | */
112 | public synchronized int read(byte[] data, int offset, int numBytes) {
113 | if (data == null)
114 | throw new NullPointerException("Data cannot be null.");
115 | if (offset < 0)
116 | throw new IllegalArgumentException("Offset cannot be negative.");
117 | if (numBytes < 1)
118 | throw new IllegalArgumentException("Number of bytes to read must be greater than 0.");
119 |
120 | // If we are empty, return 0.
121 | if (empty)
122 | return 0;
123 |
124 | // If we try to place bytes in an index bigger than buffer index, return 0 read bytes.
125 | if (offset >= data.length)
126 | return 0;
127 |
128 | if (data.length - offset < numBytes)
129 | return read(data, offset, data.length - offset);
130 | if (availableToRead() < numBytes)
131 | return read(data, offset, availableToRead());
132 | if (numBytes < buffer.length - getReadIndex()){
133 | System.arraycopy(buffer, getReadIndex(), data, offset, numBytes);
134 | readIndex = getReadIndex() + numBytes;
135 | } else {
136 | System.arraycopy(buffer, getReadIndex(), data, offset, buffer.length - getReadIndex());
137 | System.arraycopy(buffer, 0, data, offset + buffer.length - getReadIndex(), numBytes - (buffer.length - getReadIndex()));
138 | readIndex = numBytes-(buffer.length - getReadIndex());
139 | }
140 |
141 | // If we have read all bytes, set the buffer as empty.
142 | if (readIndex == writeIndex)
143 | empty = true;
144 |
145 | return numBytes;
146 | }
147 |
148 | /**
149 | * Skips the given number of bytes from the circular byte buffer.
150 | *
151 | * @param numBytes Number of bytes to skip.
152 | * @return The number of bytes actually skipped.
153 | *
154 | * @throws IllegalArgumentException if {@code numBytes < 1}.
155 | *
156 | * @see #read(byte[], int, int)
157 | * @see #write(byte[], int, int)
158 | */
159 | public synchronized int skip(int numBytes) {
160 | if (numBytes < 1)
161 | throw new IllegalArgumentException("Number of bytes to skip must be greater than 0.");
162 |
163 | // If we are empty, return 0.
164 | if (empty)
165 | return 0;
166 |
167 | if (availableToRead() < numBytes)
168 | return skip(availableToRead());
169 | if (numBytes < buffer.length - getReadIndex())
170 | readIndex = getReadIndex() + numBytes;
171 | else
172 | readIndex = numBytes - (buffer.length - getReadIndex());
173 |
174 | // If we have skipped all bytes, set the buffer as empty.
175 | if (readIndex == writeIndex)
176 | empty = true;
177 |
178 | return numBytes;
179 | }
180 |
181 | /**
182 | * Returns the available number of bytes to read from the byte buffer.
183 | *
184 | * @return The number of bytes in the buffer available for reading.
185 | *
186 | * @see #getCapacity()
187 | * @see #read(byte[], int, int)
188 | */
189 | public int availableToRead() {
190 | if (empty)
191 | return 0;
192 | if (getReadIndex() < getWriteIndex())
193 | return (getWriteIndex() - getReadIndex());
194 | else
195 | return (buffer.length - getReadIndex() + getWriteIndex());
196 | }
197 |
198 | /**
199 | * Returns the current read index.
200 | *
201 | * @return readIndex The current read index.
202 | */
203 | private int getReadIndex() {
204 | return readIndex;
205 | }
206 |
207 | /**
208 | * Returns the current write index.
209 | *
210 | * @return writeIndex The current write index.
211 | */
212 | private int getWriteIndex() {
213 | return writeIndex;
214 | }
215 |
216 | /**
217 | * Returns the circular byte buffer capacity.
218 | *
219 | * @return The circular byte buffer capacity.
220 | */
221 | public int getCapacity() {
222 | return buffer.length;
223 | }
224 |
225 | /**
226 | * Clears the circular buffer.
227 | */
228 | public void clearBuffer() {
229 | empty = true;
230 | readIndex = 0;
231 | writeIndex = 0;
232 | }
233 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_goggles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-hdpi/ic_goggles.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_goggles_disconnected_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-hdpi/ic_goggles_disconnected_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_goggles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-hdpi/ic_goggles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_goggles_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-hdpi/ic_goggles_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-hdpi/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_goggles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-mdpi/ic_goggles.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_goggles_disconnected_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-mdpi/ic_goggles_disconnected_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_goggles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-mdpi/ic_goggles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_goggles_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-mdpi/ic_goggles_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-mdpi/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_goggles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xhdpi/ic_goggles.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_goggles_disconnected_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xhdpi/ic_goggles_disconnected_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_goggles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xhdpi/ic_goggles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_goggles_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xhdpi/ic_goggles_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xhdpi/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_goggles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_goggles.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_goggles_disconnected_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_goggles_disconnected_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_goggles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_goggles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_goggles_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_goggles_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_gogles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_gogles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxhdpi/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_goggles.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxxhdpi/ic_goggles.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_goggles_disconnected_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxxhdpi/ic_goggles_disconnected_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_goggles_disconnected_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxxhdpi/ic_goggles_disconnected_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_goggles_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxxhdpi/ic_goggles_white.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/drawable-xxxhdpi/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
11 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | -
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/font/gidolinya.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/font/gidolinya.otf
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
22 |
35 |
36 |
44 |
45 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/backdrop_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
17 |
18 |
29 |
30 |
40 |
41 |
42 |
43 |
49 |
50 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/datacollection_agreement_popup.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
21 |
22 |
35 |
36 |
42 |
43 |
44 |
47 |
48 |
50 |
54 |
55 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/settings_activity.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DigiView\nfeed
4 | Warte auf USB Gerät…
5 | USB Gerät genehmigt
6 | USB Gerät getrennt\n\nWarte auf USB Gerät…
7 | USB Gerät gefunden.
8 | Warte auf Video…
9 | Datenerfassungsvereinbarung
10 | Absturzdaten an das DigiView-Team melden?
11 | Diese App wird wahrscheinlich seltener abstürzen als deine Drohne.\n Falls doch, würden wir gerne davon erfahren!\n\nWir verwenden Sentry, um Fehler zu verfolgen und diese App zu verbessern.
12 | Zustimmen
13 | Ablehnen
14 |
15 | Einstellungen
16 |
17 |
18 | Leistung
19 | Privatsphäre
20 | Video-Voreinstellung
21 | Standard
22 | Konservativ
23 | Aggressiv
24 | Legacy
25 | Analytics akivieren
26 | Datenschutzbestimmungen
27 | Sehe, welche Daten wir sammeln und wie wir sie verwenden
28 | Videoplayer
29 | Vollbild
30 | Kann auch durch zweimaliges Tippen auf den Videoplayer umgeschaltet werden.
31 | DigiView-Wasserzeichen anzeigen
32 | Links
33 | Unsere Website
34 | Chatte mit uns und anderen DigiView-Benutzern
35 | DigiView Web App
36 | Funktioniert mit Chrome und Chrome-basierten Browsern, auch auf Android!
37 | Unser gesamter Code ist Open Source
38 | Über
39 | App Version
40 | Copyright
41 | Open-Source Lizenz
42 | MIT Lizenz
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-es/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DigiView\nseñal
4 | Esperando dispositivo USB…
5 | Dispositivo USB aprobado
6 | Dispositivo USB desconectado\n\nEsperando dispositivo USB…
7 | Dispositivo USB encontrado.
8 | Esperando video…
9 | Acuerdo de recopilacion de datos
10 | ¿Enviar datos de fallos al equipo de DigiView?
11 | Esta aplicacion probablemente fallara menos que tu dron. \n ¡Aunque, si ocurre un bloqueo, nos gustaria saberlo! \n \nUtilizamos Sentry para rastrear errores y mejorar esta aplicacion.
12 | Aceptar
13 | Rechazar
14 | Ajustes
15 |
16 |
17 | Rendimiento
18 | Privacidad
19 | Video Preestablecido
20 | Defecto
21 | Conservador
22 | Agresivo
23 | Legacy
24 | Habilitar analitica
25 | Politica de privacidad
26 | Vea que datos recopilamos y como los usamos
27 | Reproductor de video
28 | Pantall completa
29 | También se puede alternar tocando dos veces o pellizcando el reproductor de video.
30 | Muestra marca de agua de DigiView
31 | Enlaces
32 | Nuestro sitio web
33 | Chatea con nosotros y otros usuarios de DigiView
34 | DigiView Web App
35 | Funciona en Chrome y navegadores basados en Chrome, ¡incluso en Android!
36 | Todo nuestro codigo es open source
37 | Sobre la app
38 | Version de la App
39 | Copyright
40 | Licencia Open-Source
41 | Licencia MIT
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DigiView\nfeed
4 | Attente de périphérique USB…
5 | Périphérique USB approuvé
6 | Périphérique USB détaché\n\nAttente de périphérique USB…
7 | Périphérique USB trouvé.
8 | En attente de la vidéo…
9 | Collection des données
10 | Partager les données de crashs avec l\'équipe de DigiView ?
11 | Cette application crashera sûrement moins que votre drone. Mais si jamais l\'application plante, On aimerait bien le savoir ! L\'équipe utilise Sentry pour tracker les bugs et rendre cette application meilleur.
12 | Accepter
13 | Refuser
14 | Paramètres
15 | Performance
16 | Vie Privée
17 | Preset Vidéo
18 | Default
19 | Conservative
20 | Aggressive
21 | Legacy
22 | Activer les Analytics
23 | politique de confidentialité
24 | Voyez quelles données nous recoltons et comment nous les utilisons.
25 | Lecteur Vidéo
26 | Plein Ecran
27 | peut aussi être activer en double tapant ou en pincant le lecteur vidéo.
28 | Afficher le logo DigiView en filigrane
29 | Liens
30 | Notre site internet
31 | Venez discuter avec nous sur Discord
32 | L\'application web DigiView
33 | Fonctionne sur Chrome et les navigateurs basés sur Chrome, même sur Android !
34 | Tout notre code est open-source
35 | À Propos
36 | Version de l\'application
37 | Copyright
38 | License Open-Source
39 | License MIT
40 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DigiView\nfeed
4 | Aguardando pelo dispositivo USB…
5 | Dispositivo USB aprovado
6 | Dispositivo USB desconectado\n\nAguardando pelo dispositivo USB…
7 | Dispositivo USB encontrado.
8 | Aguardando video…
9 | Acordo de coleta de dados
10 | Negar
11 | Aceitar
12 | Relatar dados de falha para o time da DigiView?
13 | Esse app provavelmente vai cair menos do que o seu drone.\n Mesmo assim, se ocorrer um erro gostaríamos de saber!\n\nUtilizamos Sentry para rastrear "bugs" e assim melhorar esse app.
14 | Configurações
15 |
16 |
17 | Performance
18 | Privado
19 | Definições de Video
20 | Padrão
21 | Conservador
22 | Agressivo
23 | Legado
24 | Ativar Análise de Dados
25 | Política de Privacidade
26 | Veja os dados que coletamos e como os utilizamos
27 | Reprodução Vídeo
28 | Tela Cheia
29 | Também pode ser acionada com duplo clique ou com gesto de pinça na tela de reprodução do video.
30 | Mostrar marca d\'água DigiView
31 | Links
32 | Nosso Website
33 | Venha conversar conosco e com outros usuários DigiView
34 | DigiView Web App
35 | Funciona no Chrome e navegadores baseados no Chrome, mesmo no Android!
36 | Todo o nosso código é "open source"
37 | Sobre
38 | Versão App
39 | Direitos Autorais
40 | Licença Open-Source
41 | Licença MIT
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DigiView\nfeed
4 | 等待USB设备…
5 | USB设备已批准
6 | USB设备已断开\n\n等待USB设备…
7 | 已发现USB设备.
8 | 等待视频…
9 | 数据收集协议
10 | 向DigiView团队报告崩溃数据?
11 | 该程序崩溃的次数可能比您炸机次数还少。\n 但即使发生崩溃,我们也想知道!\n\n我们使用Sentry跟踪错误并持续改进此应用程序。
12 | 拒绝
13 | 同意
14 | 设置
15 |
16 |
17 | 性能
18 | 隐私
19 | 视频预设
20 | 默认
21 | 保守
22 | 激进
23 | 传统
24 | 开启数据分析
25 | 隐私策略
26 | 查看我们收集数据的内容以及我们如何使用它们
27 | 视频播放器
28 | 全屏
29 | 也可以通过在视频播放器上点按两次或双指捏合来进行切换。
30 | 显示 DigiView 水印
31 | 相关链接
32 | 我们的网站
33 | 与我们和其他 DigiView 用户聊天
34 | DigiView Web应用
35 | 可以在Chrome以及基于Chromium核心的浏览器中使用,甚至可以在Android上运行!
36 | 我们所有的代码都是开源的
37 | 关于
38 | 应用程序版本
39 | 版权所有
40 | 开源证书
41 | MIT License
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @string/video_preset_default
5 | - @string/video_preset_conservative
6 | - @string/video_preset_aggressive
7 | - @string/video_preset_legacy
8 | - @string/video_preset_legacy_buffered
9 |
10 |
11 |
12 | - default
13 | - conservative
14 | - aggressive
15 | - legacy
16 | - legacy_buffered
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #80000000
5 | #FFFFFFFF
6 | #4776e6
7 | #5A6CE6
8 | #8e54e9
9 | #7C5CE8
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DigiView
3 | DigiView\nfeed
4 | Please Connect Goggles
5 | USB device approved
6 | Goggles Disconnected
7 | Goggles Connected
8 | Waiting For Video…
9 | Data Collection Agreement
10 | No Thanks
11 | Sure!
12 | Report crash data to the DigiView team?
13 | This app will probably crash less than your drone, but if it does, we\'d like to know!
14 | Settings
15 |
16 |
17 | Performance
18 | Privacy
19 | Video Preset
20 | Default
21 | Conservative
22 | Aggressive
23 | Legacy
24 | Legacy Buffered
25 | Enable Analytics
26 | Privacy Policy
27 | See what data we collect and how we use it
28 | Video Player
29 | Full Screen
30 | Can also be toggled by double-tapping or pinching on the video player.
31 | Show DigiView watermark
32 | Links
33 | Our Website
34 | Come chat with us and other DigiView users
35 | DigiView Web App
36 | Works in Chrome and Chrome-based browsers, even on Android!
37 | All our code is open source
38 | About
39 | App Version
40 | Copyright
41 | Open-Source License
42 | MIT License
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/root_preferences.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
42 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
58 |
59 |
60 |
63 |
66 |
67 |
68 |
71 |
74 |
75 |
76 |
79 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
94 |
95 |
98 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:4.1.3"
9 |
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fpvout/DigiView-Android/42ae75d2eab0fffe85d7016a8f79091f2ebcde36/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 21 03:24:22 CEST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "DigiView"
--------------------------------------------------------------------------------