├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── V.png
├── app-debug.apk
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── help-de.md
│ │ └── help.md
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── de
│ │ │ └── vier_bier
│ │ │ └── habpanelviewer
│ │ │ ├── AdminReceiver.java
│ │ │ ├── AppRestartingExceptionHandler.java
│ │ │ ├── ClientWebView.java
│ │ │ ├── Constants.java
│ │ │ ├── EmptyActivity.java
│ │ │ ├── IntroActivity.java
│ │ │ ├── LogActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── NetworkTracker.java
│ │ │ ├── PermissionUtil.java
│ │ │ ├── PermissionValidatingActivity.java
│ │ │ ├── ResourcesUtil.java
│ │ │ ├── ScreenCapturer.java
│ │ │ ├── ScreenControllingActivity.java
│ │ │ ├── StartActivity.java
│ │ │ ├── TrackShutdownService.java
│ │ │ ├── UiUtil.java
│ │ │ ├── command
│ │ │ ├── AdminHandler.java
│ │ │ ├── BluetoothHandler.java
│ │ │ ├── Command.java
│ │ │ ├── CommandQueue.java
│ │ │ ├── FlashHandler.java
│ │ │ ├── ICommandHandler.java
│ │ │ ├── InternalCommandHandler.java
│ │ │ ├── NotificationHandler.java
│ │ │ ├── ScreenHandler.java
│ │ │ ├── TtsHandler.java
│ │ │ ├── VolumeHandler.java
│ │ │ ├── WebViewHandler.java
│ │ │ └── log
│ │ │ │ ├── CommandLog.java
│ │ │ │ ├── CommandLogActivity.java
│ │ │ │ └── CommandLogClient.java
│ │ │ ├── connection
│ │ │ ├── ConnectionStatistics.java
│ │ │ ├── OkHttpClientFactory.java
│ │ │ └── ssl
│ │ │ │ ├── CertificateManager.java
│ │ │ │ └── LocalTrustManager.java
│ │ │ ├── db
│ │ │ ├── AppDatabase.java
│ │ │ ├── Credential.java
│ │ │ ├── CredentialDao.java
│ │ │ └── CredentialManager.java
│ │ │ ├── help
│ │ │ └── HelpActivity.java
│ │ │ ├── openhab
│ │ │ ├── FutureState.java
│ │ │ ├── ISseConnectionListener.java
│ │ │ ├── ISseListener.java
│ │ │ ├── IStateUpdateListener.java
│ │ │ ├── ISubscriptionListener.java
│ │ │ ├── IUrlListener.java
│ │ │ ├── ItemState.java
│ │ │ ├── OpenhabSseConnection.java
│ │ │ ├── RestClient.java
│ │ │ ├── ServerConnection.java
│ │ │ ├── ServerDiscovery.java
│ │ │ ├── SseConnection.java
│ │ │ └── average
│ │ │ │ ├── Average.java
│ │ │ │ ├── AveragePropagator.java
│ │ │ │ ├── FloatAverage.java
│ │ │ │ ├── IStatePropagator.java
│ │ │ │ └── Sample.java
│ │ │ ├── preferences
│ │ │ ├── AutocompleteTextPreference.java
│ │ │ ├── ItemValidator.java
│ │ │ ├── PreferenceActivity.java
│ │ │ ├── PreferenceCallback.java
│ │ │ ├── PreferenceFragment.java
│ │ │ ├── PreferenceUtil.java
│ │ │ ├── PreferencesBrowser.java
│ │ │ ├── PreferencesCamera.java
│ │ │ ├── PreferencesConnected.java
│ │ │ ├── PreferencesConnection.java
│ │ │ ├── PreferencesMain.java
│ │ │ ├── PreferencesMotion.java
│ │ │ ├── PreferencesNoiseLevel.java
│ │ │ ├── PreferencesOther.java
│ │ │ ├── PreferencesReporting.java
│ │ │ └── PreferencesUi.java
│ │ │ ├── reporting
│ │ │ ├── AbstractAveragingDeviceMonitor.java
│ │ │ ├── AbstractDeviceMonitor.java
│ │ │ ├── AccelerometerMonitor.java
│ │ │ ├── BatteryMonitor.java
│ │ │ ├── BrightnessMonitor.java
│ │ │ ├── ConnectedIndicator.java
│ │ │ ├── DockingStateMonitor.java
│ │ │ ├── IDeviceMonitor.java
│ │ │ ├── NoiseLevelMonitor.java
│ │ │ ├── PressureMonitor.java
│ │ │ ├── ProximityMonitor.java
│ │ │ ├── ScreenMonitor.java
│ │ │ ├── SensorMissingException.java
│ │ │ ├── TemperatureMonitor.java
│ │ │ ├── VolumeMonitor.java
│ │ │ └── motion
│ │ │ │ ├── AbstractCameraImpl.java
│ │ │ │ ├── Camera.java
│ │ │ │ ├── CameraException.java
│ │ │ │ ├── CameraImplNone.java
│ │ │ │ ├── CameraImplV1.java
│ │ │ │ ├── CameraImplV2.java
│ │ │ │ ├── Comparer.java
│ │ │ │ ├── ICamera.java
│ │ │ │ ├── IMotionDetector.java
│ │ │ │ ├── IMotionListener.java
│ │ │ │ ├── LumaData.java
│ │ │ │ ├── MotionDetector.java
│ │ │ │ ├── MotionReporter.java
│ │ │ │ └── MotionVisualizer.java
│ │ │ └── status
│ │ │ ├── ApplicationStatus.java
│ │ │ ├── StatusInfoActivity.java
│ │ │ └── StatusItem.java
│ └── res
│ │ ├── drawable-mdpi
│ │ ├── book.png
│ │ ├── browser.png
│ │ ├── commanding.png
│ │ ├── configuration.png
│ │ ├── credentials.png
│ │ ├── database.png
│ │ ├── logo.png
│ │ ├── ready.png
│ │ ├── reporting.png
│ │ └── server.png
│ │ ├── drawable
│ │ ├── activity_main_border.xml
│ │ ├── ic_access_time_black_24dp.xml
│ │ ├── ic_arrow_upward_black_24dp.xml
│ │ ├── ic_baseline_enhanced_encryption_24.xml
│ │ ├── ic_clear_all_black_24dp.xml
│ │ ├── ic_delete_black_24dp.xml
│ │ ├── ic_forum_black_24dp.xml
│ │ ├── ic_help_outline_black_24dp.xml
│ │ ├── ic_info_outline_black_24dp.xml
│ │ ├── ic_play_arrow_black_24dp.xml
│ │ ├── ic_preferences_black_24dp.xml
│ │ ├── ic_replay_black_24dp.xml
│ │ ├── ic_search_black_24dp.xml
│ │ ├── ic_share_black_24dp.xml
│ │ ├── ic_view_headline_black_24dp.xml
│ │ ├── ic_vpn_key_black_24dp.xml
│ │ ├── ic_wiki_black_24dp.xml
│ │ └── side_nav_bar.xml
│ │ ├── layout
│ │ ├── activity_commandlog.xml
│ │ ├── activity_empty.xml
│ │ ├── activity_help.xml
│ │ ├── activity_intro_discover.xml
│ │ ├── activity_log.xml
│ │ ├── activity_main.xml
│ │ ├── activity_main_content.xml
│ │ ├── activity_preferences.xml
│ │ ├── activity_start.xml
│ │ ├── activity_statusinfo.xml
│ │ ├── dialog_credentials.xml
│ │ ├── dialog_master_password.xml
│ │ ├── dialog_scrollable_text.xml
│ │ ├── navheader_main.xml
│ │ ├── row_commandlog.xml
│ │ └── toolbar.xml
│ │ ├── menu
│ │ ├── activity_main_drawer.xml
│ │ ├── commandlog_toolbar_menu.xml
│ │ ├── help_toolbar_menu.xml
│ │ ├── log_menu.xml
│ │ ├── preferences_toolbar_menu.xml
│ │ └── webview_context_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ └── ic_launcher.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_foreground.png
│ │ ├── raw-de
│ │ └── releasenotes.txt
│ │ ├── raw
│ │ ├── mytruststore.bks
│ │ └── releasenotes.txt
│ │ ├── values-de
│ │ ├── arrays.xml
│ │ └── strings.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ └── strings.xml
│ │ └── xml
│ │ ├── device_admin.xml
│ │ ├── network_security_config.xml
│ │ ├── preferences_accelerometer.xml
│ │ ├── preferences_battery.xml
│ │ ├── preferences_brightness.xml
│ │ ├── preferences_browser.xml
│ │ ├── preferences_camera.xml
│ │ ├── preferences_connected.xml
│ │ ├── preferences_connection.xml
│ │ ├── preferences_docking_state.xml
│ │ ├── preferences_main.xml
│ │ ├── preferences_motion.xml
│ │ ├── preferences_noise_level.xml
│ │ ├── preferences_other.xml
│ │ ├── preferences_pressure.xml
│ │ ├── preferences_proximity.xml
│ │ ├── preferences_reporting.xml
│ │ ├── preferences_restart.xml
│ │ ├── preferences_screen.xml
│ │ ├── preferences_temperature.xml
│ │ ├── preferences_ui.xml
│ │ ├── preferences_usage.xml
│ │ └── preferences_volume.xml
│ └── test
│ └── java
│ └── de
│ └── vier_bier
│ └── habpanelviewer
│ └── openhab
│ ├── OpenhabSseConnectionTest.java
│ └── SseConnectionTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── habpanelviewer.iml
├── issue_template.md
├── logo.xcf
├── logo2.xcf
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: vbier
7 |
8 | ---
9 |
10 | **Before reporting bugs, make sure you have searched for your problem in the openHAB community thread at: https://community.openhab.org/t/habpanelviewer-0-9-25/34112**
11 |
12 | **Describe the bug**
13 | A clear and concise description of what the bug is.
14 |
15 | **To Reproduce**
16 | Steps to reproduce the behavior:
17 | 1. Go to '...'
18 | 2. Click on '....'
19 | 3. Scroll down to '....'
20 | 4. See error
21 |
22 | **Expected behavior**
23 | A clear and concise description of what you expected to happen.
24 |
25 | **Screenshots**
26 | If applicable, add screenshots to help explain your problem.
27 |
28 | **Smartphone/Tablet (please complete the following information):**
29 | - Device: [e.g. iPhone6]
30 | - OS: [e.g. Android 4.5]
31 | - Version [e.g. 0.9.24]
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: vbier
7 |
8 | ---
9 |
10 | **Before asking for a feature, make sure you have searched the openHAB community thread at: https://community.openhab.org/t/habpanelviewer-0-9-25/34112. Check if the requested feature was already discussed there.**
11 |
12 | **Is your feature request related to a problem? Please describe.**
13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
14 |
15 | **Describe the solution you'd like**
16 | A clear and concise description of what you want to happen.
17 |
18 | **Describe alternatives you've considered**
19 | A clear and concise description of any alternative solutions or features you've considered.
20 |
21 | **Additional context**
22 | Add any other context or screenshots about the feature request here.
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .gradle
3 | build
4 | local.properties
5 | app/src/test/java/de/vier_bier/habpanelviewer/openhab/PrivateSseConnectionTest.java
6 | app/src/test/resources/keystore2
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
HABPanelViewer
2 |
3 | #### At the end of 2021, I have stopped maintenance and development of the app.
4 |
5 | An openHAB integrated kiosk browser.
6 |
7 | #### Version 0.9.26 is the last version to work with openHAB 2.
8 | #### 0.9.27 works with openHAB 3 as default, but can be changed to openHAB 2 in the preferences.
9 |
10 |
11 | [
](https://f-droid.org/packages/de.vier_bier.habpanelviewer/)
14 |
15 | Main features:
16 | - **mDNS server discovery**: finds the openHAB server on your network by using mDNS discovery
17 | - **launcher functionality**: the app can be set as launcher, thus starting automatically with the tablet
18 | - **app shortcut**: allows to start an app from the main menu
19 | - **device control**: allows to control different device features via an openHAB item
20 | - **motion detection**: reports motion to openHAB
21 | - **device sensor value reporting**: reports device sensor values to openHAB
22 |
23 | The app does support android from SDK version 19, which means it will run on devices running Android 4.4+ (Kitkat).
24 |
25 | For further details see the app's help file.
26 |
27 | The app uses the following libraries:
28 | - **com.github.heremaps:oksse** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
29 | - **com.squareup.okhttp3:okhttp** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
30 | - **com.burgstaller:okhttp-digest** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
31 | - **com.jakewharton:process-phoenix** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
32 | - **org.greenrobot:eventbus** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
33 | - **com.github.mukeshsolanki:MarkdownView-Android** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
34 | - **com.github.apl-devs:appintro** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
35 | - **com.github.hedzr:android-file-chooser** licensed under the Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
36 |
37 | The app uses code from the following projects:
38 | - LogActivity is based on the LogActivity from the openHAB client for Android (https://github.com/openhab/openhab-android)
39 |
40 | The app uses following icons:
41 | - Icons made by Webalys Freebies from www.flaticon.com is licensed by CC 3.0 BY
42 | - Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
43 | - Icons made by itim2101 from www.flaticon.com is licensed by CC 3.0 BY
44 | - Icons made by Lucy G from www.flaticon.com is licensed by CC 3.0 BY
--------------------------------------------------------------------------------
/V.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/V.png
--------------------------------------------------------------------------------
/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app-debug.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | app.iml
3 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | defaultConfig {
6 | applicationId "de.vier_bier.habpanelviewer"
7 | minSdkVersion 21
8 | targetSdkVersion 29
9 | versionCode 10
10 | versionName "0.9.27"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | multiDexEnabled true
13 | buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | compileOptions {
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 | lintOptions {
26 | abortOnError false
27 | }
28 | testOptions {
29 | unitTests.returnDefaultValues = true
30 | }
31 | }
32 |
33 | gradle.projectsEvaluated {
34 | tasks.withType(JavaCompile) {
35 | //options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
36 | }
37 | }
38 |
39 | dependencies {
40 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0-beta02', {
41 | exclude group: 'com.android.support', module: 'support-annotations'
42 | })
43 |
44 | def room_version = "2.3.0"
45 |
46 | implementation "androidx.room:room-runtime:$room_version"
47 | annotationProcessor "androidx.room:room-compiler:$room_version"
48 |
49 | implementation "net.zetetic:android-database-sqlcipher:4.4.2"
50 | implementation "androidx.sqlite:sqlite:2.1.0"
51 |
52 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
53 | implementation "androidx.legacy:legacy-support-v13:1.0.0"
54 |
55 | implementation 'androidx.multidex:multidex:2.0.1'
56 | implementation 'com.google.android.material:material:1.4.0'
57 |
58 | implementation 'com.squareup.okhttp3:okhttp-sse:4.7.2'
59 | implementation 'com.squareup.okhttp3:okhttp:4.7.2'
60 | implementation 'io.github.rburgst:okhttp-digest:2.5'
61 |
62 | //noinspection GradleDependency,InvalidPackage
63 | implementation 'com.github.mukeshsolanki:MarkdownView-Android:1.1.1'
64 | implementation 'org.greenrobot:eventbus:3.2.0'
65 | implementation 'com.jakewharton:process-phoenix:2.0.0'
66 | implementation 'com.github.AppIntro:AppIntro:6.1.0'
67 | implementation 'com.github.hedzr:android-file-chooser:v1.2.0-final'
68 |
69 | testImplementation 'junit:junit:4.12'
70 | testImplementation 'com.github.webbit:webbit:0.4.19'
71 | testImplementation 'io.netty:netty:3.6.5.Final'
72 | testImplementation "androidx.arch.core:core-testing:2.1.0"
73 | }
74 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/volla/Android/Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/AdminReceiver.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.app.admin.DeviceAdminReceiver;
4 | import android.content.ComponentName;
5 |
6 | /**
7 | * Specific implementation of a DeviceAdminReceiver.
8 | */
9 | public class AdminReceiver extends DeviceAdminReceiver {
10 | public static final ComponentName COMP =
11 | new ComponentName("de.vier_bier.habpanelviewer", "de.vier_bier.habpanelviewer.AdminReceiver");
12 |
13 | public AdminReceiver() {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/EmptyActivity.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.os.Looper;
8 | import android.view.View;
9 | import android.view.WindowManager;
10 |
11 | import org.greenrobot.eventbus.EventBus;
12 |
13 | public class EmptyActivity extends ScreenControllingActivity {
14 | @SuppressLint("ClickableViewAccessibility")
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | boolean dim = getIntent().getExtras() != null && getIntent().getExtras().getBoolean(Constants.INTENT_FLAG_DIM);
20 | if (dim) {
21 | setContentView(R.layout.activity_empty);
22 |
23 | View view = findViewById(R.id.blankView);
24 | view.setSystemUiVisibility(
25 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
26 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
27 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
28 | | View.SYSTEM_UI_FLAG_FULLSCREEN
29 | | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
30 |
31 | view.setOnTouchListener((view1, motionEvent) -> {
32 | finish();
33 | EventBus.getDefault().post(new Constants.LoadStartUrl());
34 | return true;
35 | });
36 |
37 | new Handler(Looper.getMainLooper()).postDelayed(() -> {
38 | final WindowManager.LayoutParams layout = getWindow().getAttributes();
39 | layout.screenBrightness = 0f;
40 | getWindow().setAttributes(layout);
41 | }, 500);
42 | } else {
43 | EventBus.getDefault().post(new Constants.LoadStartUrl());
44 | finish();
45 | }
46 | }
47 |
48 | @Override
49 | public View getScreenOnView() {
50 | return findViewById(R.id.blankView);
51 | }
52 |
53 | @Override
54 | protected void onNewIntent(Intent intent) {
55 | super.onNewIntent(intent);
56 |
57 | boolean dim = intent.getExtras() != null && intent.getExtras().getBoolean(Constants.INTENT_FLAG_DIM);
58 | if (!dim) {
59 | finish();
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/NetworkTracker.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.net.ConnectivityManager;
8 | import android.net.NetworkInfo;
9 | import android.util.Log;
10 |
11 | import java.util.ArrayList;
12 |
13 | public class NetworkTracker extends BroadcastReceiver {
14 | private static final String TAG = "HPV-NetworkTracker";
15 |
16 | private final ConnectivityManager cm;
17 | private final ArrayList mListeners = new ArrayList<>();
18 | private boolean mConnected;
19 |
20 | public NetworkTracker(Context context) {
21 | cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
22 |
23 | final IntentFilter intentFilter = new IntentFilter();
24 | intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
25 |
26 | Log.d(TAG, "registering network receiver...");
27 | context.registerReceiver(this, intentFilter);
28 |
29 | updateStatus();
30 | }
31 |
32 | public void addListener(INetworkListener l) {
33 | synchronized (mListeners) {
34 | mListeners.add(l);
35 |
36 | if (isConnected()) {
37 | l.connected();
38 | } else {
39 | l.disconnected();
40 | }
41 | }
42 | }
43 |
44 | public void removeListener(INetworkListener l) {
45 | synchronized (mListeners) {
46 | mListeners.remove(l);
47 | }
48 | }
49 |
50 | public boolean isConnected() {
51 | return mConnected;
52 | }
53 |
54 | @Override
55 | public void onReceive(Context context, Intent intent) {
56 | Log.d(TAG, "network broadcast received");
57 | updateStatus();
58 | }
59 |
60 | private void updateStatus() {
61 | NetworkInfo activeNetwork = cm == null ? null : cm.getActiveNetworkInfo();
62 |
63 | if (activeNetwork != null && activeNetwork.isConnected()) {
64 | Log.d(TAG, "network is active: " + activeNetwork);
65 |
66 | if (!mConnected) {
67 | synchronized (mListeners) {
68 | mConnected = true;
69 | Log.d(TAG, "notifying listeners of active network...");
70 | for (INetworkListener listener : mListeners) {
71 | listener.connected();
72 | }
73 | }
74 | }
75 | } else {
76 | Log.d(TAG, "network is NOT active: " + activeNetwork);
77 |
78 | if (mConnected) {
79 | synchronized (mListeners) {
80 | mConnected = false;
81 | Log.d(TAG, "notifying listeners of inactive network...");
82 | for (INetworkListener listener : mListeners) {
83 | listener.disconnected();
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | public void terminate(Context context) {
91 | Log.d(TAG, "unregistering network receiver...");
92 | try {
93 | context.unregisterReceiver(this);
94 | } catch (IllegalArgumentException e) {
95 | Log.e(TAG, "unregistering network receiver failed", e);
96 | }
97 |
98 | synchronized (mListeners) {
99 | mListeners.clear();
100 | }
101 | }
102 |
103 | public interface INetworkListener {
104 | void disconnected();
105 |
106 | void connected();
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/PermissionUtil.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.Manifest;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.content.pm.PackageManager;
8 | import android.preference.PreferenceManager;
9 |
10 | import androidx.core.content.ContextCompat;
11 |
12 | import java.util.ArrayList;
13 | import java.util.HashMap;
14 |
15 | public class PermissionUtil {
16 | private static final HashMap PERMISSIONS = new HashMap<>();
17 |
18 | static {
19 | PERMISSIONS.put(Manifest.permission.RECORD_AUDIO, new String[]{Constants.PREF_ALLOW_WEBRTC, Constants.PREF_NOISE_LEVEL_ENABLED});
20 | PERMISSIONS.put(Manifest.permission.CAMERA, new String[]{Constants.PREF_ALLOW_WEBRTC,
21 | Constants.PREF_MOTION_DETECTION_ENABLED, Constants.PREF_MOTION_DETECTION_PREVIEW});
22 | }
23 |
24 | public static String[] getMissingPermissions(Context context) {
25 | final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
26 | ArrayList missing = new ArrayList<>();
27 |
28 | for (String permission : PERMISSIONS.keySet()) {
29 | if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
30 | String[] prefKeys = PERMISSIONS.get(permission);
31 |
32 | boolean prefEnabled = false;
33 | for (String key : prefKeys) {
34 | prefEnabled = prefEnabled || prefs.getBoolean(key, false);
35 | }
36 |
37 | if (prefEnabled) {
38 | missing.add(permission);
39 | }
40 | }
41 | }
42 |
43 | return missing.toArray(new String[0]);
44 | }
45 |
46 | public static String[] getDependingPreferences(String permission) {
47 | return PERMISSIONS.get(permission);
48 | }
49 |
50 | public static Intent createRequestPermissionsIntent(Context context) {
51 | Intent mainIntent = new Intent(context, PermissionValidatingActivity.class);
52 | String[] missingPermissions = getMissingPermissions(context);
53 |
54 | if (missingPermissions.length > 0) {
55 | mainIntent.putExtra(Constants.INTENT_FLAG_PERMISSIONS, missingPermissions);
56 | return mainIntent;
57 | }
58 |
59 | return null;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/PermissionValidatingActivity.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.app.Activity;
4 | import android.content.SharedPreferences;
5 | import android.content.pm.PackageManager;
6 | import android.os.Bundle;
7 | import android.preference.PreferenceManager;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.core.app.ActivityCompat;
11 |
12 | public class PermissionValidatingActivity extends Activity {
13 | String[] mMissingPerms;
14 |
15 | @Override
16 | protected void onCreate(final Bundle savedInstanceState) {
17 | super.onCreate(null);
18 |
19 | mMissingPerms = getIntent().getStringArrayExtra(Constants.INTENT_FLAG_PERMISSIONS);
20 |
21 | if (mMissingPerms.length == 0) {
22 | setResult(Activity.RESULT_OK);
23 | finish();
24 | } else {
25 | ActivityCompat.requestPermissions(this, mMissingPerms, Constants.REQUEST_VALIDATE);
26 | }
27 | }
28 |
29 | @Override
30 | public void onRequestPermissionsResult(int requestCode,
31 | @NonNull String[] permissions, @NonNull int[] grantResults) {
32 | if (requestCode == Constants.REQUEST_VALIDATE) {
33 | final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
34 | boolean permissionsDisabled = false;
35 |
36 | int idx = 0;
37 | SharedPreferences.Editor editor1 = prefs.edit();
38 | for (String permission : mMissingPerms) {
39 | if (grantResults[idx] != PackageManager.PERMISSION_GRANTED) {
40 |
41 | String[] prefKeys = PermissionUtil.getDependingPreferences(permission);
42 | for (String key : prefKeys) {
43 | editor1.putBoolean(key, false);
44 | }
45 | permissionsDisabled = true;
46 | }
47 |
48 | idx++;
49 | }
50 | editor1.apply();
51 |
52 | setResult(permissionsDisabled ? Activity.RESULT_CANCELED : Activity.RESULT_OK);
53 | finish();
54 | }
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/ResourcesUtil.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.content.Context;
4 | import androidx.annotation.NonNull;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 |
11 | /**
12 | * Utility methods for reading resources.
13 | */
14 | class ResourcesUtil {
15 | @NonNull
16 | private static String readRawTextFile(Context ctx, int resId) {
17 | InputStream inputStream = ctx.getResources().openRawResource(resId);
18 |
19 | InputStreamReader inputreader = new InputStreamReader(inputStream);
20 | BufferedReader buffreader = new BufferedReader(inputreader);
21 | String line;
22 | StringBuilder text = new StringBuilder();
23 |
24 | try {
25 | while ((line = buffreader.readLine()) != null) {
26 | text.append(line);
27 | text.append('\n');
28 | }
29 | } catch (IOException e) {
30 | return "";
31 | }
32 |
33 | return text.toString();
34 | }
35 |
36 | static String fetchReleaseNotes(Context ctx, String lastVersion) {
37 | String text = readRawTextFile(ctx, R.raw.releasenotes);
38 |
39 | final int pos = text.indexOf(lastVersion);
40 |
41 | if (pos != -1) {
42 | text = text.substring(0, pos).trim();
43 | }
44 |
45 | return text;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/ScreenCapturer.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.PixelFormat;
5 | import android.hardware.display.DisplayManager;
6 | import android.hardware.display.VirtualDisplay;
7 | import android.media.Image;
8 | import android.media.ImageReader;
9 | import android.media.projection.MediaProjection;
10 | import android.os.Handler;
11 |
12 | import java.nio.ByteBuffer;
13 | import java.util.concurrent.CountDownLatch;
14 | import java.util.concurrent.TimeUnit;
15 | import java.util.concurrent.atomic.AtomicReference;
16 |
17 | /**
18 | * Captures screen shots
19 | */
20 | public class ScreenCapturer {
21 |
22 | private final MediaProjection mProjection;
23 | private final Handler mHandler;
24 | private final int mWidth;
25 | private final int mHeight;
26 | private final int mDensity;
27 |
28 | ScreenCapturer(MediaProjection projection, int width, int height, int density) {
29 | mHandler = new Handler();
30 | mProjection = projection;
31 | mWidth = width;
32 | mHeight = height;
33 | mDensity = density;
34 | }
35 |
36 | public synchronized Bitmap captureScreen() throws IllegalStateException {
37 | AtomicReference imageHolder = new AtomicReference<>();
38 | final CountDownLatch latch = new CountDownLatch(1);
39 |
40 | ImageReader mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA_8888, 2);
41 | mImageReader.setOnImageAvailableListener(imageReader -> {
42 | imageHolder.set(mImageReader.acquireLatestImage());
43 | latch.countDown();
44 | }, mHandler);
45 |
46 | VirtualDisplay display = mProjection.createVirtualDisplay("screen-mirror", mWidth, mHeight, mDensity,
47 | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mImageReader.getSurface(),
48 | null, null);
49 |
50 | try {
51 | latch.await(1, TimeUnit.SECONDS);
52 |
53 | if (latch.getCount() == 1) {
54 | throw new IllegalStateException("Screen capturing timed out");
55 | }
56 |
57 | final Image image = imageHolder.get();
58 | Image.Plane[] planes = image.getPlanes();
59 | ByteBuffer buffer = planes[0].getBuffer();
60 | int pixelStride = planes[0].getPixelStride();
61 | int rowStride = planes[0].getRowStride();
62 | int rowPadding = rowStride - pixelStride * mWidth;
63 |
64 | // create bitmap
65 | Bitmap bmp = Bitmap.createBitmap(mWidth + rowPadding / pixelStride, mHeight, Bitmap.Config.ARGB_8888);
66 | bmp.copyPixelsFromBuffer(buffer);
67 | image.close();
68 |
69 | return bmp;
70 | } catch (InterruptedException e) {
71 | throw new IllegalStateException("Got interrupt while capturing screen");
72 | } finally {
73 | display.release();
74 | }
75 | }
76 |
77 | public void terminate() {
78 | mProjection.stop();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/TrackShutdownService.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.Binder;
6 | import android.os.IBinder;
7 | import android.util.Log;
8 |
9 | public class TrackShutdownService extends Service {
10 | private static final String TAG = "HPV-TrackShutdownServ";
11 | private final IBinder binder = new LocalBinder();
12 |
13 | @Override
14 | public IBinder onBind(Intent intent) {
15 | return binder;
16 | }
17 |
18 | @Override
19 | public int onStartCommand(Intent intent, int flags, int startId) {
20 | Log.d(TAG, "Service Started");
21 | return START_STICKY;
22 | }
23 |
24 | @Override
25 | public void onDestroy() {
26 | Log.d(TAG, "Service Destroyed");
27 | super.onDestroy();
28 | }
29 |
30 | @Override
31 | public void onTaskRemoved(Intent rootIntent) {
32 | Log.e(TAG, "END");
33 | stopSelf();
34 | }
35 |
36 | /**
37 | * Class used for the client Binder. Because we know this service always
38 | * runs in the same process as its clients, we don't need to deal with IPC.
39 | */
40 | class LocalBinder extends Binder {
41 | TrackShutdownService getService() {
42 | // Return this instance of LocalService so clients can call public methods
43 | return TrackShutdownService.this;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/AdminHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.app.Activity;
4 | import android.app.admin.DevicePolicyManager;
5 | import android.content.Context;
6 |
7 | import de.vier_bier.habpanelviewer.AdminReceiver;
8 |
9 | /**
10 | * Handler for ADMIN_LOCK_SCREEN command.
11 | */
12 | public class AdminHandler implements ICommandHandler {
13 | private final DevicePolicyManager mDPM;
14 |
15 | public AdminHandler(Activity activity) {
16 | mDPM = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
17 | }
18 |
19 | @Override
20 | public boolean handleCommand(Command cmd) {
21 | final String cmdStr = cmd.getCommand();
22 |
23 | if ("ADMIN_LOCK_SCREEN".equals(cmdStr)) {
24 | if (mDPM.isAdminActive(AdminReceiver.COMP)) {
25 | cmd.start();
26 | mDPM.lockNow();
27 | cmd.finished();
28 | } else {
29 | cmd.failed("device admin privileges missing");
30 | }
31 | return true;
32 | }
33 |
34 | return false;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/BluetoothHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.Manifest;
4 | import android.bluetooth.BluetoothManager;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import androidx.core.content.ContextCompat;
8 |
9 | /**
10 | * Handles BLUETOOTH_ON and BLUETOOTH_OFF commands.
11 | */
12 | public class BluetoothHandler implements ICommandHandler {
13 | private final Context mContext;
14 | private final BluetoothManager mManager;
15 |
16 | public BluetoothHandler(Context ctx, BluetoothManager manager) {
17 | mContext = ctx;
18 | mManager = manager;
19 | }
20 |
21 | @Override
22 | public boolean handleCommand(Command cmd) {
23 | final String cmdStr = cmd.getCommand();
24 |
25 | if ("BLUETOOTH_ON".equals(cmdStr)) {
26 | if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
27 | cmd.failed("bluetooth permission missing");
28 | }
29 |
30 | cmd.start();
31 | mManager.getAdapter().enable();
32 | } else if ("BLUETOOTH_OFF".equals(cmdStr)) {
33 | if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) {
34 | cmd.failed("bluetooth permission missing");
35 | }
36 |
37 | cmd.start();
38 | mManager.getAdapter().disable();
39 | } else {
40 | return false;
41 | }
42 |
43 | cmd.finished();
44 | return true;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/Command.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.graphics.Color;
4 |
5 | /**
6 | * Info about an received command.
7 | */
8 | public class Command {
9 | private final long mTime = System.currentTimeMillis();
10 | private final String mCommand;
11 |
12 | private CommandStatus mStatus;
13 | private String mDetails;
14 |
15 | private boolean mShowDetails;
16 |
17 | public Command(String command) {
18 | mCommand = command;
19 | mStatus = CommandStatus.UNHANDLED;
20 | }
21 |
22 | public String getCommand() {
23 | return mCommand;
24 | }
25 |
26 | public long getTime() {
27 | return mTime;
28 | }
29 |
30 | public void failed(String errorMessage) {
31 | mDetails = errorMessage;
32 | mStatus = CommandStatus.FAILED;
33 | }
34 |
35 | public void start() {
36 | if (mStatus == CommandStatus.UNHANDLED) {
37 | setStatus(CommandStatus.EXECUTING);
38 | }
39 | }
40 |
41 | public void progress(String message) {
42 | if (mStatus == CommandStatus.EXECUTING) {
43 | if (mDetails == null) {
44 | mDetails = message;
45 | } else {
46 | mDetails = mDetails + "\n" + message;
47 | }
48 | }
49 | }
50 |
51 | public void finished() {
52 | if (mStatus == CommandStatus.EXECUTING) {
53 | setStatus(CommandStatus.OK);
54 | }
55 | }
56 |
57 | private void setStatus(CommandStatus status) {
58 | mStatus = status;
59 | }
60 |
61 | public String getDetails() {
62 | return mDetails;
63 | }
64 |
65 | public CommandStatus getStatus() {
66 | return mStatus;
67 | }
68 |
69 | public void toggleShowDetails() {
70 | mShowDetails = !mShowDetails;
71 | }
72 |
73 | public boolean hasVisibleDetails() {
74 | return mShowDetails && mDetails != null;
75 | }
76 |
77 | public enum CommandStatus {
78 | UNHANDLED {
79 | @Override
80 | public int getColor() {
81 | return Color.YELLOW;
82 | }
83 | }, EXECUTING {
84 | @Override
85 | public int getColor() {
86 | return Color.GRAY;
87 | }
88 | }, FAILED {
89 | @Override
90 | public int getColor() {
91 | return Color.RED;
92 | }
93 | }, OK {
94 | @Override
95 | public int getColor() {
96 | return Color.GREEN;
97 | }
98 | };
99 |
100 | public abstract int getColor();
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/CommandQueue.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.content.SharedPreferences;
4 | import android.os.Handler;
5 | import android.os.HandlerThread;
6 | import android.os.Looper;
7 |
8 | import org.greenrobot.eventbus.EventBus;
9 | import org.greenrobot.eventbus.Subscribe;
10 | import org.greenrobot.eventbus.ThreadMode;
11 |
12 | import java.util.ArrayList;
13 |
14 | import de.vier_bier.habpanelviewer.Constants;
15 | import de.vier_bier.habpanelviewer.command.log.CommandLog;
16 | import de.vier_bier.habpanelviewer.command.log.CommandLogClient;
17 | import de.vier_bier.habpanelviewer.openhab.IStateUpdateListener;
18 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
19 |
20 | /**
21 | * Queue for commands sent from openHAB.
22 | */
23 | public class CommandQueue extends HandlerThread implements IStateUpdateListener {
24 | private final ServerConnection mServerConnection;
25 |
26 | private final Handler mUiHandler;
27 | private Handler mWorkerHandler;
28 |
29 | private final ArrayList mHandlers = new ArrayList<>();
30 | private final CommandLog mCmdLog = new CommandLog();
31 |
32 | public CommandQueue(ServerConnection serverConnection) {
33 | super("CommandQueue");
34 |
35 | EventBus.getDefault().register(this);
36 |
37 | mServerConnection = serverConnection;
38 | mUiHandler = new Handler(Looper.getMainLooper());
39 |
40 | start();
41 | }
42 |
43 | @Subscribe(threadMode = ThreadMode.MAIN)
44 | public void onMessageEvent(CommandLogClient client) {
45 | client.setCommandLog(mCmdLog);
46 | }
47 |
48 | public void addHandler(ICommandHandler h) {
49 | synchronized (mHandlers) {
50 | if (!mHandlers.contains(h)) {
51 | mHandlers.add(h);
52 | }
53 | }
54 | }
55 |
56 | @Override
57 | protected void onLooperPrepared() {
58 | super.onLooperPrepared();
59 |
60 | mWorkerHandler = new Handler(getLooper(), msg -> {
61 | if (msg.what == 12) {
62 | Command cmd = (Command) msg.obj;
63 |
64 | synchronized (mHandlers) {
65 | for (ICommandHandler mHandler : mHandlers) {
66 | try {
67 | if (mHandler.handleCommand(cmd)) {
68 | break;
69 | }
70 | } catch (Throwable t) {
71 | cmd.failed(t.getLocalizedMessage());
72 | break;
73 | }
74 | }
75 | }
76 | }
77 |
78 | return true;
79 | });
80 | }
81 |
82 | @Override
83 | public void itemUpdated(String name, String value) {
84 | if (value != null && !value.isEmpty()) {
85 | Command cmd = new Command(value);
86 |
87 | mUiHandler.post(() -> mCmdLog.add(cmd));
88 | mWorkerHandler.obtainMessage(12, cmd).sendToTarget();
89 | }
90 | }
91 |
92 | public void updateFromPreferences(final SharedPreferences prefs) {
93 | String mCmdItemName = prefs.getString(Constants.PREF_CMD_ITEM, "");
94 |
95 | mUiHandler.post(() -> mCmdLog.setSize(prefs.getInt(Constants.PREF_CMD_LOG_SIZE, 100)));
96 |
97 | mServerConnection.subscribeCommandItem(this, mCmdItemName);
98 | }
99 |
100 | public void terminate() {
101 | EventBus.getDefault().unregister(this);
102 | quit();
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/ICommandHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | /**
4 | * Handler for commands sent from openHAB.
5 | */
6 | interface ICommandHandler {
7 | boolean handleCommand(Command cmd);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/TtsHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.content.Context;
4 | import android.speech.tts.TextToSpeech;
5 |
6 | import java.util.Locale;
7 | import java.util.regex.Matcher;
8 | import java.util.regex.Pattern;
9 |
10 | public class TtsHandler implements ICommandHandler {
11 | private final Pattern TTS_PATTERN = Pattern.compile("TTS_(SPEAK|SET_LANG) (.*)");
12 |
13 | private final Context mContext;
14 |
15 | private TextToSpeech mTTS;
16 | private int mStatus;
17 |
18 | public TtsHandler(Context ctx) {
19 | mContext = ctx;
20 | }
21 |
22 | @Override
23 | public boolean handleCommand(Command cmd) {
24 | final String cmdStr = cmd.getCommand();
25 |
26 | Matcher m = TTS_PATTERN.matcher(cmdStr);
27 | if (m.matches()) {
28 | cmd.start();
29 |
30 | final String ttsCmd = m.group(1);
31 | final String arg = m.group(2);
32 |
33 | if (mTTS == null) {
34 | mTTS = new TextToSpeech(mContext, i -> {
35 | mStatus = i;
36 |
37 | doHandleCommand(ttsCmd, arg, cmd);
38 | });
39 | } else {
40 | doHandleCommand(ttsCmd, arg, cmd);
41 | }
42 | }
43 |
44 | return m.matches();
45 | }
46 |
47 | private void doHandleCommand(String ttsCmd, String arg, Command cmd) {
48 | if (mStatus == TextToSpeech.SUCCESS) {
49 | if ("SPEAK".equalsIgnoreCase(ttsCmd)) {
50 | mTTS.speak(arg, TextToSpeech.QUEUE_ADD, null);
51 | } else {
52 | Locale l = new Locale(arg);
53 |
54 | if (mTTS.isLanguageAvailable(l) >= TextToSpeech.LANG_AVAILABLE) {
55 | mTTS.setLanguage(l);
56 | } else {
57 | cmd.failed("Given locale invalid: " + arg);
58 | }
59 | }
60 | cmd.finished();
61 | } else {
62 | cmd.failed("Could not initialize TTS engine!");
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/VolumeHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import android.content.Context;
4 | import android.media.AudioManager;
5 |
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | import de.vier_bier.habpanelviewer.R;
10 |
11 | /**
12 | * Handler for MUTE, UNMUTE, SET_VOLUME commands.
13 | */
14 | public class VolumeHandler implements ICommandHandler {
15 | private final Pattern SET_PATTERN = Pattern.compile("SET_VOLUME ([0-9]+)");
16 |
17 | private final Context mCtx;
18 | private final AudioManager mAudioManager;
19 |
20 | private final int mMaxVolume;
21 | private int mVolume = -1;
22 |
23 | public VolumeHandler(Context ctx, AudioManager audioManager) {
24 | mCtx = ctx;
25 | mAudioManager = audioManager;
26 | mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
27 | }
28 |
29 | @Override
30 | public boolean handleCommand(Command cmd) {
31 | final String cmdStr = cmd.getCommand();
32 |
33 | Matcher m = SET_PATTERN.matcher(cmdStr);
34 |
35 | if ("MUTE".equals(cmdStr)) {
36 | cmd.start();
37 | mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
38 | mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, AudioManager.FLAG_SHOW_UI);
39 | } else if ("UNMUTE".equals(cmdStr)) {
40 | if (mVolume == -1) {
41 | cmd.failed(mCtx.getString(R.string.device_not_muted));
42 | } else {
43 | cmd.start();
44 | mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolume, AudioManager.FLAG_SHOW_UI);
45 | mVolume = -1;
46 | }
47 | } else if (m.matches()) {
48 | mVolume = -1;
49 | String value = m.group(1);
50 |
51 | try {
52 | int volume = Integer.parseInt(value);
53 | if (volume < 0) {
54 | volume = 0;
55 | } else if (volume > mMaxVolume) {
56 | volume = mMaxVolume;
57 | }
58 |
59 | cmd.start();
60 | mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, AudioManager.FLAG_SHOW_UI);
61 | } catch (NumberFormatException e) {
62 | cmd.failed("failed to parse volume from command");
63 | }
64 | } else {
65 | return false;
66 | }
67 |
68 | cmd.finished();
69 | return true;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/WebViewHandler.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command;
2 |
3 | import java.util.regex.Matcher;
4 | import java.util.regex.Pattern;
5 |
6 | import de.vier_bier.habpanelviewer.ClientWebView;
7 |
8 | /**
9 | * Handler for SHOW_START_URL, SHOW_URL, SHOW_DASHBOARD, RELOAD command.
10 | */
11 | public class WebViewHandler implements ICommandHandler {
12 | private final Pattern SHOW_PATTERN = Pattern.compile("SHOW_(URL|DASHBOARD) (.+)");
13 |
14 | private final ClientWebView mWebView;
15 |
16 | public WebViewHandler(ClientWebView webView) {
17 | mWebView = webView;
18 | }
19 |
20 | @Override
21 | public boolean handleCommand(Command cmd) {
22 | final String cmdStr = cmd.getCommand();
23 |
24 | final Matcher m = SHOW_PATTERN.matcher(cmdStr);
25 | if (m.matches()) {
26 | cmd.start();
27 | final String type = m.group(1);
28 |
29 | mWebView.post(() -> {
30 | if ("URL".matches(type)) {
31 | mWebView.loadUrl(m.group(2));
32 | } else {
33 | mWebView.loadDashboard(m.group(2));
34 | }
35 | cmd.finished();
36 | });
37 | } else if ("SHOW_START_URL".equals(cmdStr)) {
38 | cmd.start();
39 | mWebView.post(() -> {
40 | mWebView.loadStartUrl();
41 | cmd.finished();
42 | });
43 | } else if ("RELOAD".equals(cmdStr)) {
44 | cmd.start();
45 | mWebView.post(() -> {
46 | mWebView.reload();
47 | cmd.finished();
48 | });
49 | } else {
50 | return false;
51 | }
52 |
53 | return true;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/log/CommandLog.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command.log;
2 |
3 | import java.util.ArrayList;
4 |
5 | import de.vier_bier.habpanelviewer.command.Command;
6 |
7 | /**
8 | * Log for commands received from openHAB.
9 | */
10 | public class CommandLog {
11 | private final ArrayList mCommands = new ArrayList<>();
12 | private final ArrayList mListeners = new ArrayList<>();
13 | private int mSize;
14 |
15 | public void setSize(int size) {
16 | mSize = size;
17 | trim();
18 | }
19 |
20 | public void add(Command commandInfo) {
21 | synchronized (mCommands) {
22 | if (mCommands.isEmpty()) {
23 | mCommands.add(commandInfo);
24 | } else {
25 | mCommands.add(0, commandInfo);
26 | }
27 | }
28 |
29 | if (mCommands.size() > mSize) {
30 | mCommands.remove(mSize);
31 | }
32 |
33 | notifyListeners();
34 | }
35 |
36 | private void trim() {
37 | synchronized (mCommands) {
38 | while (mCommands.size() > mSize) {
39 | mCommands.remove(mSize);
40 | }
41 | }
42 | notifyListeners();
43 | }
44 |
45 | public ArrayList getCommands() {
46 | return mCommands;
47 | }
48 |
49 | private void notifyListeners() {
50 | synchronized (mListeners) {
51 | for (CommandLogListener l : mListeners) {
52 | l.logChanged();
53 | }
54 | }
55 | }
56 |
57 | void addListener(CommandLogListener listener) {
58 | synchronized (mListeners) {
59 | mListeners.add(listener);
60 | }
61 | }
62 |
63 | public interface CommandLogListener {
64 | void logChanged();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/command/log/CommandLogClient.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.command.log;
2 |
3 | /**
4 | * Client for a CommandLog.
5 | */
6 | public interface CommandLogClient {
7 | void setCommandLog(CommandLog mCmdLog);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/db/AppDatabase.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.db;
2 |
3 | import androidx.room.Database;
4 | import androidx.room.RoomDatabase;
5 |
6 | @Database(entities = {Credential.class}, version = 1, exportSchema = false)
7 | public abstract class AppDatabase extends RoomDatabase {
8 | public abstract CredentialDao credentialDao();
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/db/Credential.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.db;
2 |
3 | import androidx.room.Entity;
4 | import androidx.room.Index;
5 | import androidx.annotation.NonNull;
6 |
7 | import java.util.Objects;
8 |
9 |
10 | @Entity(primaryKeys = {"host", "realm"},
11 | indices = {@Index(value = {"host", "realm"}, unique = true)})
12 | public class Credential {
13 | @NonNull
14 | private final String host;
15 | @NonNull
16 | private final String realm;
17 |
18 | private final String user;
19 | private final String passwd;
20 |
21 | Credential(@NonNull String host, @NonNull String realm, String user, String passwd) {
22 | this.host = host;
23 | this.realm = realm;
24 | this.user = user;
25 | this.passwd = passwd;
26 | }
27 |
28 | @NonNull
29 | public String getHost() {
30 | return host;
31 | }
32 |
33 | @NonNull
34 | String getRealm() {
35 | return realm;
36 | }
37 |
38 | public String getUser() {
39 | return user;
40 | }
41 |
42 | public String getPasswd() {
43 | return passwd;
44 | }
45 |
46 | @Override
47 | public boolean equals(Object o) {
48 | if (this == o) return true;
49 | if (o == null || getClass() != o.getClass()) return false;
50 | Credential that = (Credential) o;
51 | return Objects.equals(host, that.host) &&
52 | Objects.equals(realm, that.realm);
53 | }
54 |
55 | @Override
56 | public int hashCode() {
57 | return Objects.hash(host, realm);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/db/CredentialDao.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.db;
2 |
3 | import androidx.room.Dao;
4 | import androidx.room.Delete;
5 | import androidx.room.Insert;
6 | import androidx.room.OnConflictStrategy;
7 | import androidx.room.Query;
8 |
9 | import java.util.List;
10 |
11 | @Dao
12 | public interface CredentialDao {
13 | @Query("SELECT * FROM credential where host = :host and realm = :realm")
14 | Credential get(String host, String realm);
15 |
16 | @Query("DELETE FROM credential where host = :host and realm = :realm")
17 | void remove(String host, String realm);
18 |
19 | @Delete
20 | void remove(Credential credential);
21 |
22 | @Insert(onConflict = OnConflictStrategy.REPLACE)
23 | void insert(Credential... users);
24 |
25 | @Query("SELECT * FROM credential")
26 | List getAll();
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/FutureState.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.concurrent.Delayed;
6 | import java.util.concurrent.TimeUnit;
7 |
8 | public class FutureState extends ItemState implements Delayed {
9 | private final int delayInMillis;
10 | private long origin = System.currentTimeMillis();
11 |
12 | public FutureState(String item, int interval, String state) {
13 | super(item, state);
14 |
15 | delayInMillis = interval * 1000;
16 |
17 | resetTime();
18 | }
19 |
20 | public void resetTime() {
21 | origin = System.currentTimeMillis();
22 | }
23 |
24 | @Override
25 | public long getDelay(@NonNull TimeUnit timeUnit) {
26 | return timeUnit.convert(delayInMillis - (System.currentTimeMillis() - origin), TimeUnit.MILLISECONDS);
27 | }
28 |
29 | @Override
30 | public int compareTo(@NonNull Delayed delayed) {
31 | if (delayed == this) {
32 | return 0;
33 | }
34 |
35 | return Long.compare(getDelay(TimeUnit.MILLISECONDS), delayed.getDelay(TimeUnit.MILLISECONDS));
36 | }
37 |
38 | @Override
39 | public boolean equals(Object obj) {
40 | return obj instanceof FutureState && mItemName.equals(((FutureState) obj).mItemName);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/ISseConnectionListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | public interface ISseConnectionListener extends ISseListener {
4 | void statusChanged(SseConnection.Status newStatus);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/ISseListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | interface ISseListener {}
4 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/IStateUpdateListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | /**
4 | * Item subscription listener
5 | */
6 | public interface IStateUpdateListener {
7 | /**
8 | * The state of an item has changed.
9 | */
10 | void itemUpdated(String name, String value);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/ISubscriptionListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | /**
4 | * Listener for item subscriptions.
5 | */
6 | interface ISubscriptionListener extends IStateUpdateListener {
7 | void itemInvalid(String name);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/IUrlListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | /**
4 | * Interface to be notified when the web view changes URL.
5 | */
6 | public interface IUrlListener {
7 | void changed(String url, boolean isHabPanelUrl);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/ItemState.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | /**
4 | * Item with state.
5 | */
6 | class ItemState {
7 | final String mItemName;
8 | final String mItemState;
9 |
10 | ItemState(String itemName, String itemState) {
11 | mItemName = itemName;
12 | mItemState = itemState;
13 | }
14 |
15 | public String getItemName() {
16 | return mItemName;
17 | }
18 |
19 | public String getItemState() {
20 | return mItemState;
21 | }
22 |
23 | @Override
24 | public int hashCode() {
25 | return mItemName.hashCode();
26 | }
27 |
28 | @Override
29 | public boolean equals(Object obj) {
30 | return obj instanceof ItemState && mItemName != null && mItemName.equals(((ItemState) obj).mItemName);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/average/Average.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab.average;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Iterator;
7 | import java.util.concurrent.Delayed;
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * Numeric delayed average.
12 | */
13 | abstract class Average implements Delayed {
14 | private final ArrayList> samples = new ArrayList<>();
15 | private final String itemName;
16 |
17 | private int delayInMillis;
18 | R total;
19 |
20 | private long origin = System.currentTimeMillis();
21 |
22 | Average(String item, int interval, R zero) {
23 | itemName = item;
24 | delayInMillis = interval * 1000;
25 | total = zero;
26 |
27 | resetTime();
28 | }
29 |
30 | public void add(R state) {
31 | Sample newSample = new Sample<>(state);
32 |
33 | synchronized (samples) {
34 | samples.add(newSample);
35 | }
36 | addToTotal(newSample.getValue());
37 | }
38 |
39 | public String getAverage() {
40 | removeOldSamples(System.currentTimeMillis());
41 |
42 | R avg;
43 | synchronized (samples) {
44 | if (samples.isEmpty()) {
45 | return null;
46 | }
47 |
48 | avg = divideTotal(samples.size());
49 | }
50 | return String.valueOf(avg);
51 | }
52 |
53 | private void removeOldSamples(long time) {
54 | synchronized (samples) {
55 | Iterator> i = samples.iterator();
56 | Sample s;
57 | while (i.hasNext()) {
58 | s = i.next();
59 |
60 | if (s.getTime() < time - delayInMillis) {
61 | i.remove();
62 | removeFromTotal(s.getValue());
63 | } else {
64 | return;
65 | }
66 | }
67 | }
68 | }
69 |
70 | protected abstract void removeFromTotal(R value);
71 |
72 | protected abstract void addToTotal(R value);
73 |
74 | protected abstract R divideTotal(int count);
75 |
76 | String getItemName() {
77 | return itemName;
78 | }
79 |
80 | void resetTime() {
81 | origin = System.currentTimeMillis();
82 | }
83 |
84 | @Override
85 | public long getDelay(@NonNull TimeUnit timeUnit) {
86 | return timeUnit.convert(delayInMillis - (System.currentTimeMillis() - origin), TimeUnit.MILLISECONDS);
87 | }
88 |
89 | @Override
90 | public int compareTo(@NonNull Delayed delayed) {
91 | if (delayed == this) {
92 | return 0;
93 | }
94 |
95 | return Long.compare(getDelay(TimeUnit.MILLISECONDS), delayed.getDelay(TimeUnit.MILLISECONDS));
96 | }
97 |
98 | boolean setInterval(int interval) {
99 | if (delayInMillis != interval * 1000) {
100 | delayInMillis = interval * 1000;
101 |
102 | return true;
103 | }
104 |
105 | return false;
106 | }
107 |
108 | @Override
109 | public boolean equals(Object obj) {
110 | return obj instanceof Average && itemName.equals(((Average) obj).itemName);
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/average/FloatAverage.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab.average;
2 |
3 | /**
4 | * Float average.
5 | */
6 | public class FloatAverage extends Average {
7 | public FloatAverage(String item, int interval) {
8 | super(item, interval, 0f);
9 |
10 | resetTime();
11 | }
12 |
13 | @Override
14 | public void removeFromTotal(Float value) {
15 | total = total - value;
16 | }
17 |
18 | @Override
19 | public void addToTotal(Float value) {
20 | total = total + value;
21 | }
22 |
23 | @Override
24 | public Float divideTotal(int count) {
25 | return total / count;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/average/IStatePropagator.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab.average;
2 |
3 | /**
4 | * Propagates item state updates.
5 | */
6 | public interface IStatePropagator {
7 | void updateState(String item, String state);
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/openhab/average/Sample.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab.average;
2 |
3 | /**
4 | * A value with its acquisition time.
5 | */
6 | class Sample {
7 | private final N fValue;
8 | private final long fTime;
9 |
10 | public Sample(N value) {
11 | fValue = value;
12 | fTime = System.currentTimeMillis();
13 | }
14 |
15 | public N getValue() {
16 | return fValue;
17 | }
18 |
19 | public long getTime() {
20 | return fTime;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/AutocompleteTextPreference.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.preference.EditTextPreference;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.view.ViewParent;
10 | import android.widget.AutoCompleteTextView;
11 | import android.widget.EditText;
12 |
13 | /**
14 | * EditTextPreference with auto completion.
15 | */
16 | class AutocompleteTextPreference extends EditTextPreference {
17 | private final AutoCompleteTextView mTextView;
18 |
19 | public AutocompleteTextPreference(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 |
22 | mTextView = new AutoCompleteTextView(context, attrs);
23 | }
24 |
25 | @SuppressLint("MissingSuperCall")
26 | @Override
27 | protected void onBindDialogView(View view) {
28 | EditText editText = mTextView;
29 | editText.setText(getText());
30 |
31 | ViewParent oldParent = editText.getParent();
32 | if (oldParent != view) {
33 | if (oldParent != null) {
34 | ((ViewGroup) oldParent).removeView(editText);
35 | }
36 | onAddEditTextToDialogView(view, editText);
37 | }
38 | }
39 |
40 | /**
41 | * Returns the {@link AutoCompleteTextView} widget that will be shown in the dialog.
42 | *
43 | * @return The {@link AutoCompleteTextView} widget that will be shown in the dialog.
44 | */
45 | public AutoCompleteTextView getEditText() {
46 | return mTextView;
47 | }
48 |
49 | @Override
50 | protected void onDialogClosed(boolean positiveResult) {
51 | super.onDialogClosed(positiveResult);
52 |
53 | if (positiveResult) {
54 | String value = getEditText().getText().toString();
55 | if (callChangeListener(value)) {
56 | setText(value);
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/ItemValidator.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.util.Log;
4 |
5 | import org.jetbrains.annotations.NotNull;
6 | import org.json.JSONArray;
7 | import org.json.JSONException;
8 | import org.json.JSONObject;
9 |
10 | import java.io.IOException;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | import de.vier_bier.habpanelviewer.connection.OkHttpClientFactory;
15 | import okhttp3.Call;
16 | import okhttp3.Callback;
17 | import okhttp3.OkHttpClient;
18 | import okhttp3.Request;
19 | import okhttp3.Response;
20 | import okhttp3.ResponseBody;
21 |
22 | class ItemValidator {
23 | private static final String TAG = "HPV-ItemValidator";
24 | private final List mNames = new ArrayList<>();
25 |
26 | void setServerUrl(String serverUrl, ValidationStateListener l) {
27 | OkHttpClient client = OkHttpClientFactory.getInstance().create();
28 |
29 | try {
30 | Request request = new Request.Builder()
31 | .url(serverUrl + "/rest/items")
32 | .build();
33 |
34 | client.newCall(request)
35 | .enqueue(new Callback() {
36 | @Override
37 | public void onFailure(@NotNull final Call call, @NotNull IOException e) {
38 | Log.e(TAG, "Failed to fetch item names from server " + serverUrl, e);
39 | mNames.clear();
40 | l.validationUnavailable();
41 | }
42 |
43 | @Override
44 | public void onResponse(@NotNull Call call, @NotNull final Response response) throws IOException {
45 | mNames.clear();
46 |
47 | if (response.code() == 200) {
48 | try (ResponseBody responseBody = response.body()) {
49 | if (responseBody != null) {
50 | JSONArray itemArr = new JSONArray(responseBody.string());
51 | for (int i = 0; i < itemArr.length(); i++) {
52 | JSONObject item = (JSONObject) itemArr.get(i);
53 | mNames.add(item.getString("name"));
54 | }
55 | } else {
56 | mNames.clear();
57 | l.validationUnavailable();
58 | }
59 | } catch (JSONException e) {
60 | Log.e(TAG, "Failed to fetch item names from server " + serverUrl, e);
61 | }
62 | l.validationAvailable(mNames);
63 | } else {
64 | mNames.clear();
65 | l.validationUnavailable();
66 | }
67 | }
68 | });
69 | } catch (IllegalArgumentException e) {
70 | mNames.clear();
71 | l.validationUnavailable();
72 | }
73 | }
74 |
75 | boolean isValid(String itemName) {
76 | return mNames.contains(itemName);
77 | }
78 |
79 | interface ValidationStateListener {
80 | void validationAvailable(List items);
81 | void validationUnavailable();
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferenceCallback.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | interface PreferenceCallback {
4 | void onNestedPreferenceSelected(String id);
5 | }
6 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesConnected.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.os.Bundle;
4 | import android.preference.EditTextPreference;
5 | import android.preference.Preference;
6 |
7 | import de.vier_bier.habpanelviewer.Constants;
8 | import de.vier_bier.habpanelviewer.R;
9 | import de.vier_bier.habpanelviewer.UiUtil;
10 |
11 | public class PreferencesConnected extends PreferenceFragment {
12 | @Override
13 | public void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 |
16 | addPreferencesFromResource(R.xml.preferences_connected);
17 |
18 | EditTextPreference connectedIntervalPreference =
19 | (EditTextPreference) findPreference(Constants.PREF_CONNECTED_INTERVAL);
20 | connectedIntervalPreference.setOnPreferenceChangeListener(new NumberValidatingListener(0, 6000));
21 | }
22 |
23 | private class NumberValidatingListener implements Preference.OnPreferenceChangeListener {
24 | private final int minVal;
25 | private final int maxVal;
26 |
27 | NumberValidatingListener(int minVal, int maxVal) {
28 | this.minVal = minVal;
29 | this.maxVal = maxVal;
30 | }
31 |
32 | @Override
33 | public boolean onPreferenceChange(final Preference preference, Object o) {
34 | boolean invalid;
35 | try {
36 | int val = Integer.parseInt((String) o);
37 |
38 | invalid = val < minVal || val > maxVal;
39 | } catch (NumberFormatException e) {
40 | invalid = true;
41 | }
42 |
43 | if (invalid && getActivity() != null && !getActivity().isFinishing()) {
44 | UiUtil.showDialog(getActivity(),
45 | preference.getTitle() + " " + getResources().getString(R.string.invalid),
46 | getString(R.string.noValidIntInRange, minVal, maxVal));
47 | return false;
48 | }
49 |
50 | return true;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesConnection.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.os.Bundle;
4 | import android.preference.EditTextPreference;
5 | import android.preference.Preference;
6 |
7 | import java.net.MalformedURLException;
8 | import java.net.URL;
9 |
10 | import de.vier_bier.habpanelviewer.Constants;
11 | import de.vier_bier.habpanelviewer.R;
12 | import de.vier_bier.habpanelviewer.UiUtil;
13 |
14 | public class PreferencesConnection extends PreferenceFragment {
15 | @Override
16 | public void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 |
19 | addPreferencesFromResource(R.xml.preferences_connection);
20 |
21 | EditTextPreference urlPreference = (EditTextPreference) findPreference(Constants.PREF_SERVER_URL);
22 | urlPreference.setOnPreferenceChangeListener(new URLValidatingListener());
23 | }
24 |
25 | private class URLValidatingListener implements Preference.OnPreferenceChangeListener {
26 | @Override
27 | public boolean onPreferenceChange(final Preference preference, Object o) {
28 | String text = (String) o;
29 |
30 | if (text == null || text.isEmpty()) {
31 | return true;
32 | }
33 |
34 | String dialogText = null;
35 |
36 | try {
37 | URL uri = new URL(text);
38 |
39 | int port = uri.getPort();
40 | if (port == -1) {
41 | port = uri.getDefaultPort();
42 | }
43 |
44 | if (port < 0 || port > 65535) {
45 | dialogText = "Port invalid: " + port;
46 | }
47 | } catch (MalformedURLException e) {
48 | dialogText = "URL invalid: " + e.getLocalizedMessage();
49 | }
50 |
51 | if (dialogText != null) {
52 | UiUtil.showDialog(getActivity(), preference.getTitle() + " "
53 | + getActivity().getResources().getString(R.string.invalid), dialogText);
54 | }
55 |
56 | return true;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesMain.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.os.Bundle;
4 |
5 | import de.vier_bier.habpanelviewer.Constants;
6 | import de.vier_bier.habpanelviewer.R;
7 |
8 | public class PreferencesMain extends PreferenceFragment {
9 | @Override
10 | public void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 |
13 | addPreferencesFromResource(R.xml.preferences_main);
14 |
15 | Bundle bundle = this.getArguments();
16 | if (bundle == null || !Boolean.TRUE.equals(bundle.getBoolean(Constants.INTENT_FLAG_CAMERA_ENABLED))) {
17 | findPreference("nested_pref_camera").setEnabled(false);
18 | findPreference("nested_pref_camera").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_camera)));
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesNoiseLevel.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.Manifest;
4 | import android.content.SharedPreferences;
5 | import android.content.pm.PackageManager;
6 | import android.os.Bundle;
7 | import android.preference.CheckBoxPreference;
8 | import android.preference.Preference;
9 | import android.preference.PreferenceManager;
10 |
11 | import androidx.annotation.NonNull;
12 | import androidx.core.content.ContextCompat;
13 | import androidx.legacy.app.FragmentCompat;
14 |
15 | import de.vier_bier.habpanelviewer.Constants;
16 | import de.vier_bier.habpanelviewer.R;
17 | import de.vier_bier.habpanelviewer.UiUtil;
18 |
19 | public class PreferencesNoiseLevel extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
20 | @Override
21 | public void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 |
24 | addPreferencesFromResource(R.xml.preferences_noise_level);
25 |
26 | CheckBoxPreference cbPref = (CheckBoxPreference) findPreference(Constants.PREF_NOISE_LEVEL_ENABLED);
27 | cbPref.setOnPreferenceChangeListener(this);
28 | }
29 |
30 | @Override
31 | public boolean onPreferenceChange(Preference preference, Object o) {
32 | boolean value = (Boolean) o;
33 |
34 | if (value) {
35 | if (needsPermissions()) {
36 | requestMissingPermissions();
37 | return false;
38 | }
39 | }
40 |
41 | return true;
42 | }
43 |
44 | private boolean needsPermissions() {
45 | return ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED;
46 | }
47 |
48 | private void requestMissingPermissions() {
49 | FragmentCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO},
50 | Constants.REQUEST_RECORD_AUDIO);
51 | }
52 |
53 | @Override
54 | public void onRequestPermissionsResult(int requestCode,
55 | @NonNull String[] permissions,
56 | @NonNull int[] grantResults) {
57 | if (requestCode == Constants.REQUEST_RECORD_AUDIO) {
58 | setAllowPreviewPref(grantResults.length > 0
59 | && grantResults[0] == PackageManager.PERMISSION_GRANTED);
60 | }
61 | }
62 |
63 | private void setAllowPreviewPref(boolean allowPreview) {
64 | final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
65 | if (!prefs.getBoolean(Constants.PREF_NOISE_LEVEL_ENABLED, false)) {
66 | SharedPreferences.Editor editor1 = prefs.edit();
67 | editor1.putBoolean(Constants.PREF_NOISE_LEVEL_ENABLED, allowPreview);
68 | editor1.apply();
69 |
70 | CheckBoxPreference allowPreference = (CheckBoxPreference) findPreference(Constants.PREF_NOISE_LEVEL_ENABLED);
71 | allowPreference.setChecked(allowPreview);
72 | }
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesOther.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.app.admin.DevicePolicyManager;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.SharedPreferences;
7 | import android.os.Bundle;
8 | import android.preference.CheckBoxPreference;
9 | import android.preference.Preference;
10 | import android.preference.PreferenceManager;
11 |
12 | import de.vier_bier.habpanelviewer.AdminReceiver;
13 | import de.vier_bier.habpanelviewer.Constants;
14 | import de.vier_bier.habpanelviewer.R;
15 |
16 | import static android.app.Activity.RESULT_OK;
17 |
18 | public class PreferencesOther extends PreferenceFragment {
19 | private DevicePolicyManager mDPM;
20 |
21 | @Override
22 | public void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 |
25 | addPreferencesFromResource(R.xml.preferences_other);
26 |
27 | mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
28 |
29 | // add validation to the device admin
30 | CheckBoxPreference adminPreference = (CheckBoxPreference) findPreference(Constants.PREF_DEVICE_ADMIN);
31 | adminPreference.setOnPreferenceChangeListener(new AdminValidatingListener());
32 | }
33 |
34 | @Override
35 | public void onStart() {
36 | super.onStart();
37 |
38 | onActivityResult(Constants.REQUEST_DEVICE_ADMIN, RESULT_OK, null);
39 | }
40 |
41 | @Override
42 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
43 | if (requestCode == Constants.REQUEST_DEVICE_ADMIN && resultCode == RESULT_OK) {
44 | final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
45 | boolean isActive = mDPM.isAdminActive(AdminReceiver.COMP);
46 |
47 | if (prefs.getBoolean(Constants.PREF_DEVICE_ADMIN, false) != isActive) {
48 | SharedPreferences.Editor editor1 = prefs.edit();
49 | editor1.putBoolean(Constants.PREF_DEVICE_ADMIN, isActive);
50 | editor1.apply();
51 |
52 | CheckBoxPreference adminPreference = (CheckBoxPreference) findPreference(Constants.PREF_DEVICE_ADMIN);
53 | adminPreference.setChecked(isActive);
54 | }
55 | }
56 | }
57 |
58 | private void removeAsAdmin() {
59 | mDPM.removeActiveAdmin(AdminReceiver.COMP);
60 |
61 | CheckBoxPreference adminPreference = (CheckBoxPreference) findPreference(Constants.PREF_DEVICE_ADMIN);
62 | adminPreference.setChecked(false);
63 | }
64 |
65 | private void installAsAdmin() {
66 | Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
67 | intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, AdminReceiver.COMP);
68 | intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, getString(R.string.deviceAdminDescription));
69 | startActivityForResult(intent, Constants.REQUEST_DEVICE_ADMIN);
70 | }
71 |
72 | private class AdminValidatingListener implements Preference.OnPreferenceChangeListener {
73 | @Override
74 | public boolean onPreferenceChange(final Preference preference, Object o) {
75 | boolean value = (Boolean) o;
76 |
77 | if (value && !mDPM.isAdminActive(AdminReceiver.COMP)) {
78 | installAsAdmin();
79 | } else if (!value && mDPM.isAdminActive(AdminReceiver.COMP)) {
80 | removeAsAdmin();
81 | }
82 |
83 | return false;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesReporting.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.os.Bundle;
4 |
5 | import de.vier_bier.habpanelviewer.Constants;
6 | import de.vier_bier.habpanelviewer.R;
7 |
8 | public class PreferencesReporting extends PreferenceFragment {
9 | @Override
10 | public void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 |
13 | addPreferencesFromResource(R.xml.preferences_reporting);
14 |
15 | boolean motionEnabled = false;
16 | boolean proximityEnabled = false;
17 | boolean pressureEnabled = false;
18 | boolean brightnessEnabled = false;
19 | boolean temperatureEnabled = false;
20 | boolean acceleroMeterEnabled = false;
21 |
22 | Bundle bundle = this.getArguments();
23 | if (bundle != null) {
24 | motionEnabled = bundle.getBoolean(Constants.INTENT_FLAG_MOTION_ENABLED);
25 | proximityEnabled = bundle.getBoolean("proximity_enabled");
26 | pressureEnabled = bundle.getBoolean("pressure_enabled");
27 | brightnessEnabled = bundle.getBoolean("brightness_enabled");
28 | temperatureEnabled = bundle.getBoolean("temperature_enabled");
29 | acceleroMeterEnabled = bundle.getBoolean("accelerometer_enabled");
30 | }
31 |
32 | // disable preferences if functionality is not available
33 | if (!motionEnabled) {
34 | findPreference("nested_pref_motion").setEnabled(false);
35 | findPreference("nested_pref_motion").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_motion)));
36 | }
37 | if (!proximityEnabled) {
38 | findPreference("nested_pref_proximity").setEnabled(false);
39 | findPreference("nested_pref_proximity").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_proximity)));
40 | }
41 | if (!pressureEnabled) {
42 | findPreference("nested_pref_pressure").setEnabled(false);
43 | findPreference("nested_pref_pressure").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_pressure)));
44 | }
45 | if (!brightnessEnabled) {
46 | findPreference("nested_pref_brightness").setEnabled(false);
47 | findPreference("nested_pref_brightness").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_brightness)));
48 | }
49 | if (!temperatureEnabled) {
50 | findPreference("nested_pref_temperature").setEnabled(false);
51 | findPreference("nested_pref_temperature").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_temperature)));
52 | }
53 | if (!acceleroMeterEnabled) {
54 | findPreference("nested_pref_accelerometer").setEnabled(false);
55 | findPreference("nested_pref_accelerometer").setSummary(getString(R.string.notAvailableOnDevice, getString(R.string.pref_accelerometer)));
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/preferences/PreferencesUi.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.preferences;
2 |
3 | import android.os.Bundle;
4 | import android.preference.ListPreference;
5 |
6 | import org.greenrobot.eventbus.EventBus;
7 |
8 | import de.vier_bier.habpanelviewer.Constants;
9 | import de.vier_bier.habpanelviewer.R;
10 | import de.vier_bier.habpanelviewer.UiUtil;
11 |
12 | public class PreferencesUi extends PreferenceFragment {
13 | @Override
14 | public void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 |
17 | addPreferencesFromResource(R.xml.preferences_ui);
18 |
19 | ListPreference themePreference = (ListPreference) findPreference(Constants.PREF_THEME);
20 | themePreference.setOnPreferenceChangeListener((preference, o) -> {
21 | if (getActivity() != null && !getActivity().isFinishing()) {
22 | if (UiUtil.themeChanged((String) o, getActivity())) {
23 | UiUtil.showSnackBar(getActivity().findViewById(R.id.myCoordinatorLayout),
24 | R.string.themeChangedRestartRequired, R.string.action_restart,
25 | view -> EventBus.getDefault().post(new Constants.Restart()));
26 | }
27 | }
28 |
29 | return true;
30 | });
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/AbstractAveragingDeviceMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.content.res.Resources;
6 | import android.hardware.SensorEvent;
7 | import android.hardware.SensorManager;
8 |
9 | import de.vier_bier.habpanelviewer.Constants;
10 | import de.vier_bier.habpanelviewer.R;
11 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
12 | import de.vier_bier.habpanelviewer.status.ApplicationStatus;
13 |
14 | public abstract class AbstractAveragingDeviceMonitor extends AbstractDeviceMonitor {
15 | private boolean mDoAverage;
16 | private int mInterval;
17 | Float mValue;
18 |
19 | AbstractAveragingDeviceMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection,
20 | String sensorName, String prefkey, int sensorType) {
21 | super(ctx, sensorManager, serverConnection, sensorName, prefkey, sensorType);
22 | }
23 |
24 | protected synchronized void addStatusItems(ApplicationStatus status) {
25 | if (mSensorEnabled) {
26 | String state = mCtx.getString(R.string.enabled);
27 | if (mDoAverage) {
28 | Resources res = mCtx.getResources();
29 | state += "\n" + res.getQuantityString(R.plurals.updateInterval, mInterval, mInterval);
30 | }
31 | if (!mSensorItem.isEmpty()) {
32 | state += "\n" + getInfoString(mValue, mSensorItem, mSensorState);
33 | }
34 |
35 | status.set(mSensorName, state);
36 | } else {
37 | status.set(mSensorName, mCtx.getString(R.string.disabled));
38 | }
39 | }
40 |
41 | abstract String getInfoString(Float value, String item, String state);
42 |
43 | @Override
44 | public synchronized void updateFromPreferences(SharedPreferences prefs) {
45 | boolean removeFromAverage = false;
46 | if (mDoAverage != prefs.getBoolean(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_AVERAGE, true)) {
47 | mDoAverage = prefs.getBoolean(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_AVERAGE, true);
48 |
49 | if (!mDoAverage) {
50 | removeFromAverage = true;
51 | }
52 | }
53 |
54 | if (mInterval != Integer.parseInt(prefs.getString(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_INTERVAL, "60"))) {
55 | mInterval = Integer.parseInt(prefs.getString(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_INTERVAL, "60"));
56 | }
57 | boolean wasEnabled = mSensorEnabled;
58 | super.updateFromPreferences(prefs);
59 |
60 | // if sensor or averaging turned off remove current average so it is not propagated
61 | if ((!mSensorEnabled && wasEnabled) || (wasEnabled && removeFromAverage)) {
62 | mServerConnection.removeFromAverage(mSensorItem);
63 | }
64 | }
65 | @Override
66 | public void onSensorChanged(SensorEvent event) {
67 | mValue = event.values[0];
68 |
69 | if (mDoAverage) {
70 | mServerConnection.addStateToAverage(mSensorItem, mValue, mInterval);
71 | } else {
72 | mServerConnection.updateState(mSensorItem, String.valueOf(mValue));
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/AbstractDeviceMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.hardware.Sensor;
7 | import android.hardware.SensorEventListener;
8 | import android.hardware.SensorManager;
9 | import android.util.Log;
10 |
11 | import org.greenrobot.eventbus.EventBus;
12 | import org.greenrobot.eventbus.Subscribe;
13 | import org.greenrobot.eventbus.ThreadMode;
14 |
15 | import de.vier_bier.habpanelviewer.Constants;
16 | import de.vier_bier.habpanelviewer.R;
17 | import de.vier_bier.habpanelviewer.openhab.IStateUpdateListener;
18 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
19 | import de.vier_bier.habpanelviewer.status.ApplicationStatus;
20 |
21 | /**
22 | * Abstract base class for device sensor monitors.
23 | */
24 | public abstract class AbstractDeviceMonitor implements IDeviceMonitor, SensorEventListener, IStateUpdateListener {
25 | private static final String TAG = "HPV-AbstractDevMon";
26 |
27 | final Context mCtx;
28 | private final SensorManager mSensorManager;
29 | final ServerConnection mServerConnection;
30 | final Sensor mSensor;
31 |
32 | final String mPreferenceKey;
33 | final String mSensorName;
34 | boolean mSensorEnabled;
35 |
36 | String mSensorItem;
37 | String mSensorState;
38 |
39 | AbstractDeviceMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection,
40 | String sensorName, String prefkey, int sensorType) {
41 | mCtx = ctx;
42 | mSensorManager = sensorManager;
43 | mServerConnection = serverConnection;
44 | mPreferenceKey = prefkey;
45 | mSensor = mSensorManager.getDefaultSensor(sensorType);
46 | mSensorName = sensorName;
47 |
48 | EventBus.getDefault().register(this);
49 | }
50 |
51 | @Subscribe(threadMode = ThreadMode.MAIN)
52 | public void onMessageEvent(ApplicationStatus status) {
53 | if (mSensor == null) {
54 | status.set(mSensorName, mCtx.getString(R.string.unavailable));
55 | }
56 | addStatusItems(status);
57 | }
58 |
59 | public synchronized void terminate() {
60 | EventBus.getDefault().unregister(this);
61 | mSensorManager.unregisterListener(this);
62 | }
63 |
64 | @Override
65 | public void disablePreferences(Intent intent) {
66 | intent.putExtra(mPreferenceKey + Constants.PREF_SUFFIX_ENABLED, mSensor != null);
67 | }
68 |
69 | protected abstract void addStatusItems(ApplicationStatus status);
70 |
71 | public synchronized void updateFromPreferences(SharedPreferences prefs) {
72 | if (mSensor != null) {
73 | if (mSensorEnabled != prefs.getBoolean(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_ENABLED, false)) {
74 | mSensorEnabled = !mSensorEnabled;
75 |
76 | if (mSensorEnabled) {
77 | mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
78 | } else {
79 | mSensorManager.unregisterListener(this);
80 | }
81 | }
82 |
83 | mSensorItem = prefs.getString(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_ITEM, "");
84 | mServerConnection.subscribeItems(this, mSensorItem);
85 | }
86 | }
87 |
88 | @Override
89 | public void itemUpdated(String name, String value) {
90 | mSensorState = value;
91 | }
92 |
93 | @Override
94 | public void onAccuracyChanged(Sensor sensor, int i) {
95 | Log.v(TAG, "onAccuracyChanged: " + i);
96 | }
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/AccelerometerMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.hardware.Sensor;
6 | import android.hardware.SensorEvent;
7 | import android.hardware.SensorManager;
8 |
9 | import java.util.Arrays;
10 |
11 | import de.vier_bier.habpanelviewer.Constants;
12 | import de.vier_bier.habpanelviewer.R;
13 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
14 | import de.vier_bier.habpanelviewer.status.ApplicationStatus;
15 |
16 | /**
17 | * Monitors accelerometer sensor state and reports to openHAB.
18 | */
19 | public class AccelerometerMonitor extends AbstractDeviceMonitor {
20 | private static final String TAG = "HPV-AccelerometerMon";
21 |
22 | private float mSensitivity = 1f;
23 | private String mSensitivityStr = "1.0";
24 | private long mLastMotionTime;
25 | private Boolean mMotion = Boolean.FALSE;
26 |
27 | public AccelerometerMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection) {
28 | super(ctx, sensorManager, serverConnection, ctx.getString(R.string.pref_accelerometer), "accelerometer", Sensor.TYPE_LINEAR_ACCELERATION);
29 | }
30 |
31 | @Override
32 | public void onSensorChanged(SensorEvent event) {
33 | // Shake detection
34 | float x = event.values[0];
35 | float y = event.values[1];
36 | float z = event.values[2];
37 | //Log.v(TAG, "onSensorChanged: x=" + x + ", y=" + y + ",z=" + z);
38 |
39 | float mAccel = (float) Math.sqrt(x * x + y * y + z * z);
40 |
41 | //Log.v(TAG, "onSensorChanged: mAccel=" + mAccel);
42 | if (mAccel > mSensitivity) {
43 | mLastMotionTime = System.currentTimeMillis();
44 | if (!mMotion) {
45 | mMotion = true;
46 | mServerConnection.updateState(mSensorItem, "CLOSED");
47 | }
48 | } else {
49 | if (mMotion && System.currentTimeMillis() - mLastMotionTime > 60000) {
50 | mMotion = false;
51 | mServerConnection.updateState(mSensorItem, "OPEN");
52 | }
53 | }
54 | }
55 |
56 | protected synchronized void addStatusItems(ApplicationStatus status) {
57 | if (mSensorEnabled) {
58 | String state = mCtx.getString(R.string.enabled);
59 | if (!mSensorItem.isEmpty()) {
60 | state += "\n" + mCtx.getString(R.string.deviceMoving, mMotion, mSensorItem, mSensorState);
61 | }
62 | state += "\n" + mCtx.getString(R.string.pref_accelerometerSensitivity) + ": " + sensText();
63 |
64 | status.set(mCtx.getString(R.string.pref_accelerometer), state);
65 | } else {
66 | status.set(mCtx.getString(R.string.pref_accelerometer), mCtx.getString(R.string.disabled));
67 | }
68 | }
69 |
70 | private String sensText() {
71 | String[] sensVals = mCtx.getResources().getStringArray(R.array.sensitivityValues);
72 | String[] sensitivities = mCtx.getResources().getStringArray(R.array.sensitivityNames);
73 |
74 | int idx = Arrays.asList(sensVals).indexOf(mSensitivityStr);
75 | return idx == -1 ? "??" : sensitivities[idx];
76 | }
77 |
78 | public synchronized void updateFromPreferences(SharedPreferences prefs) {
79 | super.updateFromPreferences(prefs);
80 |
81 | mSensitivityStr = prefs.getString(Constants.PREF_PREFIX + mPreferenceKey + Constants.PREF_SUFFIX_SENSITIVITY, "3.0");
82 | mSensitivity = Float.parseFloat(mSensitivityStr);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/BrightnessMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorManager;
6 |
7 | import de.vier_bier.habpanelviewer.R;
8 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
9 |
10 | /**
11 | * Monitors brightness sensor state and reports to openHAB.
12 | */
13 | public class BrightnessMonitor extends AbstractAveragingDeviceMonitor {
14 | public BrightnessMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection) {
15 | super(ctx, sensorManager, serverConnection, ctx.getString(R.string.pref_brightness), "brightness", Sensor.TYPE_LIGHT);
16 | }
17 |
18 | @Override
19 | String getInfoString(Float value, String item, String state) {
20 | return mCtx.getString(R.string.brightness, mValue, mSensorItem, mSensorState);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/IDeviceMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Intent;
4 | import android.content.SharedPreferences;
5 |
6 | public interface IDeviceMonitor {
7 | void terminate();
8 | void updateFromPreferences(SharedPreferences prefs);
9 | void disablePreferences(Intent intent);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/PressureMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorManager;
6 |
7 | import de.vier_bier.habpanelviewer.R;
8 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
9 |
10 | /**
11 | * Monitors pressure sensor state and reports to openHAB.
12 | */
13 | public class PressureMonitor extends AbstractAveragingDeviceMonitor {
14 | public PressureMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection) {
15 | super(ctx, sensorManager, serverConnection, ctx.getString(R.string.pref_pressure), "pressure", Sensor.TYPE_PRESSURE);
16 | }
17 |
18 | @Override
19 | String getInfoString(Float value, String item, String state) {
20 | return mCtx.getString(R.string.pressure, mValue, mSensorItem, mSensorState);
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/ProximityMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorEvent;
6 | import android.hardware.SensorManager;
7 | import android.util.Log;
8 |
9 | import de.vier_bier.habpanelviewer.R;
10 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
11 | import de.vier_bier.habpanelviewer.status.ApplicationStatus;
12 |
13 | /**
14 | * Monitors proximity sensor state and reports to openHAB.
15 | */
16 | public class ProximityMonitor extends AbstractDeviceMonitor {
17 | private static final String TAG = "HPV-ProximityMonitor";
18 |
19 | private Boolean mProximity;
20 |
21 | public ProximityMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection) {
22 | super(ctx, sensorManager, serverConnection, ctx.getString(R.string.pref_proximity), "proximity", Sensor.TYPE_PROXIMITY);
23 | }
24 |
25 | @Override
26 | public void onSensorChanged(SensorEvent event) {
27 | float distance = event.values[0];
28 |
29 | Log.v(TAG, "onSensorChanged" + distance);
30 |
31 | if (distance < mSensor.getMaximumRange()) {
32 | if (mProximity == null || !mProximity) {
33 | mProximity = true;
34 | mServerConnection.updateState(mSensorItem, "CLOSED");
35 | }
36 | } else if (mProximity == null || mProximity) {
37 | mProximity = false;
38 | mServerConnection.updateState(mSensorItem, "OPEN");
39 | }
40 | }
41 |
42 | protected synchronized void addStatusItems(ApplicationStatus status) {
43 | if (mSensorEnabled) {
44 | String state = mCtx.getString(R.string.enabled);
45 | if (!mSensorItem.isEmpty()) {
46 | state += "\n" + mCtx.getString(R.string.objectClose, mProximity, mSensorItem, mSensorState);
47 | }
48 |
49 | state += "\n" + mCtx.getString(R.string.maxRangeResolution, mSensor.getMaximumRange(), mSensor.getResolution());
50 |
51 | status.set(mCtx.getString(R.string.pref_proximity), state);
52 | } else {
53 | status.set(mCtx.getString(R.string.pref_proximity), mCtx.getString(R.string.disabled));
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/SensorMissingException.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | /**
4 | * Exception for indicating a missing device sensor.
5 | */
6 | class SensorMissingException extends Exception {
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/TemperatureMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorManager;
6 |
7 | import de.vier_bier.habpanelviewer.R;
8 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
9 |
10 | /**
11 | * Monitors temperature sensor state and reports to openHAB.
12 | */
13 | public class TemperatureMonitor extends AbstractAveragingDeviceMonitor {
14 | public TemperatureMonitor(Context ctx, SensorManager sensorManager, ServerConnection serverConnection) {
15 | super(ctx, sensorManager, serverConnection, ctx.getString(R.string.pref_temperature), "temperature", Sensor.TYPE_AMBIENT_TEMPERATURE);
16 | }
17 |
18 |
19 | @Override
20 | String getInfoString(Float value, String item, String state) {
21 | return mCtx.getString(R.string.temperature, mValue, mSensorItem, mSensorState);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/VolumeMonitor.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.database.ContentObserver;
7 | import android.media.AudioManager;
8 | import android.os.Handler;
9 |
10 | import org.greenrobot.eventbus.EventBus;
11 | import org.greenrobot.eventbus.Subscribe;
12 | import org.greenrobot.eventbus.ThreadMode;
13 |
14 | import de.vier_bier.habpanelviewer.Constants;
15 | import de.vier_bier.habpanelviewer.R;
16 | import de.vier_bier.habpanelviewer.openhab.IStateUpdateListener;
17 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
18 | import de.vier_bier.habpanelviewer.status.ApplicationStatus;
19 |
20 | /**
21 | * Monitors the device volume.
22 | */
23 | public class VolumeMonitor implements IDeviceMonitor, IStateUpdateListener {
24 | private final Context mCtx;
25 | private final AudioManager mAudioManager;
26 | private final ServerConnection mServerConnection;
27 |
28 | private boolean mVolumeEnabled;
29 |
30 | private String mVolumeItem;
31 | private Integer mVolume;
32 | private final Integer mMaxVolume;
33 | private String mVolumeState;
34 |
35 | private final ContentObserver mVolumeObserver = new ContentObserver(new Handler()) {
36 | @Override
37 | public void onChange(boolean selfChange) {
38 | super.onChange(selfChange);
39 |
40 | mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
41 | mServerConnection.updateState(mVolumeItem, String.valueOf(mVolume));
42 | }
43 | };
44 |
45 | public VolumeMonitor(Context context, AudioManager audioManager, ServerConnection serverConnection) {
46 | mCtx = context;
47 | mAudioManager = audioManager;
48 | mServerConnection = serverConnection;
49 |
50 | EventBus.getDefault().register(this);
51 |
52 | mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
53 | }
54 |
55 | @Override
56 | public void disablePreferences(Intent intent) { }
57 |
58 | @Override
59 | public synchronized void updateFromPreferences(SharedPreferences prefs) {
60 | mVolumeItem = prefs.getString(Constants.PREF_VOLUME_ITEM, "");
61 |
62 | if (mVolumeEnabled != prefs.getBoolean(Constants.PREF_VOLUME_ENABLED, false)) {
63 | mVolumeEnabled = !mVolumeEnabled;
64 |
65 | if (mVolumeEnabled) {
66 | mCtx.getContentResolver().registerContentObserver(
67 | android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver);
68 |
69 | mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
70 | mServerConnection.updateState(mVolumeItem, String.valueOf(mVolume));
71 | } else {
72 | mCtx.getContentResolver().unregisterContentObserver(mVolumeObserver);
73 | }
74 | }
75 |
76 | mServerConnection.subscribeItems(this, mVolumeItem);
77 | }
78 |
79 | @Subscribe(threadMode = ThreadMode.MAIN)
80 | public void onMessageEvent(ApplicationStatus status) {
81 | if (mVolumeEnabled) {
82 | String state = mCtx.getString(R.string.enabled);
83 | if (!mVolumeItem.isEmpty()) {
84 | state += "\n" + mCtx.getString(R.string.volumeIsOf, mVolume, mMaxVolume, mVolumeItem, mVolumeState);
85 | }
86 | status.set(mCtx.getString(R.string.pref_volume), state);
87 | } else {
88 | status.set(mCtx.getString(R.string.pref_volume), mCtx.getString(R.string.disabled));
89 | }
90 | }
91 |
92 | @Override
93 | public void terminate() {
94 | EventBus.getDefault().unregister(this);
95 | mCtx.getContentResolver().unregisterContentObserver(mVolumeObserver);
96 | }
97 |
98 | @Override
99 | public void itemUpdated(String name, String value) {
100 | mVolumeState = value;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/AbstractCameraImpl.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Point;
5 | import android.util.Log;
6 | import android.view.TextureView;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.Comparator;
11 | import java.util.List;
12 |
13 | /**
14 | * Base camera implementation.
15 | */
16 | abstract class AbstractCameraImpl implements ICamera {
17 | private static final String TAG = "HPV-AbstractCameraImpl";
18 |
19 | final Activity mActivity;
20 | final TextureView mPreviewView;
21 | int mDeviceOrientation;
22 |
23 | final List mListeners = new ArrayList<>();
24 |
25 | AbstractCameraImpl(Activity activity, TextureView previewView) {
26 | mActivity = activity;
27 | mPreviewView = previewView;
28 | mDeviceOrientation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
29 | }
30 |
31 | @Override
32 | public void addLumaListener(ILumaListener l) {
33 | mListeners.add(l);
34 | }
35 |
36 | @Override
37 | public void removeLumaListener(ILumaListener l) {
38 | mListeners.remove(l);
39 | }
40 |
41 | Point chooseOptimalSize(Point[] choices) {
42 |
43 | // Collect the supported resolutions that are at least as big as the preview Surface
44 | List bigEnough = new ArrayList<>();
45 | // Collect the supported resolutions that are smaller than the preview Surface
46 | List notBigEnough = new ArrayList<>();
47 | int w = 640;
48 | int h = 480;
49 | for (Point option : choices) {
50 | if (option.y == option.x * h / w) {
51 | if (option.x >= 640 &&
52 | option.y >= 480) {
53 | bigEnough.add(option);
54 | } else {
55 | notBigEnough.add(option);
56 | }
57 | }
58 | }
59 |
60 | // Pick the smallest of those big enough. If there is no one big enough, pick the
61 | // largest of those not big enough.
62 | if (bigEnough.size() > 0) {
63 | return Collections.min(bigEnough, new CompareSizesByArea());
64 | } else if (notBigEnough.size() > 0) {
65 | return Collections.max(notBigEnough, new CompareSizesByArea());
66 | } else {
67 | Log.e(TAG, "Couldn't find any suitable preview size");
68 | return choices[0];
69 | }
70 | }
71 |
72 | private static class CompareSizesByArea implements Comparator {
73 | @Override
74 | public int compare(Point lhs, Point rhs) {
75 | return Long.signum((long) lhs.x * lhs.y -
76 | (long) rhs.x * rhs.y);
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/CameraException.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | /**
4 | * Camera related exception independent on Android API level.
5 | */
6 | class CameraException extends Exception {
7 | CameraException(String s) {
8 | super(s);
9 | }
10 |
11 | CameraException(String s, Exception e) {
12 | super(s, e);
13 | }
14 |
15 | CameraException(Exception e) {
16 | super(e);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/CameraImplNone.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.graphics.SurfaceTexture;
4 |
5 | public class CameraImplNone implements ICamera {
6 | private final String mErrMsg;
7 |
8 | CameraImplNone(String error) {
9 | mErrMsg = error;
10 | }
11 |
12 | @Override
13 | public void lockCamera() {
14 | }
15 |
16 | @Override
17 | public void unlockCamera() {
18 | }
19 |
20 | @Override
21 | public boolean isCameraLocked() {
22 | return false;
23 | }
24 |
25 | @Override
26 | public void startPreview(SurfaceTexture surfaceTexture, IPreviewListener previewListener) {
27 | previewListener.error("");
28 | previewListener.started();
29 | }
30 |
31 | @Override
32 | public void stopPreview() {
33 | }
34 |
35 | @Override
36 | public boolean isPreviewRunning() {
37 | return false;
38 | }
39 |
40 | @Override
41 | public void addLumaListener(ILumaListener l) {
42 | }
43 |
44 | @Override
45 | public void removeLumaListener(ILumaListener l) {
46 | }
47 |
48 | @Override
49 | public int getCameraOrientation() {
50 | return 0;
51 | }
52 |
53 | @Override
54 | public void takePicture(IPictureListener iPictureHandler) {
55 | }
56 |
57 | @Override
58 | public void setDeviceRotation(int deviceOrientation) {
59 | }
60 |
61 | public String getMessage() {
62 | return mErrMsg;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/Comparer.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.graphics.Point;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Compares to images for equality. Does this by dividing the images into smaller rectangles,
9 | * computing the brightness average and comparing that.
10 | */
11 | class Comparer {
12 | private final int boxes;
13 |
14 | private final Point[] boxStart;
15 | private final Point[] boxEnd;
16 | private final int leniency;
17 |
18 | Comparer(int width, int height, int boxes, int leniency) {
19 | this.boxes = boxes;
20 | this.leniency = leniency;
21 |
22 | // how many points per box
23 | float xPixelsPerBox = width / (float) boxes;
24 | float yPixelsPerBox = height / (float) boxes;
25 |
26 | boxStart = new Point[boxes];
27 | boxEnd = new Point[boxes];
28 |
29 | for (int i = 0; i < boxes; i++) {
30 | int startX = (int) (i * xPixelsPerBox);
31 | int endX = i == (boxes - 1) ? width - 1 : (int) ((i + 1) * xPixelsPerBox) - 1;
32 |
33 | int startY = (int) (i * yPixelsPerBox);
34 | int endY = i == (boxes - 1) ? height - 1 : (int) ((i + 1) * yPixelsPerBox) - 1;
35 |
36 | boxStart[i] = new Point(startX, startY);
37 | boxEnd[i] = new Point(endX, endY);
38 | }
39 | }
40 |
41 | /**
42 | * Compare two images for difference
43 | */
44 | ArrayList isDifferent(LumaData s1, LumaData s2) {
45 | ArrayList differing = new ArrayList<>();
46 |
47 | s1.setBoxCount(boxes);
48 | s2.setBoxCount(boxes);
49 |
50 | int b1;
51 | int b2;
52 | int diff;
53 | for (int y = 0; y < boxes; y++) {
54 | for (int x = 0; x < boxes; x++) {
55 | b1 = calcAverage(s1, x, y);
56 | b2 = calcAverage(s2, x, y);
57 | diff = Math.abs(b1 - b2);
58 |
59 | if (diff > leniency * 2.55f) {
60 | differing.add(new Point(x, y));
61 | }
62 | }
63 | }
64 | return differing;
65 | }
66 |
67 | private int calcAverage(LumaData luma, int xBox, int yBox) {
68 | if (luma.getAverage(xBox, yBox) != -1) {
69 | return luma.getAverage(xBox, yBox);
70 | }
71 |
72 | byte[] data = luma.getData();
73 | if (data == null) throw new NullPointerException();
74 |
75 | int yPix = boxEnd[yBox].y - boxStart[yBox].y + 1;
76 | int xPix = boxEnd[xBox].x - boxStart[xBox].x + 1;
77 |
78 | int i = 0;
79 | int idx = boxStart[yBox].y * luma.getWidth() + boxStart[xBox].x;
80 | for (int y = boxStart[yBox].y; y <= boxEnd[yBox].y; y++) {
81 | for (int x = 0; x < xPix; x++) {
82 | i += data[idx++];
83 | }
84 | idx += luma.getWidth() - xPix;
85 | }
86 |
87 | byte result = (byte) (i / (xPix * yPix));
88 | luma.setAverage(xBox, yBox, result);
89 |
90 | return result;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/ICamera.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.graphics.SurfaceTexture;
4 | import android.util.Log;
5 |
6 | /**
7 | * Generic camera interface.
8 | */
9 | public interface ICamera {
10 | void lockCamera() throws CameraException;
11 |
12 | void unlockCamera();
13 |
14 | boolean isCameraLocked();
15 |
16 | void startPreview(SurfaceTexture surfaceTexture, IPreviewListener previewListener);
17 |
18 | void stopPreview() throws CameraException;
19 |
20 | boolean isPreviewRunning();
21 |
22 | void addLumaListener(ILumaListener l);
23 |
24 | void removeLumaListener(ILumaListener l);
25 |
26 | int getCameraOrientation();
27 |
28 | void takePicture(IPictureListener iPictureHandler);
29 |
30 | void setDeviceRotation(int deviceOrientation);
31 |
32 | interface ILumaListener {
33 | void preview(LumaData previewBytes);
34 |
35 | boolean needsPreview();
36 | }
37 |
38 | interface IPictureListener {
39 | void picture(byte[] data);
40 |
41 | void error(String message);
42 |
43 | void progress(String message);
44 | }
45 |
46 | interface IPreviewListener {
47 | void started();
48 |
49 | void error(String message);
50 |
51 | void exception(Exception e);
52 |
53 | void progress(String message);
54 | }
55 |
56 | class LoggingPreviewListener implements IPreviewListener {
57 | private static final String TAG = "HPV-LoggingPreviewLi";
58 |
59 | @Override
60 | public void started() {
61 | Log.d(TAG, "preview started");
62 | }
63 |
64 | @Override
65 | public void error(String message) {
66 | Log.e(TAG, "preview error: " + message);
67 | }
68 |
69 | @Override
70 | public void exception(Exception e) {
71 | Log.e(TAG, "preview error", e);
72 | }
73 |
74 | @Override
75 | public void progress(String message) {
76 | Log.d(TAG, "preview progress: " + message);
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/IMotionDetector.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.content.SharedPreferences;
4 |
5 | /**
6 | * Interface for motion detectors capabilities for the MainActivity
7 | */
8 | public interface IMotionDetector {
9 | void terminate();
10 |
11 | void updateFromPreferences(SharedPreferences prefs);
12 |
13 | Camera getCamera();
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/IMotionListener.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.graphics.Point;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * Interface for being notified about motion events.
9 | */
10 | public interface IMotionListener {
11 | void motionDetected(ArrayList differing);
12 |
13 | void noMotion();
14 |
15 | void tooDark();
16 |
17 | class MotionAdapter implements IMotionListener {
18 | @Override
19 | public void motionDetected(ArrayList differing) {
20 | }
21 |
22 | @Override
23 | public void noMotion() {
24 | }
25 |
26 | @Override
27 | public void tooDark() {
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/reporting/motion/MotionReporter.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.reporting.motion;
2 |
3 | import android.content.SharedPreferences;
4 | import android.graphics.Point;
5 |
6 | import java.util.ArrayList;
7 |
8 | import de.vier_bier.habpanelviewer.Constants;
9 | import de.vier_bier.habpanelviewer.openhab.ServerConnection;
10 |
11 | /**
12 | * Reports motion events to openHAB.
13 | */
14 | public class MotionReporter extends IMotionListener.MotionAdapter {
15 | private final ServerConnection mServerConnection;
16 | private final IMotionListener mListener;
17 |
18 | private long mLastMotionTime;
19 | private boolean mMotion;
20 |
21 | private String mMotionItem;
22 | private int mMotionTimeout;
23 |
24 | MotionReporter(IMotionListener l, ServerConnection serverConnection) {
25 | mServerConnection = serverConnection;
26 | mListener = l;
27 | }
28 |
29 | @Override
30 | public void motionDetected(ArrayList differing) {
31 | mListener.motionDetected(differing);
32 |
33 | mLastMotionTime = System.currentTimeMillis();
34 | if (!mMotion) {
35 | mMotion = true;
36 | mServerConnection.updateState(mMotionItem, "CLOSED");
37 | }
38 | }
39 |
40 | @Override
41 | public void noMotion() {
42 | mListener.noMotion();
43 |
44 | if (mMotion && System.currentTimeMillis() - mLastMotionTime > mMotionTimeout) {
45 | mMotion = false;
46 | mServerConnection.updateState(mMotionItem, "OPEN");
47 | }
48 | }
49 |
50 | public void updateFromPreferences(SharedPreferences prefs) {
51 | if (mMotionItem == null || !mMotionItem.equalsIgnoreCase(prefs.getString(Constants.PREF_MOTION_DETECTION_ITEM, ""))) {
52 | mMotionItem = prefs.getString(Constants.PREF_MOTION_DETECTION_ITEM, "");
53 | mMotion = false;
54 | }
55 |
56 | mMotionTimeout = Integer.parseInt(prefs.getString(Constants.PREF_MOTION_DETECTION_TIMEOUT, "60")) * 1000;
57 | mServerConnection.updateState(mMotionItem, mMotion ? "CLOSED" : "OPEN");
58 | }
59 |
60 | public void terminate() {
61 | mMotion = false;
62 | mServerConnection.updateState(mMotionItem, "OPEN");
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/status/ApplicationStatus.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.status;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 |
6 | /**
7 | * Class holding StatusItems for visualization in the Status Screen.
8 | */
9 | public class ApplicationStatus {
10 | private final ArrayList mValues = new ArrayList<>();
11 | private final HashMap mIndices = new HashMap<>();
12 |
13 | public synchronized void set(String key, String value) {
14 | StatusItem item = mIndices.get(key);
15 | if (item == null) {
16 | item = new StatusItem(key, value);
17 | mValues.add(item);
18 | mIndices.put(key, item);
19 | } else {
20 | item.setValue(value);
21 | }
22 | }
23 |
24 | int getItemCount() {
25 | return mValues.size();
26 | }
27 |
28 | StatusItem getItem(int i) {
29 | return mValues.get(i);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/status/StatusInfoActivity.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.status;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import androidx.annotation.NonNull;
6 | import androidx.appcompat.app.ActionBar;
7 | import androidx.appcompat.widget.Toolbar;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.BaseAdapter;
12 | import android.widget.ListView;
13 | import android.widget.TextView;
14 |
15 | import org.greenrobot.eventbus.EventBus;
16 |
17 | import java.util.concurrent.Executors;
18 | import java.util.concurrent.ScheduledExecutorService;
19 | import java.util.concurrent.TimeUnit;
20 |
21 | import de.vier_bier.habpanelviewer.R;
22 | import de.vier_bier.habpanelviewer.ScreenControllingActivity;
23 |
24 | /**
25 | * Status Information Activity showing interesting app or hardware details.
26 | */
27 | public class StatusInfoActivity extends ScreenControllingActivity {
28 | private final ApplicationStatus status = new ApplicationStatus();
29 | private ScheduledExecutorService executor;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 |
35 | final StatusItemAdapter adapter = new StatusItemAdapter(this, status);
36 |
37 | executor = Executors.newSingleThreadScheduledExecutor();
38 | executor.scheduleWithFixedDelay(() -> {
39 | EventBus.getDefault().post(status);
40 |
41 | runOnUiThread(adapter::notifyDataSetChanged);
42 | }, 0, 1, TimeUnit.SECONDS);
43 |
44 | setContentView(R.layout.activity_statusinfo);
45 |
46 | Toolbar myToolbar = findViewById(R.id.toolbar);
47 | setSupportActionBar(myToolbar);
48 |
49 | ActionBar ab = getSupportActionBar();
50 | if (ab != null) {
51 | ab.setDisplayHomeAsUpEnabled(true);
52 | }
53 |
54 | final ListView listview = findViewById(R.id.info_listview);
55 | listview.setAdapter(adapter);
56 | }
57 |
58 | @Override
59 | public View getScreenOnView() {
60 | return findViewById(R.id.info_listview);
61 | }
62 |
63 | @Override
64 | protected void onDestroy() {
65 | if (executor != null) {
66 | executor.shutdown();
67 | executor = null;
68 | }
69 | super.onDestroy();
70 | }
71 |
72 | private static class StatusItemAdapter extends BaseAdapter {
73 | private final Activity mContext;
74 | private final ApplicationStatus mStatus;
75 |
76 | StatusItemAdapter(Activity context, ApplicationStatus status) {
77 | mContext = context;
78 | mStatus = status;
79 | }
80 |
81 | @Override
82 | public int getCount() {
83 | return mStatus.getItemCount();
84 | }
85 |
86 | @Override
87 | public StatusItem getItem(int i) {
88 | return mStatus.getItem(i);
89 | }
90 |
91 | @Override
92 | public long getItemId(int i) {
93 | return mStatus.getItem(i).getId();
94 | }
95 |
96 | @NonNull
97 | @Override
98 | public View getView(int position, View view, @NonNull ViewGroup parent) {
99 | LayoutInflater inflater = mContext.getLayoutInflater();
100 | View rowView= inflater.inflate(R.layout.row_commandlog, parent, false);
101 |
102 | TextView txtTitle = rowView.findViewById(R.id.name);
103 | TextView txtValue = rowView.findViewById(R.id.value);
104 | txtTitle.setText(getItem(position).getName());
105 | txtValue.setText(getItem(position).getValue());
106 |
107 | return rowView;
108 | }
109 |
110 | @Override
111 | public CharSequence[] getAutofillOptions() {
112 | return new CharSequence[0];
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/de/vier_bier/habpanelviewer/status/StatusItem.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.status;
2 |
3 | /**
4 | * Info Item that will be shown in the Status Information Screen
5 | */
6 | public class StatusItem {
7 | private static long COUNTER;
8 | private final long id = COUNTER++;
9 |
10 | private final String mName;
11 | private String mValue;
12 |
13 | StatusItem(String name, String value) {
14 | mName = name;
15 | mValue = value;
16 | }
17 |
18 | public String getName() {
19 | return mName;
20 | }
21 |
22 | public String getValue() {
23 | return mValue;
24 | }
25 |
26 | public long getId() {
27 | return id;
28 | }
29 |
30 | public void setValue(String value) {
31 | mValue = value;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/book.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/browser.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/browser.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/commanding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/commanding.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/configuration.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/credentials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/credentials.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/database.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ready.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/ready.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/reporting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/reporting.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/drawable-mdpi/server.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/activity_main_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_access_time_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_upward_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_enhanced_encryption_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear_all_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_forum_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_help_outline_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info_outline_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_play_arrow_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_preferences_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_replay_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_share_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_view_headline_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_vpn_key_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_wiki_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_commandlog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_help.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_intro_discover.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
32 |
33 |
47 |
48 |
60 |
61 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
18 |
19 |
23 |
24 |
35 |
36 |
44 |
45 |
53 |
63 |
64 |
65 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main_content.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
17 |
18 |
25 |
26 |
32 |
33 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
16 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_statusinfo.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_credentials.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
24 |
25 |
37 |
38 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_master_password.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_scrollable_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/navheader_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
25 |
26 |
36 |
37 |
47 |
48 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/row_commandlog.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/commandlog_toolbar_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/help_toolbar_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/log_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/preferences_toolbar_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/webview_context_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/mytruststore.bks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/app/src/main/res/raw/mytruststore.bks
--------------------------------------------------------------------------------
/app/src/main/res/values-de/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - niedrig
5 | - mittel
6 | - hoch
7 |
8 |
9 | - 5.0
10 | - 1.0
11 | - 0.1
12 |
13 |
14 | - 10
15 | - 20
16 | - 30
17 | - 40
18 | - 50
19 | - 60
20 | - 70
21 | - 80
22 | - 90
23 | - 100
24 |
25 |
26 | - 5
27 | - 10
28 | - 15
29 | - 20
30 | - 25
31 | - 30
32 | - 35
33 | - 40
34 | - 45
35 | - 50
36 | - 55
37 | - 60
38 | - 65
39 | - 70
40 | - 75
41 | - 80
42 | - 85
43 | - 90
44 |
45 |
46 | - links
47 | - rechts
48 |
49 |
50 | - left
51 | - right
52 |
53 |
54 | - 0
55 | - 100
56 | - 200
57 | - 300
58 | - 400
59 | - 500
60 | - 600
61 | - 700
62 | - 800
63 | - 900
64 | - 1000
65 |
66 |
67 | - dunkel
68 | - hell
69 |
70 |
71 | - dark
72 | - light
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - low
5 | - medium
6 | - high
7 |
8 |
9 | - 5.0
10 | - 1.0
11 | - 0.1
12 |
13 |
14 | - 10
15 | - 20
16 | - 30
17 | - 40
18 | - 50
19 | - 60
20 | - 70
21 | - 80
22 | - 90
23 | - 100
24 |
25 |
26 | - 5
27 | - 10
28 | - 15
29 | - 20
30 | - 25
31 | - 30
32 | - 35
33 | - 40
34 | - 45
35 | - 50
36 | - 55
37 | - 60
38 | - 65
39 | - 70
40 | - 75
41 | - 80
42 | - 85
43 | - 90
44 |
45 |
46 | - left
47 | - right
48 |
49 |
50 | - left
51 | - right
52 |
53 |
54 | - 0
55 | - 100
56 | - 200
57 | - 300
58 | - 400
59 | - 500
60 | - 600
61 | - 700
62 | - 800
63 | - 900
64 | - 1000
65 |
66 |
67 | - dark
68 | - light
69 |
70 |
71 | - dark
72 | - light
73 |
74 |
75 | - OH2
76 | - OH3
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 160dp
7 |
8 | 20sp
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4CAF50
4 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/device_admin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_accelerometer.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_battery.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
23 |
29 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_brightness.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
23 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_browser.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
16 |
22 |
29 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_connected.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
13 |
19 |
20 |
21 |
23 |
28 |
34 |
41 |
42 |
43 |
45 |
50 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_connection.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
14 |
21 |
22 |
23 |
26 |
30 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_docking_state.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
12 |
13 |
17 |
18 |
23 |
24 |
29 |
30 |
34 |
35 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_motion.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
25 |
33 |
41 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_noise_level.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_other.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
15 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_pressure.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
23 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_proximity.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_reporting.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
14 |
15 |
19 |
20 |
24 |
25 |
29 |
30 |
34 |
35 |
36 |
37 |
40 |
41 |
45 |
46 |
50 |
51 |
55 |
56 |
60 |
61 |
65 |
66 |
70 |
71 |
75 |
76 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_restart.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_temperature.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
23 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_ui.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | android:persistent="false"
4 | android:title="@string/pref_ui">
5 |
6 |
13 |
20 |
25 |
30 |
35 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_usage.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_volume.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/test/java/de/vier_bier/habpanelviewer/openhab/OpenhabSseConnectionTest.java:
--------------------------------------------------------------------------------
1 | package de.vier_bier.habpanelviewer.openhab;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.assertEquals;
6 |
7 | public class OpenhabSseConnectionTest {
8 | @Test
9 | public void testBuildUrl() {
10 | OpenhabSseConnection c = new OpenhabSseConnection();
11 | c.setServerUrl("URL");
12 | c.setItemNames("cmdName");
13 | assertEquals("URL/rest/events?topics=openhab/items/cmdName/command", c.buildUrl());
14 |
15 | c.setItemNames("cmdName","item1", "item2");
16 | assertEquals("URL/rest/events?topics=openhab/items/item1/statechanged,openhab/items/item2/statechanged,openhab/items/cmdName/command", c.buildUrl());
17 |
18 | c.setServer("URL", OpenhabSseConnection.OHVersion.OH2);
19 | assertEquals("URL/rest/events?topics=smarthome/items/item1/statechanged,smarthome/items/item2/statechanged,smarthome/items/cmdName/command", c.buildUrl());
20 |
21 | c.setItemNames("cmdName");
22 | assertEquals("URL/rest/events?topics=smarthome/items/cmdName/command", c.buildUrl());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | mavenCentral()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.0.4'
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 | mavenCentral()
19 | flatDir {
20 | dirs 'libs'
21 | }
22 | maven { url "https://jitpack.io" }
23 | google()
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
8 | #
9 | # When configured, Gradle will run in incubating parallel mode.
10 | # This option should only be used with decoupled projects. More details, visit
11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
12 | # org.gradle.parallel=true
13 | #Sun Aug 01 11:05:47 CEST 2021
14 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx1536M"
15 | android.enableJetifier=true
16 | android.useAndroidX=true
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Dec 11 19:00:18 CET 2020
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-7.0.2-all.zip
7 |
--------------------------------------------------------------------------------
/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/habpanelviewer.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/issue_template.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ## Expected Behavior
7 |
8 |
9 |
10 | ## Current Behavior
11 |
12 |
13 |
14 |
15 |
16 | ## Possible Solution
17 |
18 |
19 | ## Steps to Reproduce (for Bugs)
20 |
21 | 1.
22 | 2.
23 |
24 | ## Your Environment
25 |
26 | * Device
27 | * Android Version
28 | * HabPanelViewer Version
29 |
30 |
--------------------------------------------------------------------------------
/logo.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/logo.xcf
--------------------------------------------------------------------------------
/logo2.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vbier/habpanelviewer/0c5756752e45562fc880fd096a8c67a703f83382/logo2.xcf
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------