Initial commit
This commit is contained in:
commit
9fc4e462d0
8 changed files with 278 additions and 0 deletions
118
.gitignore
vendored
Normal file
118
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
.yolo.json
|
||||||
|
credentials.json
|
||||||
|
verzendlijst.md
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids/
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage/
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS generated files
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
test/fixtures/
|
||||||
|
test/sample-pdfs/
|
||||||
|
test/output/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
temp/
|
||||||
|
tmp/
|
||||||
|
*.tmp%
|
||||||
|
|
||||||
|
# Android build output (tailscale-ha-launcher)
|
||||||
|
out/
|
||||||
|
classes/
|
||||||
|
dex/
|
||||||
|
*.keystore
|
||||||
30
AndroidManifest.xml
Normal file
30
AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="nl.eddy.tailscalehalauncher"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0">
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="26" android:targetSdkVersion="34" />
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:label="HA starten"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:allowBackup="false">
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
BIN
res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
BIN
res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
BIN
res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6 KiB |
130
src/nl/eddy/tailscalehalauncher/MainActivity.java
Normal file
130
src/nl/eddy/tailscalehalauncher/MainActivity.java
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
package nl.eddy.tailscalehalauncher;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity {
|
||||||
|
|
||||||
|
private static final String TAILSCALE_PKG = "com.tailscale.ipn";
|
||||||
|
private static final String TAILSCALE_RECEIVER = TAILSCALE_PKG + ".IPNReceiver";
|
||||||
|
private static final String CONNECT_ACTION = "com.tailscale.ipn.CONNECT_VPN";
|
||||||
|
private static final String DISCONNECT_ACTION = "com.tailscale.ipn.DISCONNECT_VPN";
|
||||||
|
private static final String HA_PKG = "io.homeassistant.companion.android";
|
||||||
|
private static final long WAIT_FOR_VPN_MS = 3000;
|
||||||
|
|
||||||
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Button connectButton = new Button(this);
|
||||||
|
connectButton.setText("VPN aan + HA openen");
|
||||||
|
connectButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onConnectClicked();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button disconnectButton = new Button(this);
|
||||||
|
disconnectButton.setText("VPN uit");
|
||||||
|
disconnectButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
onDisconnectClicked();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
int density = (int) getResources().getDisplayMetrics().density;
|
||||||
|
int sidePad = 24 * density;
|
||||||
|
|
||||||
|
LinearLayout layout = new LinearLayout(this);
|
||||||
|
layout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
layout.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||||
|
layout.setPadding(sidePad, 0, sidePad, 0);
|
||||||
|
|
||||||
|
// Connect knop bovenaan (met wat marge onder de statusbalk)
|
||||||
|
LinearLayout.LayoutParams connectLp =
|
||||||
|
new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
connectLp.setMargins(0, 90 * density, 0, 0);
|
||||||
|
layout.addView(connectButton, connectLp);
|
||||||
|
|
||||||
|
// Flexibele ruimte: duwt de onderste knop tot onderaan
|
||||||
|
View spacer = new View(this);
|
||||||
|
layout.addView(spacer, new LinearLayout.LayoutParams(0, 0, 1f));
|
||||||
|
|
||||||
|
// Disconnect knop onderaan
|
||||||
|
LinearLayout.LayoutParams disconnectLp =
|
||||||
|
new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
disconnectLp.setMargins(0, 0, 0, 90 * density);
|
||||||
|
layout.addView(disconnectButton, disconnectLp);
|
||||||
|
|
||||||
|
setContentView(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConnectClicked() {
|
||||||
|
if (!sendTailscaleCommand(CONNECT_ACTION)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toast.makeText(this, "Tailscale wordt aangezet…", Toast.LENGTH_SHORT).show();
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
launchHomeAssistant();
|
||||||
|
}
|
||||||
|
}, WAIT_FOR_VPN_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDisconnectClicked() {
|
||||||
|
sendTailscaleCommand(DISCONNECT_ACTION);
|
||||||
|
Toast.makeText(this, "Tailscale uitgezet", Toast.LENGTH_SHORT).show();
|
||||||
|
// korte delay zodat de broadcast verwerkt is, dan app afsluiten
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns false when Tailscale does not have a broadcast receiver. */
|
||||||
|
private boolean sendTailscaleCommand(String action) {
|
||||||
|
Intent intent = new Intent(action);
|
||||||
|
intent.setComponent(new ComponentName(TAILSCALE_PKG, TAILSCALE_RECEIVER));
|
||||||
|
try {
|
||||||
|
sendBroadcast(intent);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Toast.makeText(this, "Kon Tailscale-commando niet sturen: " + e.getMessage(),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchHomeAssistant() {
|
||||||
|
PackageManager pm = getPackageManager();
|
||||||
|
Intent launchIntent = pm.getLaunchIntentForPackage(HA_PKG);
|
||||||
|
if (launchIntent == null) {
|
||||||
|
Toast.makeText(this, "Home Assistant app is niet geïnstalleerd", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(launchIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue