Showing preview only (285K chars total). Download the full file or copy to clipboard to get everything.
Repository: MarkAdamson/home-assistant-plugin-for-tasker
Branch: master
Commit: 6df3a10d85ec
Files: 66
Total size: 261.5 KB
Directory structure:
gitextract_wh_8w5zl/
├── .gitignore
├── HomeAssistantTaskerPlugin/
│ ├── build.gradle
│ ├── lint.xml
│ ├── proguard-project.txt
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── markadamson/
│ │ └── taskerplugin/
│ │ └── homeassistant/
│ │ ├── bundle/
│ │ │ └── PluginBundleValuesTest.java
│ │ ├── receiver/
│ │ │ └── FireReceiverTest.java
│ │ ├── setting/
│ │ │ └── toast/
│ │ │ └── test/
│ │ │ ├── InstallLocation.java
│ │ │ └── ManifestTest.java
│ │ └── ui/
│ │ └── activity/
│ │ └── EditActivityTest.java
│ ├── debug/
│ │ └── AndroidManifest.xml
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── markadamson/
│ │ ├── locale/
│ │ │ └── sdk/
│ │ │ └── client/
│ │ │ ├── internal/
│ │ │ │ └── PluginActivityDelegate.java
│ │ │ └── ui/
│ │ │ └── activity/
│ │ │ ├── AbstractAppCompatPluginActivity.java
│ │ │ └── IPluginActivity.java
│ │ └── taskerplugin/
│ │ └── homeassistant/
│ │ ├── Constants.java
│ │ ├── PluginApplication.java
│ │ ├── TaskerPlugin.java
│ │ ├── Utils.java
│ │ ├── bundle/
│ │ │ ├── GetStatePluginBundleValues.java
│ │ │ ├── PluginBundleValues.java
│ │ │ └── RenderTemplatePluginBundleValues.java
│ │ ├── model/
│ │ │ ├── HAAPI.java
│ │ │ ├── HAAPIException.java
│ │ │ ├── HAAPIResult.java
│ │ │ ├── HAAPITask.java
│ │ │ ├── HAEntity.java
│ │ │ ├── HAServer.java
│ │ │ └── HAServerStore.java
│ │ ├── receiver/
│ │ │ ├── AbstractAsyncReceiver.java
│ │ │ ├── AbstractPluginSettingReceiver.java
│ │ │ └── FireReceiver.java
│ │ ├── service/
│ │ │ └── ActionService.java
│ │ └── ui/
│ │ ├── ServerSelectionUI.java
│ │ └── activity/
│ │ ├── EditActivity.java
│ │ ├── EditGetStateActivity.java
│ │ ├── EditRenderTemplateActivity.java
│ │ └── EditServerActivity.java
│ └── res/
│ ├── drawable/
│ │ ├── baseline_add_24.xml
│ │ ├── baseline_cancel_24.xml
│ │ ├── baseline_delete_24.xml
│ │ ├── baseline_edit_24.xml
│ │ └── outline_label_24.xml
│ ├── layout/
│ │ ├── edit_get_state.xml
│ │ ├── edit_render_template.xml
│ │ ├── edit_server.xml
│ │ ├── main.xml
│ │ └── select_server.xml
│ ├── menu/
│ │ └── menu.xml
│ └── values/
│ └── strings.xml
├── LICENSE
├── README.md
├── build.gradle
├── circle.yml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── tools/
├── ci/
│ ├── android-sdk-license
│ ├── android-sdk-setup.sh
│ ├── firebase-test-lab-test-module.sh
│ ├── google-cloud-test-lab-setup.sh
│ └── repositories.cfg
├── findbugs/
│ └── android-filter.xml
└── gcloud.yml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
syntax: glob
.crashlytics_data/
.DS_Store
.gradle/
.settings
.idea
*.iml
*.apk
bin/
build/
gen/
local.properties
HomeAssistantTaskerPlugin/release/
================================================
FILE: HomeAssistantTaskerPlugin/build.gradle
================================================
apply plugin: 'com.android.application'
apply plugin: 'findbugs'
dependencies {
implementation group:'net.jcip', name:'jcip-annotations', version:"${JCIP_ANNOTATION_VERSION_MATCHER}"
implementation group:'com.android.support', name:'support-annotations', version:"${ANDROID_SUPPORT_VERSION_MATCHER}"
implementation group:'com.android.support', name:'appcompat-v7', version:"${ANDROID_SUPPORT_VERSION_MATCHER}"
implementation group:'com.twofortyfouram', name:'android-annotation', version:"${TWOFORTYFOURAM_ANNOTATION_VERSION_MATCHER}"
implementation group:'com.twofortyfouram', name:'android-assertion', version:"${TWOFORTYFOURAM_ASSERTION_VERSION_MATCHER}"
implementation group:'com.twofortyfouram', name:'android-plugin-api-for-locale', version:"${TWOFORTYFOURAM_PLUGIN_API_VERSION_MATCHER}"
implementation group:'com.twofortyfouram', name:'android-plugin-client-sdk-for-locale', version:"${TWOFORTYFOURAM_PLUGIN_CLIENT_SDK_VERSION_MATCHER}"
implementation group:'com.twofortyfouram', name:'android-spackle', version:"${TWOFORTYFOURAM_SPACKLE_VERSION_MATCHER}"
androidTestImplementation group:'com.twofortyfouram', name:'android-test', version:"${TWOFORTYFOURAM_TEST_VERSION_MATCHER}"
}
android {
compileSdkVersion Integer.parseInt(ANDROID_COMPILE_SDK_VERSION)
buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(ANDROID_MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(ANDROID_TARGET_SDK_VERSION)
versionCode Integer.parseInt(ANDROID_VERSION_CODE)
versionName ANDROID_VERSION_NAME
// Keep only the default and English localizations; which reduces app size since
// dependencies contain localizations.
// The last two are for support of pseudolocales in debug builds.
resConfigs 'en', 'en-rUS', 'en_XA', 'ar_XB'
vectorDrawables.useSupportLibrary = true
}
if (RELEASE_KEYSTORE_PATH && RELEASE_KEYSTORE_PASSWORD && RELEASE_KEY_ALIAS && RELEASE_KEY_ALIAS_PASSWORD) {
signingConfigs {
release {
storeFile file(RELEASE_KEYSTORE_PATH)
storePassword RELEASE_KEYSTORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_ALIAS_PASSWORD
}
}
}
buildTypes {
debug {
testCoverageEnabled Boolean.parseBoolean(IS_COVERAGE_ENABLED)
applicationIdSuffix '.debug'
pseudoLocalesEnabled true
zipAlignEnabled true
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-project.txt'
shrinkResources true
if (RELEASE_KEYSTORE_PATH && RELEASE_KEYSTORE_PASSWORD && RELEASE_KEY_ALIAS && RELEASE_KEY_ALIAS_PASSWORD) {
signingConfig signingConfigs.release
}
}
}
}
task findbugs(type: FindBugs) {
ignoreFailures = true
classes = fileTree('build/intermediates/classes/release/')
source = fileTree('src/main/java/')
classpath = files()
excludeFilter = file("$rootProject.projectDir/tools/findbugs/android-filter.xml")
effort = 'max'
reportLevel = 'low'
reports {
// Only one report type can be enabled at a time, apparently.
xml.enabled = false
html.enabled = true
html.destination = "$project.buildDir/outputs/reports/findbugs/findbugs.html"
}
}
================================================
FILE: HomeAssistantTaskerPlugin/lint.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- App indexing for this app makes no sense. -->
<issue id="GoogleAppIndexingWarning" severity="ignore"/>
<!-- https://code.google.com/p/android/issues/detail?id=203407. -->
<issue id="ResourceCycle" severity="ignore"/>
</lint>
================================================
FILE: HomeAssistantTaskerPlugin/proguard-project.txt
================================================
# This improves obfuscation and moves non-public classes to their own namespace.
-repackageclasses 'com.markadamson.taskerplugin.homeassistant'
# Ensure that stacktraces are reversible.
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
================================================
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValuesTest.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.bundle;
import android.os.Bundle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.twofortyfouram.spackle.AppBuildInfo;
/**
* Tests {@link PluginBundleValues}.
*/
public final class PluginBundleValuesTest extends AndroidTestCase {
@SmallTest
public static void testExtraConstants() {
/*
* NOTE: This test is expected to fail initially when you are adapting this example to your
* own plug-in. Once you've settled on constant names for your Intent extras, go ahead and
* update this test case.
*
* The goal of this test case is to prevent accidental renaming of the Intent extras. If the
* extra is intentionally changed, then this unit test needs to be intentionally updated.
*/
assertEquals(
"com.markadamson.taskerplugin.homeassistant.extra.STRING_MESSAGE",
PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE); //$NON-NLS-1$
assertEquals(
"com.markadamson.taskerplugin.homeassistant.extra.INT_VERSION_CODE",
PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE); //$NON-NLS-1$
}
@SmallTest
public void testGenerateBundle() {
final Bundle bundle = PluginBundleValues.generateBundle(getContext(), "Foo"); //$NON-NLS-1$
assertNotNull(bundle);
assertEquals(2, bundle.keySet().size());
assertEquals("Foo",
bundle.getString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE)); //$NON-NLS-1$
assertEquals(AppBuildInfo.getVersionCode(getContext()),
bundle.getInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE));
}
@SmallTest
public static void testVerifyBundle_correct() {
final Bundle bundle = new Bundle();
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE,
"I am a toast message!"); //$NON-NLS-1$
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE, 1);
assertTrue(PluginBundleValues.isBundleValid(bundle));
}
@SmallTest
public static void testVerifyBundle_null() {
assertFalse(PluginBundleValues.isBundleValid(null));
}
@SmallTest
public static void testVerifyBundle_missing_extra() {
assertFalse(PluginBundleValues.isBundleValid(new Bundle()));
}
@SmallTest
public static void testVerifyBundle_extra_items() {
final Bundle bundle = new Bundle();
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE,
"I am a toast message!"); //$NON-NLS-1$
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE, 1);
bundle.putString("test", "test"); //$NON-NLS-1$//$NON-NLS-2$
assertFalse(PluginBundleValues.isBundleValid(bundle));
}
@SmallTest
public static void testVerifyBundle_null_message() {
final Bundle bundle = new Bundle();
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE, null);
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE, 1);
assertFalse(PluginBundleValues.isBundleValid(bundle));
}
@SmallTest
public static void testVerifyBundle_empty_message() {
final Bundle bundle = new Bundle();
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE, ""); //$NON-NLS-1$
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE, 1);
assertFalse(PluginBundleValues.isBundleValid(bundle));
}
@SmallTest
public static void testVerifyBundle_wrong_type() {
{
final Bundle bundle = new Bundle();
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE, 1);
bundle.putInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE, 1);
assertFalse(PluginBundleValues.isBundleValid(bundle));
}
{
final Bundle bundle = new Bundle();
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE,
"I am a toast message!"); //$NON-NLS-1$
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE,
"test"); //$NON-NLS-1$
assertFalse(PluginBundleValues.isBundleValid(bundle));
}
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiverTest.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.receiver;
import com.markadamson.taskerplugin.homeassistant.bundle.PluginBundleValues;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.Bundle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
/**
* Tests the {@link FireReceiver}.
*/
public final class FireReceiverTest extends AndroidTestCase {
/*
* These test cases perform sanity checks. These tests are not very extensive and additional
* testing is required to verify the BroadcastReceiver works correctly. For example, a human
* would need to manually verify that a Toast message appears when a correct Intent is sent to
* the receiver. Depending on what your setting implements, you may be able to verify more
* easily that the setting triggered the desired result via unit tests than this sample setting
* can.
*/
@SmallTest
public void testNullMessage() {
final BroadcastReceiver fireReceiver = new FireReceiver();
final Bundle bundle = PluginBundleValues
.generateBundle(getContext(), "test_message"); //$NON-NLS-1$
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE, null);
/*
* The receiver shouldn't crash if the EXTRA_BUNDLE is incorrect
*/
fireReceiver.onReceive(getContext(), new Intent(
com.twofortyfouram.locale.api.Intent.ACTION_FIRE_SETTING).putExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE, bundle));
}
@SmallTest
public void testNormal() {
final BroadcastReceiver fireReceiver = new FireReceiver();
final Bundle bundle = PluginBundleValues
.generateBundle(getContext(), "test_message"); //$NON-NLS-1$
fireReceiver.onReceive(getContext(), new Intent(
com.twofortyfouram.locale.api.Intent.ACTION_FIRE_SETTING).putExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE, bundle));
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/InstallLocation.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.setting.toast.test;
import com.twofortyfouram.annotation.Slow;
import com.twofortyfouram.annotation.Slow.Speed;
import com.twofortyfouram.annotation.VisibleForTesting;
import com.twofortyfouram.annotation.VisibleForTesting.Visibility;
import com.twofortyfouram.assertion.Assertions;
import net.jcip.annotations.ThreadSafe;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.XmlResourceParser;
import android.support.annotation.NonNull;
import java.io.IOException;
/**
* Represents the Android Manifest's possible states for install location.
*/
@ThreadSafe
public enum InstallLocation {
/**
* The application permits installation to either internal or external
* storage, with Android automatically deciding.
*/
auto,
/**
* The application can only be installed to internal storage.
*/
internalOnly,
/**
* The application permits installation to either internal or external
* storage, with preference for external storage.
*/
preferExternal,
/**
* No install location was specified in the Android Manifest. In terms of
* how Android interprets this, it is basically the same as
* {@link #internalOnly}.
*/
MISSING,
/**
* An unknown install location, such as a new install location added in
* newer versions of Android.
*/
UNKNOWN;
/**
* The Android Manifest int value for auto install location.
*/
/*
* Note: This value is a private API in Android and could change without
* warning
*/
@VisibleForTesting(Visibility.PRIVATE)
/* package */ static final int MANIFEST_INSTALL_LOCATION_AUTO = 0;
/**
* The Android Manifest int value for internal only install location.
*/
/*
* Note: This value is a private API in Android and could change without
* warning
*/
@VisibleForTesting(Visibility.PRIVATE)
/* package */ static final int MANIFEST_INSTALL_LOCATION_INTERNAL_ONLY = 1;
/**
* The Android Manifest int value for internal or external storage, with
* preference for external storage.
*/
/*
* Note: This value is a private API in Android and could change without
* warning
*/
@VisibleForTesting(Visibility.PRIVATE)
/* package */ static final int MANIFEST_INSTALL_LOCATION_PREFER_EXTERNAL = 2;
/**
* Takes the integer value of install location from the Android Manifest and
* converts it to an enum value.
*
* @param location one of the Android Manifest install locations.
* @return The enum type for the install location.
*/
@NonNull
/* package */ static InstallLocation getInstallLocation(final int location) {
switch (location) {
case MANIFEST_INSTALL_LOCATION_AUTO: {
return auto;
}
case MANIFEST_INSTALL_LOCATION_INTERNAL_ONLY: {
return internalOnly;
}
case MANIFEST_INSTALL_LOCATION_PREFER_EXTERNAL: {
return preferExternal;
}
default: {
return UNKNOWN;
}
}
}
/**
* Gets a package's install location, as per the Android Manifest.
*
* @param context Application context.
* @param packageName Package whose install location is to be checked.
* @return the install location.
* @throws NameNotFoundException if {@code packageName} isn't installed.
* @throws XmlPullParserException If the target package's manifest couldn't
* be parsed.
* @throws IOException If an error occurred reading the target package.
*/
@NonNull
@Slow(Speed.MILLISECONDS)
public static InstallLocation getManifestInstallLocation(@NonNull final Context context,
@NonNull final String packageName) throws NameNotFoundException,
XmlPullParserException, IOException {
Assertions.assertNotNull(context, "context"); //$NON-NLS-1$
Assertions.assertNotNull(packageName, "packageName"); //$NON-NLS-1$
/*
* There isn't a public API to check the installLocation of an APK, so
* this is a hacky implementation to read the value directly from the
* package's AndroidManifest.
*/
final XmlResourceParser xml = context
.createPackageContext(packageName, Context.CONTEXT_RESTRICTED).getAssets()
.openXmlResourceParser("AndroidManifest.xml"); //$NON-NLS-1$
try {
for (int eventType = xml.getEventType(); XmlPullParser.END_DOCUMENT != eventType;
eventType = xml
.nextToken()) {
switch (eventType) {
case XmlPullParser.START_TAG: {
if (xml.getName().matches("manifest")) { //$NON-NLS-1$
for (int x = 0; x < xml.getAttributeCount(); x++) {
if (xml.getAttributeName(x)
.matches("installLocation")) { //$NON-NLS-1$
return InstallLocation.getInstallLocation(Integer.parseInt(xml
.getAttributeValue(x)));
}
}
}
break;
}
}
}
/*
* Once this point is reached, it can be assumed the installLocation
* didn't exist in the AndroidManifest
*/
return InstallLocation.MISSING;
} finally {
xml.close();
}
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/ManifestTest.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.setting.toast.test;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.support.annotation.NonNull;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.LinkedList;
import java.util.List;
/**
* Tests to verify proper entries in the plug-in's Android Manifest.
*/
public final class ManifestTest extends AndroidTestCase {
@SmallTest
public void testApplicationEnabled() {
assertTrue(getContext().getApplicationInfo().enabled);
}
@SmallTest
public void testPluginActivityPresent() {
final List<ResolveInfo> activities = getPluginActivities(getContext());
assertFalse(activities.isEmpty());
for (final ResolveInfo x : activities) {
assertTrue(x.activityInfo.enabled);
assertTrue(x.activityInfo.exported);
/*
* Verify that the plug-in doesn't request permissions not available to the host
*/
assertNull(x.activityInfo.permission);
/*
* Verify that the plug-in has a label attribute in the AndroidManifest
*/
assertFalse(0 == x.activityInfo.labelRes);
/*
* Verify that the plug-in has a icon attribute in the AndroidManifest
*/
assertFalse(0 == x.activityInfo.icon);
}
}
@SmallTest
public void testPluginReceiver() {
final List<ResolveInfo> receivers = getPluginReceivers(getContext());
assertEquals(1, receivers.size());
for (final ResolveInfo x : receivers) {
assertTrue(x.activityInfo.enabled);
assertTrue(x.activityInfo.exported);
/*
* Verify that the plug-in doesn't request permissions not available to the host
*/
assertNull(x.activityInfo.permission);
}
}
/**
* Verifies the package is configured to be installed to internal memory
*/
@SmallTest
public void testManifestInstallLocation() throws Exception {
/*
* Note that in addition to this test, Locale will also check that a plug-in is actually on
* internal memory at runtime. This primarily affects custom ROMs that permit moving apps to
* external memory even if the app specifies internalOnly.
*/
assertEquals(InstallLocation.internalOnly, InstallLocation.getManifestInstallLocation(
getContext(), getContext().getPackageName()));
}
/**
* Gets a list of all Activities in {@code context}'s package that export
* {@link com.twofortyfouram.locale.api.Intent#ACTION_EDIT_SETTING}.
*
* @param context Application context.
*/
private static List<ResolveInfo> getPluginActivities(@NonNull final Context context) {
final String packageName = context.getPackageName();
final List<ResolveInfo> result = new LinkedList<ResolveInfo>();
for (final ResolveInfo x : context.getPackageManager().queryIntentActivities(
new Intent(com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING),
0)) {
if (packageName.equals(x.activityInfo.packageName)) {
result.add(x);
}
}
return result;
}
/**
* Gets a list of all BroadcastReceivers in {@code context}'s package that export
* {@link com.twofortyfouram.locale.api.Intent#ACTION_FIRE_SETTING ACTION_FIRE_SETTING}.
*
* @param context Application context.
*/
private static List<ResolveInfo> getPluginReceivers(@NonNull final Context context) {
final String packageName = context.getPackageName();
final List<ResolveInfo> result = new LinkedList<ResolveInfo>();
for (final ResolveInfo x : context.getPackageManager().queryBroadcastReceivers(
new Intent(com.twofortyfouram.locale.api.Intent.ACTION_FIRE_SETTING),
0)) {
if (packageName.equals(x.activityInfo.packageName)) {
result.add(x);
}
}
return result;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivityTest.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.ui.activity;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.text.TextUtils;
import android.widget.EditText;
import com.markadamson.taskerplugin.homeassistant.bundle.PluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.R;
import com.twofortyfouram.test.ui.activity.ActivityTestUtil;
/**
* Tests the {@link EditActivity}.
*/
public final class EditActivityTest extends ActivityInstrumentationTestCase2<EditActivity> {
/**
* Context of the target application. This is initialized in {@link #setUp()}.
*/
private Context mTargetContext;
/**
* Instrumentation for the test. This is initialized in {@link #setUp()}.
*/
private Instrumentation mInstrumentation;
/**
* Constructor for the test class; required by Android.
*/
public EditActivityTest() {
super(EditActivity.class);
}
/**
* Setup that executes before every test case
*/
@Override
protected void setUp() throws Exception {
super.setUp();
mInstrumentation = getInstrumentation();
mTargetContext = mInstrumentation.getTargetContext();
/*
* Perform test case specific initialization. This is required to be set up here because
* setActivityIntent has no effect inside a method annotated with @UiThreadTest
*/
if ("testNewSettingCancel".equals(getName())) { //$NON-NLS-1$
setActivityIntent(new Intent(com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING)
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BREADCRUMB,
"Locale > Edit Situation")); //$NON-NLS-1$
} else if ("testNewSettingSave".equals(getName())) { //$NON-NLS-1$
setActivityIntent(new Intent(com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING)
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BREADCRUMB,
"Locale > Edit Situation")); //$NON-NLS-1$
} else if ("testOldSetting".equals(getName())) { //$NON-NLS-1$
final Bundle bundle = PluginBundleValues.generateBundle(mTargetContext,
"I am a toast message!"); //$NON-NLS-1$
setActivityIntent(new Intent(com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING)
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BREADCRUMB,
"Locale > Edit Situation")
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB,
"I am a toast message!")
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE,
bundle)); //$NON-NLS-1$
} else if ("testBadBundle".equals(getName())) { //$NON-NLS-1$
final Bundle bundle = PluginBundleValues.generateBundle(mTargetContext,
"I am a toast message!"); //$NON-NLS-1$
bundle.putString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE, null);
setActivityIntent(new Intent(com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING)
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BREADCRUMB,
"Locale > Edit Situation")
.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE,
bundle)); //$NON-NLS-1$
}
}
@MediumTest
@UiThreadTest
public void testNewSettingCancel() throws Throwable {
final Activity activity = getActivity();
assertMessageAutoSync(""); //$NON-NLS-1$
assertHintAutoSync(mTargetContext.getString(R.string.message_hint));
activity.finish();
assertEquals(Activity.RESULT_CANCELED, ActivityTestUtil.getActivityResultCode(activity));
}
@MediumTest
@UiThreadTest
public void testNewSettingSave() throws Throwable {
final Activity activity = getActivity();
assertMessageAutoSync(""); //$NON-NLS-1$
assertHintAutoSync(mTargetContext.getString(R.string.message_hint));
setMessageAutoSync(getName());
activity.finish();
assertActivityResultAutoSync(getName());
}
@MediumTest
@UiThreadTest
public void testOldSetting() throws Throwable {
final Activity activity = getActivity();
/*
* It is necessary to call this manually; the test case won't call
* onPostCreate() for us :-(
*/
getActivity().onPostCreateWithPreviousResult(
getActivity().getIntent().getBundleExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE),
getActivity().getIntent().getStringExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB));
assertMessageAutoSync("I am a toast message!"); //$NON-NLS-1$
activity.finish();
assertEquals(Activity.RESULT_CANCELED, ActivityTestUtil.getActivityResultCode
(activity));
}
/**
* Verifies the Activity properly handles a bundle with a bad value embedded in it.
*/
@MediumTest
@UiThreadTest
public void testBadBundle() throws Throwable {
final Activity activity = getActivity();
assertMessageAutoSync(""); //$NON-NLS-1$
assertHintAutoSync(mTargetContext.getString(R.string.message_hint));
activity.finish();
assertEquals(Activity.RESULT_CANCELED, ActivityTestUtil.getActivityResultCode
(activity));
}
/**
* Asserts the Activity result contains the expected values for the given display state.
*
* @param message The message the plug-in is supposed to show.
*/
private void assertActivityResultAutoSync(final String message) throws Throwable {
final Activity activity = getActivity();
final Runnable runnable = new Runnable() {
public void run() {
activity.finish();
assertEquals(Activity.RESULT_OK, ActivityTestUtil.getActivityResultCode(activity));
final Intent result = ActivityTestUtil.getActivityResultData(activity);
assertNotNull(result);
final Bundle extras = result.getExtras();
assertNotNull(extras);
assertEquals(
String.format(
"Extras should only contain %s and %s but actually contain %s",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE,
com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB,
extras.keySet()), 2, extras.keySet() //$NON-NLS-1$
.size());
assertFalse(TextUtils.isEmpty(extras
.getString(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB)));
final Bundle pluginBundle = extras
.getBundle(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
assertNotNull(pluginBundle);
assertTrue(PluginBundleValues.isBundleValid(pluginBundle));
assertEquals(message,
pluginBundle.getString(PluginBundleValues.BUNDLE_EXTRA_STRING_MESSAGE));
}
};
autoSyncRunnable(runnable);
}
/**
* Asserts provided message is what the UI shows.
*
* @param message Message to assert equals the EditText.
*/
private void assertMessageAutoSync(final String message) throws Throwable {
final Runnable runnable = new Runnable() {
private final Activity mActivity = getActivity();
public void run() {
assertEquals(message, ((EditText) mActivity.findViewById(android.R.id.text1))
.getText().toString());
}
};
autoSyncRunnable(runnable);
}
/**
* Asserts provided hint is what the UI shows.
*
* @param hint Hint to assert equals the EditText.
*/
private void assertHintAutoSync(final String hint) throws Throwable {
final Runnable runnable = new Runnable() {
private final Activity mActivity = getActivity();
public void run() {
assertEquals(hint, ((EditText) mActivity.findViewById(android.R.id.text1))
.getHint());
}
};
autoSyncRunnable(runnable);
}
/**
* Sets the message.
*
* @param message The message to set.
*/
private void setMessageAutoSync(final String message) throws Throwable {
final Runnable runnable = new Runnable() {
private final Activity mActivity = getActivity();
public void run() {
final EditText editText = (EditText) mActivity.findViewById(android.R.id.text1);
editText.setText(message);
}
};
autoSyncRunnable(runnable);
}
/**
* Executes a runnable on the main thread. This method works even if the current thread is
* already the main thread.
*
* @param runnable to execute.
*/
protected final void autoSyncRunnable(final Runnable runnable) {
//noinspection ObjectEquality
if (Looper.getMainLooper() == Looper.myLooper()) {
runnable.run();
} else {
getInstrumentation().runOnMainSync(runnable);
getInstrumentation().waitForIdleSync();
}
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/debug/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.markadamson.taskerplugin.homeassistant">
<!-- For code coverage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application/>
</manifest>
================================================
FILE: HomeAssistantTaskerPlugin/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Because plug-ins interact with the host in the background, plug-ins MUST be installed to
internal memory. This is consistent with Google's app install location guidelines:
<http://developer.android.com/guide/appendix/install-location.html#Should>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.markadamson.taskerplugin.homeassistant"
android:installLocation="internalOnly">
<!-- This allows the plug-in to appear on non-touchscreen devices like Google TV.
Be sure to test that the app works without a touchscreen. -->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Although many of these default to true, some users with modified ROMs have
trouble seeing apps unless these attributes are explicitly set. -->
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" tools:ignore="UnusedAttribute"/>
<!-- When hardware acceleration is enabled, the RAM memory usage of the app is
significantly increased as soon as any UI is displayed. To reduce the memory
usage of a plug-in which is primarily a background app, either (1) hardware
acceleration should be disabled or (2) the plug-in's BroadcastReceiver (and any
applicable Service) should be put into a different process. This example plug-in
creates two processes. -->
<application
android:name="com.markadamson.taskerplugin.homeassistant.PluginApplication"
android:allowBackup="true"
android:fullBackupContent="true"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
tools:ignore="UnusedAttribute"
android:usesCleartextTraffic="true">
<activity
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.EditActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:label="@string/call_service"
android:theme="@style/Theme.AppCompat"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.EditGetStateActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:label="@string/get_state"
android:theme="@style/Theme.AppCompat"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.EditRenderTemplateActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:label="@string/render_template"
android:theme="@style/Theme.AppCompat"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
tools:ignore="UnusedAttribute">
</activity>
<activity
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.EditServerActivity"
android:exported="false"
android:label="@string/edit_server"
android:theme="@style/Theme.AppCompat"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
tools:ignore="UnusedAttribute">
</activity>
<!--
This is the "edit" Activity. Note that the host will reject plug-in
Activities for the following reasons:
- Missing "android:label=[...]"
- Missing "android:icon=[...]"
- The Activity isn't exported (e.g. android:exported="false")
- The Activity isn't enabled (e.g. android:enabled="false")
- The Activity requires permissions not available to the host
-->
<activity-alias
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.PluginActivity"
android:exported="true"
android:icon="@drawable/ic_plugin"
android:label="@string/call_service"
android:targetActivity="com.markadamson.taskerplugin.homeassistant.ui.activity.EditActivity"
tools:ignore="ExportedActivity">
<!-- this Intent filter allows the plug-in to be discovered by the host. -->
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.GetStatePluginActivity"
android:exported="true"
android:icon="@drawable/ic_plugin"
android:label="@string/get_state"
android:targetActivity="com.markadamson.taskerplugin.homeassistant.ui.activity.EditGetStateActivity"
tools:ignore="ExportedActivity">
<!-- this Intent filter allows the plug-in to be discovered by the host. -->
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name="com.markadamson.taskerplugin.homeassistant.ui.activity.RenderTemplatePluginActivity"
android:exported="true"
android:icon="@drawable/ic_plugin"
android:label="@string/render_template"
android:targetActivity="com.markadamson.taskerplugin.homeassistant.ui.activity.EditRenderTemplateActivity"
tools:ignore="ExportedActivity">
<!-- this Intent filter allows the plug-in to be discovered by the host. -->
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING"/>
</intent-filter>
</activity-alias>
<!--
If this plug-in does not stand alone (e.g. it is only a plug-in for Locale and does not
have a Launcher Activity), including this in the manifest will help when users try to
open the app directly from the app store.
-->
<activity
android:name="com.twofortyfouram.locale.sdk.client.ui.activity.InfoActivity"
android:exported="true"
tools:ignore="ExportedActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.INFO"/>
</intent-filter>
</activity>
<!--
This is the "fire" BroadcastReceiver. Note that the host will reject plug-in
BroadcastReceivers for the following reasons:
- The BroadcastReceiver isn't exported (e.g. android:exported="false")
- The BroadcastReceiver isn't enabled (e.g. android:enabled="false")
- The BroadcastReceiver requires permissions not available to the host
- There are multiple BroadcastReceivers for com.twofortyfouram.locale.intent.action.FIRE_SETTING
-->
<receiver
android:name="com.markadamson.taskerplugin.homeassistant.receiver.FireReceiver"
android:exported="true"
android:process=":background"
tools:ignore="ExportedReceiver">
<!-- this Intent filter allows the plug-in to discovered by the host. -->
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>
</intent-filter>
</receiver>
<service
android:name=".service.ActionService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"/>
</application>
</manifest>
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/internal/PluginActivityDelegate.java
================================================
package com.markadamson.locale.sdk.client.internal;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.twofortyfouram.assertion.BundleAssertions;
import com.markadamson.locale.sdk.client.ui.activity.IPluginActivity;
import com.twofortyfouram.log.Lumberjack;
import com.twofortyfouram.spackle.bundle.BundleComparer;
import com.twofortyfouram.spackle.bundle.BundleScrubber;
import net.jcip.annotations.Immutable;
import static com.twofortyfouram.assertion.Assertions.assertNotNull;
/**
* Activities that implement the {@link IPluginActivity} interface can delegate much of their
* responsibility to this class.
*
* @param <T> Plug-in activity.
*/
/*
* This class is intended to make the implementation of various plug-in Activities DRY.
*
* This class has no state, so therefore is immutable.
*/
@Immutable
public final class PluginActivityDelegate<T extends Activity & IPluginActivity> {
/**
* @param intent Intent to check.
* @return True if intent is a Locale plug-in edit Intent.
*/
public static boolean isLocalePluginIntent(@NonNull final Intent intent) {
assertNotNull(intent, "intent"); //$NON-NLS-1$
final String action = intent.getAction();
return com.twofortyfouram.locale.api.Intent.ACTION_EDIT_CONDITION.equals(action)
|| com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING.equals(action);
}
public void onCreate(@NonNull final T activity, @Nullable final Bundle savedInstanceState) {
assertNotNull(activity, "activity"); //$NON-NLS-1$
final Intent intent = activity.getIntent();
if (isLocalePluginIntent(intent)) {
if (BundleScrubber.scrub(intent)) {
return;
}
final Bundle previousBundle = activity.getPreviousBundle();
if (BundleScrubber.scrub(previousBundle)) {
return;
}
Lumberjack
.v("Creating Activity with Intent=%s, savedInstanceState=%s, EXTRA_BUNDLE=%s",
intent, savedInstanceState, previousBundle); //$NON-NLS-1$
}
}
public void onPostCreate(@NonNull final T activity, @Nullable final Bundle savedInstanceState) {
assertNotNull(activity, "activity"); //$NON-NLS-1$
if (PluginActivityDelegate.isLocalePluginIntent(activity.getIntent())) {
if (null == savedInstanceState) {
final Bundle previousBundle = activity.getPreviousBundle();
final String previousBlurb = activity.getPreviousBlurb();
if (null != previousBundle && null != previousBlurb) {
activity.onPostCreateWithPreviousResult(previousBundle, previousBlurb);
}
}
}
}
public void finish(@NonNull final T activity, final boolean isCancelled) {
if (PluginActivityDelegate.isLocalePluginIntent(activity.getIntent())) {
if (!isCancelled) {
final Bundle resultBundle = activity.getResultBundle();
if (null != resultBundle) {
BundleAssertions.assertSerializable(resultBundle);
final String blurb = activity.getResultBlurb(resultBundle);
assertNotNull(blurb, "blurb"); //$NON-NLS-1$
if (!BundleComparer.areBundlesEqual(resultBundle, activity.getPreviousBundle())
|| !blurb.equals(activity.getPreviousBlurb())) {
final Intent resultIntent = new Intent();
resultIntent.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE,
resultBundle);
resultIntent.putExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB,
blurb);
TaskerPlugin.addRelevantVariableList(resultIntent,
activity.getRelevantVariableList());
TaskerPlugin.Setting.requestTimeoutMS(resultIntent,
activity.requestedTimeoutMS());
activity.setResult(Activity.RESULT_OK, resultIntent);
}
}
}
}
}
@Nullable
public final String getPreviousBlurb(@NonNull final T activity) {
final String blurb = activity.getIntent().getStringExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB);
return blurb;
}
@Nullable
public Bundle getPreviousBundle(@NonNull final T activity) {
assertNotNull(activity, "activity"); //$NON-NLS-1$
final Bundle bundle = activity.getIntent().getBundleExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
if (null != bundle) {
if (activity.isBundleValid(bundle)) {
return bundle;
}
}
return null;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/AbstractAppCompatPluginActivity.java
================================================
/*
* android-plugin-client-sdk-for-locale https://github.com/twofortyfouram/android-plugin-client-sdk-for-locale
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.locale.sdk.client.ui.activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import com.markadamson.locale.sdk.client.internal.PluginActivityDelegate;
/**
* <p>NOTE: This class is for compatibility with Material Design via the appcompat-v7 library. To use this
* class, appcompat-v7 must be on the application's build path. Typically, this would involve adding
* appcompat-v7 to the dependencies section of the application's build.gradle script. For example,
* the dependency might look something like this
* {@code compile group:'com.android.support', name:'appcompat-v7', version:'[21,)'}</p>
* <p>
* Implements the basic behaviors of a "Edit" activity for a
* plug-in, handling the Intent protocol for storing and retrieving the plug-in's data.
* Recall that a plug-in Activity more or less saves a Bundle and a String blurb via the Intent
* extras {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} and {@link
* com.twofortyfouram.locale.api.Intent#EXTRA_STRING_BLURB EXTRA_STRING_BLURB}.
* Those extras represent the configured plug-in, so this Activity helps plug-ins store and
* retrieve
* those
* extras while abstracting the actual Intent protocol.
* </p>
* <p>
* The Activity can be started in one of two states:
* <ul>
* <li>New plug-in instance: The Activity's Intent will not contain
* {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE}.</li>
* <li>Old plug-in instance: The Activity's Intent will contain
* {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} and {@link
* com.twofortyfouram.locale.api.Intent#EXTRA_STRING_BLURB EXTRA_STRING_BLURB} from a previously
* saved plug-in instance that the user is editing. The previously saved Bundle
* and blurb can be retrieved at any time via {@link #getPreviousBundle()} and
* {@link #getPreviousBlurb()}. These will also be delivered via
* {@link #onPostCreateWithPreviousResult(android.os.Bundle, String)} during the
* Activity's {@link #onPostCreate(android.os.Bundle)} phase when the Activity is first
* created.</li>
* </ul>
* <p>During
* the Activity's {@link #finish()} lifecycle callback, this class will call {@link
* #getResultBundle()} and {@link #getResultBlurb(android.os.Bundle)}, which should return the
* Bundle and blurb data the Activity would like to save back to the host.
* </p>
* <p>
* Note that all of these behaviors only apply if the Intent
* starting the Activity is one of the plug-in "edit" Intent actions.
* </p>
*
* @see com.twofortyfouram.locale.api.Intent#ACTION_EDIT_CONDITION ACTION_EDIT_CONDITION
* @see com.twofortyfouram.locale.api.Intent#ACTION_EDIT_SETTING ACTION_EDIT_SETTING
*/
public abstract class AbstractAppCompatPluginActivity extends AppCompatActivity implements IPluginActivity {
/**
* Flag boolean that can be set prior to calling {@link #finish()} to control whether the
* Activity
* attempts to save a result back to the host. Typically this is only set to true after an
* explicit user interaction to abort editing the plug-in, such as tapping a "cancel" button.
*/
/*
* There is no need to save/restore this field's state.
*/
protected boolean mIsCancelled = false;
@NonNull
private final PluginActivityDelegate<AbstractAppCompatPluginActivity> mPluginActivityDelegate = new PluginActivityDelegate<>();
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPluginActivityDelegate.onCreate(this, savedInstanceState);
}
@Override
public void onPostCreate(@Nullable final Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mPluginActivityDelegate.onPostCreate(this, savedInstanceState);
}
@Override
public void finish() {
mPluginActivityDelegate.finish(this, mIsCancelled);
/*
* Super call must come after the Activity result is set. If it comes
* first, then the Activity result will be lost.
*/
super.finish();
}
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing. Internally, this method relies on {@link #isBundleValid(android.os.Bundle)}. If
* the bundle exists but is not valid, this method will return null.
*/
@Nullable
public final Bundle getPreviousBundle() {
return mPluginActivityDelegate.getPreviousBundle(this);
}
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_STRING_BLURB
* EXTRA_STRING_BLURB} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing.
*/
@Nullable
public final String getPreviousBlurb() {
return mPluginActivityDelegate.getPreviousBlurb(this);
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/IPluginActivity.java
================================================
package com.markadamson.locale.sdk.client.ui.activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Common interface for plug-in Activities.
*/
public interface IPluginActivity {
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing. Internally, this method relies on {@link #isBundleValid(android.os.Bundle)}. If
* the bundle exists but is not valid, this method will return null.
*/
@Nullable
Bundle getPreviousBundle();
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_STRING_BLURB
* EXTRA_STRING_BLURB} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing.
*/
@Nullable
String getPreviousBlurb();
/**
* <p>Validates the Bundle, to ensure that a malicious application isn't attempting to pass
* an invalid Bundle.</p>
*
* @param bundle The plug-in's Bundle previously returned by the edit
* Activity. {@code bundle} should not be mutated by this method.
* @return true if {@code bundle} is valid for the plug-in.
*/
boolean isBundleValid(@NonNull final Bundle bundle);
/**
* Plug-in Activity lifecycle callback to allow the Activity to restore
* state for editing a previously saved plug-in instance. This callback will
* occur during the onPostCreate() phase of the Activity lifecycle.
* <p>{@code bundle} will have been
* validated by {@link #isBundleValid(android.os.Bundle)} prior to this
* method being called. If {@link #isBundleValid(android.os.Bundle)} returned false, then this
* method will not be called. This helps ensure that plug-in Activity subclasses only have to
* worry about bundle validation once, in the {@link #isBundleValid(android.os.Bundle)}
* method.</p>
* <p>Note this callback only occurs the first time the Activity is created, so it will not be
* called
* when the Activity is recreated (e.g. {@code savedInstanceState != null}) such as after a
* configuration change like a screen rotation.</p>
*
* @param previousBundle Previous bundle that the Activity saved.
* @param previousBlurb Previous blurb that the Activity saved
*/
void onPostCreateWithPreviousResult(
@NonNull final Bundle previousBundle, @NonNull final String previousBlurb);
/**
* @return Bundle for the plug-in or {@code null} if a valid Bundle cannot
* be generated.
*/
@Nullable
Bundle getResultBundle();
/**
* @param bundle Valid bundle for the component.
* @return Blurb for {@code bundle}.
*/
@NonNull
String getResultBlurb(@NonNull final Bundle bundle);
/**
* @return Relevant variables.
*/
@NonNull
String[] getRelevantVariableList();
int requestedTimeoutMS();
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Constants.java
================================================
package com.markadamson.taskerplugin.homeassistant;
import android.support.annotation.NonNull;
public class Constants {
@NonNull
public static final String BUNDLE_EXTRA_BUNDLE_TYPE
= "com.markadamson.taskerplugin.homeassistant.extra.BUNDLE_TYPE"; //$NON-NLS-1$
public static final int BUNDLE_CALL_SERVICE = 0;
public static final int BUNDLE_GET_STATE = 1;
public static final int BUNDLE_RENDER_TEMPLATE = 2;
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/PluginApplication.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant;
import android.app.Application;
import com.twofortyfouram.log.Lumberjack;
/**
* Implements an application object for the plug-in.
*/
/*
* This application is non-essential for the plug-in's operation; it simply enables debugging
* options globally for the app.
*/
public final class PluginApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Lumberjack.init(getApplicationContext());
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/TaskerPlugin.java
================================================
//package com.yourcompany.yourcondition;
//package com.yourcompany.yoursetting;
package com.markadamson.taskerplugin.homeassistant;
// Constants and functions for Tasker *extensions* to the plugin protocol
// See Also: http://tasker.dinglisch.net/plugins.html
// Release Notes
// v1.1 20140202
// added function variableNameValid()
// fixed some javadoc entries (thanks to David Stone)
// v1.2 20140211
// added ACTION_EDIT_EVENT
// v1.3 20140227
// added REQUESTED_TIMEOUT_MS_NONE, REQUESTED_TIMEOUT_MS_MAX and REQUESTED_TIMEOUT_MS_NEVER
// requestTimeoutMS(): added range check
// v1.4 20140516
// support for data pass through in REQUEST_QUERY intent
// some javadoc entries fixed (thanks again David :-))
// v1.5 20141120
// added RESULT_CODE_FAILED_PLUGIN_FIRST
// added Setting.VARNAME_ERROR_MESSAGE
// v1.6 20150213
// added Setting.getHintTimeoutMS()
// added Host.addHintTimeoutMS()
// v1.7 20160619
// null check for getCallingActivity() in hostSupportsOnFireVariableReplacement( Activity editActivity )
// v1.8 20161002
// added hostSupportsKeyEncoding(), setKeyEncoding() and Host.getKeysWithEncoding()
import java.net.URISyntaxException;
import java.security.SecureRandom;
import java.util.regex.Pattern;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.util.Log;
public class TaskerPlugin {
private final static String TAG = "TaskerPlugin";
private final static String BASE_KEY = "net.dinglisch.android.tasker";
private final static String EXTRAS_PREFIX = BASE_KEY + ".extras.";
private final static int FIRST_ON_FIRE_VARIABLES_TASKER_VERSION = 80;
public final static String VARIABLE_PREFIX = "%";
// when generating non-repeating integers, look this far back for repeats
// see getPositiveNonRepeatingRandomInteger()
private final static int RANDOM_HISTORY_SIZE = 100;
/**
* Action that the EditActivity for an event plugin should be launched by
*/
public final static String ACTION_EDIT_EVENT = BASE_KEY + ".ACTION_EDIT_EVENT";
private final static String VARIABLE_NAME_START_EXPRESSION = "[\\w&&[^_]]";
private final static String VARIABLE_NAME_MID_EXPRESSION = "[\\w0-9]+";
private final static String VARIABLE_NAME_END_EXPRESSION = "[\\w0-9&&[^_]]";
public final static String VARIABLE_NAME_MAIN_PART_MATCH_EXPRESSION =
VARIABLE_NAME_START_EXPRESSION + VARIABLE_NAME_MID_EXPRESSION + VARIABLE_NAME_END_EXPRESSION
;
public final static String VARIABLE_NAME_MATCH_EXPRESSION =
VARIABLE_PREFIX + "+" +
VARIABLE_NAME_MAIN_PART_MATCH_EXPRESSION
;
private static Pattern VARIABLE_NAME_MATCH_PATTERN = null;
/**
* @see #addVariableBundle(Bundle, Bundle)
* @see Host#getVariablesBundle(Bundle)
*/
private final static String EXTRA_VARIABLES_BUNDLE = EXTRAS_PREFIX + "VARIABLES";
/**
* Host capabilities, passed to plugin with edit intents
*/
private final static String EXTRA_HOST_CAPABILITIES = EXTRAS_PREFIX + "HOST_CAPABILITIES";
/**
* @see Setting#hostSupportsVariableReturn(Bundle)
*/
public final static int EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES = 2;
/**
* @see Condition#hostSupportsVariableReturn(Bundle)
*/
public final static int EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES = 4;
/**
* @see Setting#hostSupportsOnFireVariableReplacement(Bundle)
*/
public final static int EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT = 8;
/**
* @see Setting#hostSupportsVariableReturn(Bundle)
*/
private final static int EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES = 16;
public final static int EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION = 32;
public final static int EXTRA_HOST_CAPABILITY_REQUEST_QUERY_DATA_PASS_THROUGH = 64;
public final static int EXTRA_HOST_CAPABILITY_ENCODING_JSON = 128;
public final static int EXTRA_HOST_CAPABILITY_ALL =
EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES |
EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES |
EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT |
EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES|
EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION |
EXTRA_HOST_CAPABILITY_REQUEST_QUERY_DATA_PASS_THROUGH |
EXTRA_HOST_CAPABILITY_ENCODING_JSON
;
/**
* Possible encodings of text in bundle values
*
* @see #setKeyEncoding(Bundle,String[],Encoding)
*/
public enum Encoding { JSON };
private final static String BUNDLE_KEY_ENCODING_JSON_KEYS = BASE_KEY + ".JSON_ENCODED_KEYS";
public static boolean hostSupportsKeyEncoding( Bundle extrasFromHost, Encoding encoding ) {
switch ( encoding ) {
case JSON:
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_ENCODING_JSON );
default:
return false;
}
}
/**
*
* Miscellaneous operational hints going one way or the other
* @see Setting#hostSupportsVariableReturn(Bundle)
*/
private final static String EXTRA_HINTS_BUNDLE = EXTRAS_PREFIX + "HINTS";
private final static String BUNDLE_KEY_HINT_PREFIX = ".hints.";
private final static String BUNDLE_KEY_HINT_TIMEOUT_MS = BUNDLE_KEY_HINT_PREFIX + "TIMEOUT";
/**
*
* @see #hostSupportsRelevantVariables(Bundle)
* @see #addRelevantVariableList(Intent, String[])
* @see #getRelevantVariableList(Bundle)
*/
private final static String BUNDLE_KEY_RELEVANT_VARIABLES = BASE_KEY + ".RELEVANT_VARIABLES";
public static boolean hostSupportsRelevantVariables( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_RELEVANT_VARIABLES );
}
/**
* Specifies to host which variables might be used by the plugin.
*
* Used in EditActivity, before setResult().
*
* @param intentToHost the intent being returned to the host
* @param variableNames array of relevant variable names
*/
public static void addRelevantVariableList( Intent intentToHost, String [] variableNames ) {
intentToHost.putExtra( BUNDLE_KEY_RELEVANT_VARIABLES, variableNames );
}
/**
* Validate a variable name.
*
* The basic requirement for variables from a plugin is that they must be all lower-case.
*
* @param varName name to check
*/
public static boolean variableNameValid( String varName ) {
boolean validFlag = false;
if ( varName == null )
Log.d( TAG, "variableNameValid: null name" );
else {
if ( VARIABLE_NAME_MATCH_PATTERN == null )
VARIABLE_NAME_MATCH_PATTERN = Pattern.compile( VARIABLE_NAME_MATCH_EXPRESSION, 0 );
if ( VARIABLE_NAME_MATCH_PATTERN.matcher( varName ).matches() ) {
if ( variableNameIsLocal( varName ) )
validFlag = true;
else
Log.d( TAG, "variableNameValid: name not local: " + varName );
}
else
Log.d( TAG, "variableNameValid: invalid name: " + varName );
}
return validFlag;
}
/**
* Allows the plugin/host to indicate to each other a set of variables which they are referencing.
* The host may use this to e.g. show a variable selection list in it's UI.
* The host should use this if it previously indicated to the plugin that it supports relevant vars
*
* @param fromHostIntentExtras usually from getIntent().getExtras()
* @return variableNames an array of relevant variable names
*/
public static String [] getRelevantVariableList( Bundle fromHostIntentExtras ) {
String [] relevantVars = (String []) getBundleValueSafe( fromHostIntentExtras, BUNDLE_KEY_RELEVANT_VARIABLES, String [].class, "getRelevantVariableList" );
if ( relevantVars == null )
relevantVars = new String [0];
return relevantVars;
}
/**
* Used by: plugin QueryReceiver, FireReceiver
*
* Add a bundle of variable name/value pairs.
*
* Names must be valid Tasker local variable names.
* Values must be String, String [] or ArrayList<String>
* Null values cause deletion of possible already-existing variables
* A null value where the variable does not already exist results in attempted deletion
* of any existing array indices (%arr1, %arr2 etc)
*
* @param resultExtras the result extras from the receiver onReceive (from a call to getResultExtras())
* @param variables the variables to send
* @see Setting#hostSupportsVariableReturn(Bundle)
* @see #variableNameValid(String)
*/
public static void addVariableBundle( Bundle resultExtras, Bundle variables ) {
resultExtras.putBundle( EXTRA_VARIABLES_BUNDLE, variables );
}
/**
* Used by: plugin EditActivity
*
* Specify the encoding for a set of bundle keys.
*
* This is completely optional and currently only necessary if using Setting#setVariableReplaceKeys
* where the corresponding values of some of the keys specified are JSON encoded.
*
* @param resultBundleToHost the bundle being returned to the host
* @param keys the keys being returned to the host which are encoded in some way
* @param encoding the encoding of the values corresponding to the specified keys
* @see #setVariableReplaceKeys(Bundle,String[])
* @see #hostSupportsKeyEncoding(Bundle, Encoding)
*/
public static void setKeyEncoding( Bundle resultBundleToHost, String [] keys, Encoding encoding ) {
if ( Encoding.JSON.equals( encoding ) )
addStringArrayToBundleAsString(
keys, resultBundleToHost, BUNDLE_KEY_ENCODING_JSON_KEYS, "setValueEncoding"
);
else
Log.e( TAG, "unknown encoding: " + encoding );
}
// ----------------------------- SETTING PLUGIN ONLY --------------------------------- //
public static class Setting {
/**
* Variable name into which a description of any error that occurred can be placed
* for the user to process.
*
* Should *only* be set when the BroadcastReceiver result code indicates a failure.
*
* Note that the user needs to have configured the task to continue after failure of the plugin
* action otherwise they will not be able to make use of the error message.
*
* For use with #addRelevantVariableList(Intent, String[]) and #addVariableBundle(Bundle, Bundle)
*
*/
public final static String VARNAME_ERROR_MESSAGE = VARIABLE_PREFIX + "errmsg";
/**
* @see #setVariableReplaceKeys(Bundle, String[])
*/
private final static String BUNDLE_KEY_VARIABLE_REPLACE_STRINGS = EXTRAS_PREFIX + "VARIABLE_REPLACE_KEYS";
/**
* @see #requestTimeoutMS(android.content.Intent, int)
*/
private final static String EXTRA_REQUESTED_TIMEOUT = EXTRAS_PREFIX + "REQUESTED_TIMEOUT";
/**
* @see #requestTimeoutMS(android.content.Intent, int)
*/
public final static int REQUESTED_TIMEOUT_MS_NONE = 0;
/**
* @see #requestTimeoutMS(android.content.Intent, int)
*/
public final static int REQUESTED_TIMEOUT_MS_MAX = 3599000;
/**
* @see #requestTimeoutMS(android.content.Intent, int)
*/
public final static int REQUESTED_TIMEOUT_MS_NEVER = REQUESTED_TIMEOUT_MS_MAX + 1000;
/**
* @see #signalFinish(Context, Intent, int, Bundle)
* @see #addCompletionIntent(Intent, Intent,ComponentName, boolean)
*/
private final static String EXTRA_PLUGIN_COMPLETION_INTENT = EXTRAS_PREFIX + "COMPLETION_INTENT";
/**
* @see #signalFinish(Context, Intent, int, Bundle)
* @see Host#getSettingResultCode(Intent)
*/
public final static String EXTRA_RESULT_CODE = EXTRAS_PREFIX + "RESULT_CODE";
/**
*
* @see #signalFinish(Context, Intent, int, Bundle)
* @see #addCompletionIntent(Intent, Intent,ComponentName, boolean)
*/
public final static String EXTRA_CALL_SERVICE_PACKAGE = BASE_KEY + ".EXTRA_CALL_SERVICE_PACKAGE";
public final static String EXTRA_CALL_SERVICE = BASE_KEY + ".EXTRA_CALL_SERVICE";
public final static String EXTRA_CALL_SERVICE_FOREGROUND = BASE_KEY + ".EXTRA_CALL_SERVICE_FOREGROUND";
/**
* @see #signalFinish(Context, Intent, int, Bundle)
* @see Host#getSettingResultCode(Intent)
*/
public final static int RESULT_CODE_OK = Activity.RESULT_OK;
public final static int RESULT_CODE_OK_MINOR_FAILURES = Activity.RESULT_FIRST_USER;
public final static int RESULT_CODE_FAILED = Activity.RESULT_FIRST_USER + 1;
public final static int RESULT_CODE_PENDING = Activity.RESULT_FIRST_USER + 2;
public final static int RESULT_CODE_UNKNOWN = Activity.RESULT_FIRST_USER + 3;
/**
* If a plugin wants to define it's own error codes, start numbering them here.
* The code will be placed in an error variable (%err in the case of Tasker) for
* the user to process after the plugin action.
*/
public final static int RESULT_CODE_FAILED_PLUGIN_FIRST = Activity.RESULT_FIRST_USER + 9;
/**
* Used by: plugin EditActivity.
*
* Indicates to plugin that host will replace variables in specified bundle keys.
*
* Replacement takes place every time the setting is fired, before the bundle is
* passed to the plugin FireReceiver.
*
* @param extrasFromHost intent extras from the intent received by the edit activity
* @see #setVariableReplaceKeys(Bundle, String[])
*/
public static boolean hostSupportsOnFireVariableReplacement( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT );
}
/**
* Used by: plugin EditActivity.
*
* Description as above.
*
* This version also includes backwards compatibility with pre 4.2 Tasker versions.
* At some point this function will be deprecated.
*
* @param editActivity the plugin edit activity, needed to test calling Tasker version
* @see #setVariableReplaceKeys(Bundle, String[])
*/
public static boolean hostSupportsOnFireVariableReplacement( Activity editActivity ) {
boolean supportedFlag = hostSupportsOnFireVariableReplacement( editActivity.getIntent().getExtras() );
if ( ! supportedFlag ) {
ComponentName callingActivity = editActivity.getCallingActivity();
if ( callingActivity == null )
Log.w( TAG, "hostSupportsOnFireVariableReplacement: null callingActivity, defaulting to false" );
else {
String callerPackage = callingActivity.getPackageName();
// Tasker only supporteed this from 1.0.10
supportedFlag =
( callerPackage.startsWith( BASE_KEY ) ) &&
( getPackageVersionCode( editActivity.getPackageManager(), callerPackage ) > FIRST_ON_FIRE_VARIABLES_TASKER_VERSION )
;
}
}
return supportedFlag;
}
public static boolean hostSupportsSynchronousExecution( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_SYNCHRONOUS_EXECUTION );
}
/**
* Request the host to wait the specified number of milliseconds before continuing.
* Note that the host may choose to ignore the request.
*
* Maximum value is REQUESTED_TIMEOUT_MS_MAX.
* Also available are REQUESTED_TIMEOUT_MS_NONE (continue immediately without waiting
* for the plugin to finish) and REQUESTED_TIMEOUT_MS_NEVER (wait forever for
* a result).
*
* Used in EditActivity, before setResult().
*
* @param intentToHost the intent being returned to the host
* @param timeoutMS
*/
public static void requestTimeoutMS( Intent intentToHost, int timeoutMS ) {
if ( timeoutMS < 0 )
Log.w( TAG, "requestTimeoutMS: ignoring negative timeout (" + timeoutMS + ")" );
else {
if (
( timeoutMS > REQUESTED_TIMEOUT_MS_MAX ) &&
( timeoutMS != REQUESTED_TIMEOUT_MS_NEVER )
) {
Log.w( TAG, "requestTimeoutMS: requested timeout " + timeoutMS + " exceeds maximum, setting to max (" + REQUESTED_TIMEOUT_MS_MAX + ")" );
timeoutMS = REQUESTED_TIMEOUT_MS_MAX;
}
intentToHost.putExtra( EXTRA_REQUESTED_TIMEOUT, timeoutMS );
}
}
/**
* Used by: plugin EditActivity
*
* Indicates to host which bundle keys should be replaced.
*
* @param resultBundleToHost the bundle being returned to the host
* @param listOfKeyNames which bundle keys to replace variables in when setting fires
* @see #hostSupportsOnFireVariableReplacement(Bundle)
* @see #setKeyEncoding(Bundle,String[],Encoding)
*/
public static void setVariableReplaceKeys( Bundle resultBundleToHost, String [] listOfKeyNames ) {
addStringArrayToBundleAsString(
listOfKeyNames, resultBundleToHost, BUNDLE_KEY_VARIABLE_REPLACE_STRINGS,
"setVariableReplaceKeys"
);
}
public static boolean hasVariableReplaceKeys(Bundle resultBundleToHost) {
return resultBundleToHost.containsKey(BUNDLE_KEY_VARIABLE_REPLACE_STRINGS);
}
/**
* Used by: plugin FireReceiver
*
* Indicates to plugin whether the host will process variables which it passes back
*
* @param extrasFromHost intent extras from the intent received by the FireReceiver
* @see #signalFinish(Context, Intent, int, Bundle)
*/
public static boolean hostSupportsVariableReturn( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_RETURN_VARIABLES );
}
/**
* Used by: plugin FireReceiver
*
* Tell the host that the plugin has finished execution.
*
* This should only be used if RESULT_CODE_PENDING was returned by FireReceiver.onReceive().
*
* @param originalFireIntent the intent received from the host (via onReceive())
* @param resultCode level of success in performing the settings
* @param vars any variables that the plugin wants to set in the host
* @see #hostSupportsSynchronousExecution(Bundle)
*/
public static boolean signalFinish( Context context, Intent originalFireIntent, int resultCode, Bundle vars ) {
String errorPrefix = "signalFinish: ";
boolean okFlag = false;
String completionIntentString = (String) getExtraValueSafe( originalFireIntent, Setting.EXTRA_PLUGIN_COMPLETION_INTENT, String.class, "signalFinish" );
if ( completionIntentString != null ) {
Uri completionIntentUri = null;
try {
completionIntentUri = Uri.parse( completionIntentString );
}
// should only throw NullPointer but don't particularly trust it
catch ( Exception e ) {
Log.w( TAG, errorPrefix + "couldn't parse " + completionIntentString );
}
if ( completionIntentUri != null ) {
try {
Intent completionIntent = Intent.parseUri( completionIntentString, Intent.URI_INTENT_SCHEME );
completionIntent.putExtra( EXTRA_RESULT_CODE, resultCode );
if ( vars != null )
completionIntent.putExtra( EXTRA_VARIABLES_BUNDLE, vars );
String callServicePackage = (String) getExtraValueSafe(completionIntent, Setting.EXTRA_CALL_SERVICE_PACKAGE, String.class, "signalFinish");
String callService = (String) getExtraValueSafe(completionIntent, Setting.EXTRA_CALL_SERVICE, String.class, "signalFinish");
Boolean foreground = (Boolean) getExtraValueSafe(completionIntent, Setting.EXTRA_CALL_SERVICE_FOREGROUND, Boolean.class, "signalFinish");
if (callServicePackage != null && callService != null && foreground != null) {
completionIntent.setComponent(new ComponentName(callServicePackage, callService));
if (foreground && android.os.Build.VERSION.SDK_INT >= 26) {
context.startForegroundService(completionIntent);
} else {
context.startService(completionIntent);
}
} else {
context.sendBroadcast(completionIntent);
}
okFlag = true;
}
catch ( URISyntaxException e ) {
Log.w( TAG, errorPrefix + "bad URI: " + completionIntentUri );
}
}
}
return okFlag;
}
/**
* Check for a hint on the timeout value the host is using.
* Used by: plugin FireReceiver.
* Requires Tasker 4.7+
*
* @param extrasFromHost intent extras from the intent received by the FireReceiver
* @return timeoutMS the hosts timeout setting for the action or -1 if no hint is available.
*
* @see #REQUESTED_TIMEOUT_MS_NONE, REQUESTED_TIMEOUT_MS_MAX, REQUESTED_TIMEOUT_MS_NEVER
*/
public static int getHintTimeoutMS( Bundle extrasFromHost ) {
int timeoutMS = -1;
Bundle hintsBundle = (Bundle) TaskerPlugin.getBundleValueSafe( extrasFromHost, EXTRA_HINTS_BUNDLE, Bundle.class, "getHintTimeoutMS" );
if ( hintsBundle != null ) {
Integer val = (Integer) getBundleValueSafe( hintsBundle, BUNDLE_KEY_HINT_TIMEOUT_MS, Integer.class, "getHintTimeoutMS" );
if ( val != null )
timeoutMS = val;
}
return timeoutMS;
}
}
// ----------------------------- CONDITION/EVENT PLUGIN ONLY --------------------------------- //
public static class Condition {
/**
* @see #getResultReceiver(Intent)
*/
public final static String EXTRA_RESULT_RECEIVER = BASE_KEY + ".EXTRA_RESULT_RECEIVER";
/**
* Used by: plugin QueryReceiver
*
* Indicates to plugin whether the host will process variables which it passes back
*
* @param extrasFromHost intent extras from the intent received by the QueryReceiver
* @see #addVariableBundle(Bundle, Bundle)
*/
public static boolean hostSupportsVariableReturn( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_CONDITION_RETURN_VARIABLES );
}
public static ResultReceiver getResultReceiver(Intent intentFromHost) {
if (intentFromHost == null) {
return null;
}
return (ResultReceiver) getExtraValueSafe(intentFromHost, EXTRA_RESULT_RECEIVER, ResultReceiver.class, "getResultReceiver");
}
}
// ----------------------------- EVENT PLUGIN ONLY --------------------------------- //
public static class Event {
public final static String PASS_THROUGH_BUNDLE_MESSAGE_ID_KEY = BASE_KEY + ".MESSAGE_ID";
private final static String EXTRA_REQUEST_QUERY_PASS_THROUGH_DATA = EXTRAS_PREFIX + "PASS_THROUGH_DATA";
/**
* @param extrasFromHost intent extras from the intent received by the QueryReceiver
* @see #addPassThroughData(Intent, Bundle)
*/
public static boolean hostSupportsRequestQueryDataPassThrough( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_REQUEST_QUERY_DATA_PASS_THROUGH );
}
/**
* Specify a bundle of data (probably representing whatever change happened in the condition)
* which will be included in the QUERY_CONDITION broadcast sent by the host for each
* event instance of the plugin.
*
* The minimal purpose is to enable the plugin to associate a QUERY_CONDITION to the
* with the REQUEST_QUERY that caused it.
*
* Note that for security reasons it is advisable to also store a message ID with the bundle
* which can be compared to known IDs on receipt. The host cannot validate the source of
* REQUEST_QUERY intents so fake data may be passed. Replay attacks are also possible.
* addPassThroughMesssageID() can be used to add an ID if the plugin doesn't wish to add it's
* own ID to the pass through bundle.
*
* Note also that there are several situations where REQUEST_QUERY will not result in a
* QUERY_CONDITION intent (e.g. event throttling by the host), so plugin-local data
* indexed with a message ID needs to be timestamped and eventually timed-out.
*
* This function can be called multiple times, each time all keys in data will be added to
* that of previous calls.
*
* @param requestQueryIntent intent being sent to the host
* @param data the data to be passed-through
* @see #hostSupportsRequestQueryDataPassThrough(Bundle)
* @see #retrievePassThroughData(Intent)
* @see #addPassThroughMessageID
*
*/
public static void addPassThroughData( Intent requestQueryIntent, Bundle data ) {
Bundle passThroughBundle = retrieveOrCreatePassThroughBundle( requestQueryIntent );
passThroughBundle.putAll( data );
}
/**
* Retrieve the pass through data from a QUERY_REQUEST from the host which was generated
* by a REQUEST_QUERY from the plugin.
*
* Note that if addPassThroughMessageID() was previously called, the data will contain an extra
* key TaskerPlugin.Event.PASS_THOUGH_BUNDLE_MESSAGE_ID_KEY.
*
* @param queryConditionIntent QUERY_REQUEST sent from host
* @return data previously added to the REQUEST_QUERY intent
* @see #hostSupportsRequestQueryDataPassThrough(Bundle)
* @see #addPassThroughData(Intent,Bundle)
*/
public static Bundle retrievePassThroughData( Intent queryConditionIntent ) {
return (Bundle) getExtraValueSafe(
queryConditionIntent,
EXTRA_REQUEST_QUERY_PASS_THROUGH_DATA,
Bundle.class,
"retrievePassThroughData"
);
}
/**
* Add a message ID to a REQUEST_QUERY intent which will then be included in the corresponding
* QUERY_CONDITION broadcast sent by the host for each event instance of the plugin.
*
* The minimal purpose is to enable the plugin to associate a QUERY_CONDITION to the
* with the REQUEST_QUERY that caused it. It also allows the message to be verified
* by the plugin to prevent e.g. replay attacks
*
* @param requestQueryIntent intent being sent to the host
* @return a guaranteed non-repeating within 100 calls message ID
* @see #hostSupportsRequestQueryDataPassThrough(Bundle)
* @see #retrievePassThroughData(Intent)
* @return an ID for the bundle so it can be identified and the caller verified when it is again received by the plugin
*
*/
public static int addPassThroughMessageID( Intent requestQueryIntent ) {
Bundle passThroughBundle = retrieveOrCreatePassThroughBundle( requestQueryIntent );
int id = getPositiveNonRepeatingRandomInteger();
passThroughBundle.putInt( PASS_THROUGH_BUNDLE_MESSAGE_ID_KEY, id );
return id;
}
/*
* Retrieve the pass through data from a QUERY_REQUEST from the host which was generated
* by a REQUEST_QUERY from the plugin.
*
* @param queryConditionIntent QUERY_REQUEST sent from host
* @return the ID which was passed through by the host, or -1 if no ID was found
* @see #hostSupportsRequestQueryDataPassThrough(Bundle)
* @see #addPassThroughData(Intent,Bundle)
*/
public static int retrievePassThroughMessageID( Intent queryConditionIntent ) {
int toReturn = -1;
Bundle passThroughData = Event.retrievePassThroughData( queryConditionIntent );
if ( passThroughData != null ) {
Integer id = (Integer) getBundleValueSafe(
passThroughData,
PASS_THROUGH_BUNDLE_MESSAGE_ID_KEY,
Integer.class,
"retrievePassThroughMessageID"
);
if ( id != null )
toReturn = id;
}
return toReturn;
}
// internal use
private static Bundle retrieveOrCreatePassThroughBundle( Intent requestQueryIntent ) {
Bundle passThroughBundle;
if ( requestQueryIntent.hasExtra( EXTRA_REQUEST_QUERY_PASS_THROUGH_DATA ) )
passThroughBundle = requestQueryIntent.getBundleExtra( EXTRA_REQUEST_QUERY_PASS_THROUGH_DATA );
else {
passThroughBundle = new Bundle();
requestQueryIntent.putExtra( EXTRA_REQUEST_QUERY_PASS_THROUGH_DATA, passThroughBundle );
}
return passThroughBundle;
}
}
// ---------------------------------- HOST ----------------------------------------- //
public static class Host {
/**
* Tell the plugin what capabilities the host support. This should be called when sending
* intents to any EditActivity, FireReceiver or QueryReceiver.
*
* @param toPlugin the intent we're sending
* @return capabilities one or more of the EXTRA_HOST_CAPABILITY_XXX flags
*/
public static Intent addCapabilities( Intent toPlugin, int capabilities ) {
return toPlugin.putExtra( EXTRA_HOST_CAPABILITIES, capabilities );
}
/**
* Add an intent to the fire intent before it goes to the plugin FireReceiver, which the plugin
* can use to signal when it is finished. Only use if @code{pluginWantsSychronousExecution} is true.
*
* @param fireIntent fire intent going to the plugin
* @param completionIntent intent which will signal the host that the plugin is finished.
* Implementation is host-dependent.
*/
public static void addCompletionIntent(Intent fireIntent, Intent completionIntent, ComponentName callService, boolean foreground) {
if (callService != null) {
completionIntent.putExtra(Setting.EXTRA_CALL_SERVICE_PACKAGE, callService.getPackageName());
completionIntent.putExtra(Setting.EXTRA_CALL_SERVICE, callService.getClassName());
completionIntent.putExtra(Setting.EXTRA_CALL_SERVICE_FOREGROUND, foreground);
}
fireIntent.putExtra(
Setting.EXTRA_PLUGIN_COMPLETION_INTENT,
completionIntent.toUri(Intent.URI_INTENT_SCHEME)
);
}
/**
* When a setting plugin is finished, it sends the host the intent which was passed to it
* via @code{addCompletionIntent}.
*
* @param completionIntent intent returned from the plugin when it finished.
* @return resultCode measure of plugin success, defaults to UNKNOWN
*/
public static int getSettingResultCode( Intent completionIntent ) {
Integer val = (Integer) getExtraValueSafe( completionIntent, Setting.EXTRA_RESULT_CODE, Integer.class, "getSettingResultCode" );
return ( val == null ) ? Setting.RESULT_CODE_UNKNOWN : val;
}
/**
* Extract a bundle of variables from an intent received from the FireReceiver. This
* should be called if the host previously indicated to the plugin
* that it supports setting variable return.
*
* @param resultExtras getResultExtras() from BroadcastReceiver:onReceive()
* @return variables a bundle of variable name/value pairs
* @see #addCapabilities(Intent, int)
*/
public static Bundle getVariablesBundle( Bundle resultExtras ) {
return (Bundle) getBundleValueSafe(
resultExtras, EXTRA_VARIABLES_BUNDLE, Bundle.class, "getVariablesBundle"
);
}
/**
* Inform a setting plugin of the timeout value the host is using.
*
* @param toPlugin the intent we're sending
* @param timeoutMS the hosts timeout setting for the action. Note that this may differ from
* that which the plugin requests.
* @see #REQUESTED_TIMEOUT_MS_NONE, REQUESTED_TIMEOUT_MS_MAX, REQUESTED_TIMEOUT_MS_NEVER
*/
public static void addHintTimeoutMS( Intent toPlugin, int timeoutMS ) {
getHintsBundle( toPlugin, "addHintTimeoutMS" ).putInt( BUNDLE_KEY_HINT_TIMEOUT_MS, timeoutMS );
}
private static Bundle getHintsBundle( Intent intent, String funcName ) {
Bundle hintsBundle = (Bundle) getExtraValueSafe( intent, EXTRA_HINTS_BUNDLE, Bundle.class, funcName );
if ( hintsBundle == null ) {
hintsBundle = new Bundle();
intent.putExtra( EXTRA_HINTS_BUNDLE, hintsBundle );
}
return hintsBundle;
}
public static boolean haveRequestedTimeout( Bundle extrasFromPluginEditActivity ) {
return extrasFromPluginEditActivity.containsKey( Setting.EXTRA_REQUESTED_TIMEOUT );
}
public static int getRequestedTimeoutMS( Bundle extrasFromPluginEditActivity ) {
return
(Integer) getBundleValueSafe(
extrasFromPluginEditActivity, Setting.EXTRA_REQUESTED_TIMEOUT, Integer.class, "getRequestedTimeout"
)
;
}
public static String [] getSettingVariableReplaceKeys( Bundle fromPluginEditActivity ) {
return getStringArrayFromBundleString(
fromPluginEditActivity, Setting.BUNDLE_KEY_VARIABLE_REPLACE_STRINGS,
"getSettingVariableReplaceKeys"
);
}
public static String [] getKeysWithEncoding( Bundle fromPluginEditActivity, Encoding encoding ) {
String [] toReturn = null;
if ( Encoding.JSON.equals( encoding ) )
toReturn = getStringArrayFromBundleString(
fromPluginEditActivity, TaskerPlugin.BUNDLE_KEY_ENCODING_JSON_KEYS,
"getKeyEncoding:JSON"
);
else
Log.w( TAG, "Host.getKeyEncoding: unknown encoding " + encoding );
return toReturn;
}
public static boolean haveRelevantVariables( Bundle b ) {
return b.containsKey( BUNDLE_KEY_RELEVANT_VARIABLES );
}
public static void cleanRelevantVariables( Bundle b ) {
b.remove( BUNDLE_KEY_RELEVANT_VARIABLES );
}
public static void cleanHints( Bundle extras ) {
extras.remove( TaskerPlugin.EXTRA_HINTS_BUNDLE );
}
public static void cleanRequestedTimeout( Bundle extras ) {
extras.remove( Setting.EXTRA_REQUESTED_TIMEOUT );
}
public static void cleanSettingReplaceVariables( Bundle b ) {
b.remove( Setting.BUNDLE_KEY_VARIABLE_REPLACE_STRINGS );
}
}
// ---------------------------------- HELPER FUNCTIONS -------------------------------- //
private static Object getBundleValueSafe( Bundle b, String key, Class<?> expectedClass, String funcName ) {
Object value = null;
if ( b != null ) {
if ( b.containsKey( key ) ) {
Object obj = b.get( key );
if ( obj == null )
Log.w( TAG, funcName + ": " + key + ": null value" );
else if ( obj.getClass() != expectedClass )
Log.w( TAG, funcName + ": " + key + ": expected " + expectedClass.getClass().getName() + ", got " + obj.getClass().getName() );
else
value = obj;
}
}
return value;
}
private static Object getExtraValueSafe( Intent i, String key, Class<?> expectedClass, String funcName ) {
return ( i.hasExtra( key ) ) ?
getBundleValueSafe( i.getExtras(), key, expectedClass, funcName ) :
null;
}
private static boolean hostSupports( Bundle extrasFromHost, int capabilityFlag ) {
Integer flags = (Integer) getBundleValueSafe( extrasFromHost, EXTRA_HOST_CAPABILITIES, Integer.class, "hostSupports" );
return
( flags != null ) &&
( ( flags & capabilityFlag ) > 0 )
;
}
public static int getPackageVersionCode( PackageManager pm, String packageName ) {
int code = -1;
if ( pm != null ) {
try {
PackageInfo pi = pm.getPackageInfo( packageName, 0 );
if ( pi != null )
code = pi.versionCode;
}
catch ( Exception e ) {
Log.e( TAG, "getPackageVersionCode: exception getting package info" );
}
}
return code;
}
private static boolean variableNameIsLocal( String varName ) {
int digitCount = 0;
int length = varName.length();
for ( int x = 0; x < length; x++ ) {
char ch = varName.charAt( x );
if ( Character.isUpperCase( ch ) )
return false;
else if ( Character.isDigit( ch ) )
digitCount++;
}
if ( digitCount == ( varName.length() - 1 ) )
return false;
return true;
}
private static String [] getStringArrayFromBundleString( Bundle bundle, String key, String funcName ) {
String spec = (String) getBundleValueSafe( bundle, key, String.class, funcName );
String [] toReturn = null;
if ( spec != null )
toReturn = spec.split( " " );
return toReturn;
}
private static void addStringArrayToBundleAsString( String [] toAdd, Bundle bundle, String key, String callerName ) {
StringBuilder builder = new StringBuilder();
if ( toAdd != null ) {
for ( String keyName : toAdd ) {
if ( keyName.contains( " " ) )
Log.w( TAG, callerName + ": ignoring bad keyName containing space: " + keyName );
else {
if ( builder.length() > 0 )
builder.append( ' ' );
builder.append( keyName );
}
if ( builder.length() > 0 )
bundle.putString( key, builder.toString() );
}
}
}
// state tracking for random number sequence
private static int [] lastRandomsSeen = null;
private static int randomInsertPointer = 0;
private static SecureRandom sr = null;
/**
* Generate a sequence of secure random positive integers which is guaranteed not to repeat
* in the last 100 calls to this function.
*
* @return a random positive integer
*/
public static int getPositiveNonRepeatingRandomInteger() {
// initialize on first call
if ( sr == null ) {
sr = new SecureRandom();
lastRandomsSeen = new int[RANDOM_HISTORY_SIZE];
for ( int x = 0; x < lastRandomsSeen.length; x++ )
lastRandomsSeen[x] = -1;
}
int toReturn;
do {
// pick a number
toReturn = sr.nextInt( Integer.MAX_VALUE );
// check we havn't see it recently
for ( int seen : lastRandomsSeen ) {
if ( seen == toReturn ) {
toReturn = -1;
break;
}
}
}
while ( toReturn == -1 );
// update history
lastRandomsSeen[randomInsertPointer] = toReturn;
randomInsertPointer = ( randomInsertPointer + 1 ) % lastRandomsSeen.length;
return toReturn;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Utils.java
================================================
package com.markadamson.taskerplugin.homeassistant;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
public class Utils {
public static void initVariableSelectUI(String[] variables, View button, final EditText destination) {
final ArrayAdapter<String> adapter = new ArrayAdapter<>(button.getContext(), android.R.layout.select_dialog_item, variables);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builderSingle = new AlertDialog.Builder(v.getContext());
builderSingle.setTitle("Variable Select");
builderSingle.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builderSingle.setAdapter(adapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String variable = adapter.getItem(which);
assert variable != null;
int start = Math.max(destination.getSelectionStart(), 0);
int end = Math.max(destination.getSelectionEnd(), 0);
destination.getText().replace(Math.min(start, end), Math.max(start, end),
variable, 0, variable.length());
}
});
builderSingle.show();
}
});
}
public static void checkBatteryOptimisation(final Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = activity.getPackageName();
PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
new android.app.AlertDialog.Builder(activity)
.setTitle(R.string.disable_battery_optimization)
.setMessage(R.string.battery_optimization_dialog)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
activity.startActivity(new Intent()
.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS));
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
}
}
private Utils() {
throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/GetStatePluginBundleValues.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.bundle;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.markadamson.taskerplugin.homeassistant.Constants;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.twofortyfouram.assertion.BundleAssertions;
import com.twofortyfouram.log.Lumberjack;
import com.twofortyfouram.spackle.AppBuildInfo;
import net.jcip.annotations.ThreadSafe;
import java.util.UUID;
import static com.twofortyfouram.assertion.Assertions.assertNotEmpty;
import static com.twofortyfouram.assertion.Assertions.assertNotNull;
/**
* Manages the {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} for this
* plug-in.
*/
@ThreadSafe
public final class GetStatePluginBundleValues {
/**
* Type: {@code String}.
* <p>
* Server UUID as string.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_SERVER
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_SERVER"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Domain/service to call.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_ENTITY
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_ENTITY"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* State output variable.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_VARIABLE
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_VARIABLE"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Attributes output variable.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_ATTRS_VARIABLE
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_ATTRS_VARIABLE"; //$NON-NLS-1$
/**
* Type: {@code int}.
* <p>
* versionCode of the plug-in that saved the Bundle.
*/
/*
* This extra is not strictly required, however it makes backward and forward compatibility
* significantly easier. For example, suppose a bug is found in how some version of the plug-in
* stored its Bundle. By having the version, the plug-in can better detect when such bugs occur.
*/
@NonNull
public static final String BUNDLE_EXTRA_INT_VERSION_CODE
= "com.markadamson.taskerplugin.homeassistant.extra.INT_VERSION_CODE"; //$NON-NLS-1$
/**
* Method to verify the content of the bundle are correct.
* <p>
* This method will not mutate {@code bundle}.
*
* @param bundle bundle to verify. May be null, which will always return false.
* @return true if the Bundle is valid, false if the bundle is invalid.
*/
public static boolean isBundleValid(@Nullable final Bundle bundle) {
if (null == bundle) {
return false;
}
try {
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_SERVER, false, false);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_ENTITY, false, false);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_VARIABLE, false, true);
BundleAssertions.assertHasInt(bundle, BUNDLE_EXTRA_INT_VERSION_CODE);
BundleAssertions.assertHasInt(bundle, Constants.BUNDLE_EXTRA_BUNDLE_TYPE,
Constants.BUNDLE_GET_STATE, Constants.BUNDLE_GET_STATE);
int bundleVer = bundle.getInt(BUNDLE_EXTRA_INT_VERSION_CODE), expectedCount = 5;
// Bundle may now have replacement vars key:
if (bundleVer >= 5 && TaskerPlugin.Setting.hasVariableReplaceKeys(bundle))
expectedCount++;
if (bundleVer >= 6) {
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_ATTRS_VARIABLE, false, true);
expectedCount++;
}
BundleAssertions.assertKeyCount(bundle, expectedCount);
} catch (final AssertionError e) {
Lumberjack.e("Bundle failed verification%s", e); //$NON-NLS-1$
return false;
}
return true;
}
/**
* @param context Application context.
* @param server The server UUID.
* @param entity The domain/service to call.
* @param stateVariable The state output variable.
* @param attrsVariable The attributes output variable.
* @return A plug-in bundle.
*/
@NonNull
public static Bundle generateBundle(@NonNull final Context context,
@NonNull final UUID server,
@NonNull final String entity,
@NonNull final String stateVariable,
@NonNull final String attrsVariable) {
assertNotNull(context, "context"); //$NON-NLS-1$
assertNotNull(server, "server"); //$NON-NLS-1$
assertNotEmpty(entity, "service"); //$NON-NLS-1$
final Bundle result = new Bundle();
result.putInt(BUNDLE_EXTRA_INT_VERSION_CODE, AppBuildInfo.getVersionCode(context));
result.putInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE, Constants.BUNDLE_GET_STATE);
result.putString(BUNDLE_EXTRA_STRING_SERVER, server.toString());
result.putString(BUNDLE_EXTRA_STRING_ENTITY, entity);
result.putString(BUNDLE_EXTRA_STRING_VARIABLE, stateVariable);
result.putString(BUNDLE_EXTRA_STRING_ATTRS_VARIABLE, attrsVariable);
TaskerPlugin.Setting.setVariableReplaceKeys(result, new String[] {BUNDLE_EXTRA_STRING_ENTITY});
return result;
}
/**
* @param bundle A valid plug-in bundle.
* @return The message inside the plug-in bundle.
*/
@NonNull
public static UUID getServer(@NonNull final Bundle bundle) {
return UUID.fromString(bundle.getString(BUNDLE_EXTRA_STRING_SERVER));
}
@NonNull
public static String getEntity(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_ENTITY);
}
@NonNull
public static String getStateVariable(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_VARIABLE);
}
@NonNull
public static String getAttrsVariable(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_ATTRS_VARIABLE, "");
}
/**
* Private constructor prevents instantiation
*
* @throws UnsupportedOperationException because this class cannot be instantiated.
*/
private GetStatePluginBundleValues() {
throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValues.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.bundle;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.markadamson.taskerplugin.homeassistant.Constants;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.twofortyfouram.assertion.BundleAssertions;
import com.twofortyfouram.log.Lumberjack;
import com.twofortyfouram.spackle.AppBuildInfo;
import net.jcip.annotations.ThreadSafe;
import java.util.UUID;
import static com.twofortyfouram.assertion.Assertions.assertNotEmpty;
import static com.twofortyfouram.assertion.Assertions.assertNotNull;
/**
* Manages the {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} for this
* plug-in.
*/
@ThreadSafe
public final class PluginBundleValues {
/**
* Type: {@code String}.
* <p>
* Server UUID as string.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_SERVER
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_SERVER"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Domain/service to call.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_SERVICE
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_SERVICE"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Service data (JSON, optional).
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_DATA
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_DATA"; //$NON-NLS-1$
/**
* Type: {@code int}.
* <p>
* versionCode of the plug-in that saved the Bundle.
*/
/*
* This extra is not strictly required, however it makes backward and forward compatibility
* significantly easier. For example, suppose a bug is found in how some version of the plug-in
* stored its Bundle. By having the version, the plug-in can better detect when such bugs occur.
*/
@NonNull
public static final String BUNDLE_EXTRA_INT_VERSION_CODE
= "com.markadamson.taskerplugin.homeassistant.extra.INT_VERSION_CODE"; //$NON-NLS-1$
/**
* Method to verify the content of the bundle are correct.
* <p>
* This method will not mutate {@code bundle}.
*
* @param bundle bundle to verify. May be null, which will always return false.
* @return true if the Bundle is valid, false if the bundle is invalid.
*/
public static boolean isBundleValid(@Nullable final Bundle bundle) {
if (null == bundle) {
return false;
}
try {
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_SERVER, false, false);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_SERVICE, false, false);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_DATA, false, true);
BundleAssertions.assertHasInt(bundle, BUNDLE_EXTRA_INT_VERSION_CODE);
int bundleVer = bundle.getInt(BUNDLE_EXTRA_INT_VERSION_CODE), expectedCount = 4;
if (bundleVer >= 3) {
BundleAssertions.assertHasInt(bundle, Constants.BUNDLE_EXTRA_BUNDLE_TYPE,
Constants.BUNDLE_CALL_SERVICE, Constants.BUNDLE_CALL_SERVICE);
expectedCount++;
}
// Bundle may now have replacement vars key:
if (bundleVer >= 5 && TaskerPlugin.Setting.hasVariableReplaceKeys(bundle))
expectedCount++;
BundleAssertions.assertKeyCount(bundle, expectedCount);
} catch (final AssertionError e) {
Lumberjack.e("Bundle failed verification%s", e); //$NON-NLS-1$
return false;
}
return true;
}
/**
* @param context Application context.
* @param server The server UUID.
* @param service The domain/service to call.
* @param data The service data to send.
* @return A plug-in bundle.
*/
@NonNull
public static Bundle generateBundle(@NonNull final Context context,
@NonNull final UUID server,
@NonNull final String service,
@NonNull final String data) {
assertNotNull(context, "context"); //$NON-NLS-1$
assertNotNull(server, "server"); //$NON-NLS-1$
assertNotEmpty(service, "service"); //$NON-NLS-1$
final Bundle result = new Bundle();
result.putInt(BUNDLE_EXTRA_INT_VERSION_CODE, AppBuildInfo.getVersionCode(context));
result.putInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE, Constants.BUNDLE_CALL_SERVICE);
result.putString(BUNDLE_EXTRA_STRING_SERVER, server.toString());
result.putString(BUNDLE_EXTRA_STRING_SERVICE, service);
result.putString(BUNDLE_EXTRA_STRING_DATA, data);
TaskerPlugin.Setting.setVariableReplaceKeys(result, new String[] {BUNDLE_EXTRA_STRING_SERVICE, BUNDLE_EXTRA_STRING_DATA});
return result;
}
/**
* @param bundle A valid plug-in bundle.
* @return The message inside the plug-in bundle.
*/
@NonNull
public static UUID getServer(@NonNull final Bundle bundle) {
return UUID.fromString(bundle.getString(BUNDLE_EXTRA_STRING_SERVER));
}
@NonNull
public static String getService(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_SERVICE);
}
@NonNull
public static String getData(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_DATA);
}
/**
* Private constructor prevents instantiation
*
* @throws UnsupportedOperationException because this class cannot be instantiated.
*/
private PluginBundleValues() {
throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/RenderTemplatePluginBundleValues.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.bundle;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.markadamson.taskerplugin.homeassistant.Constants;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.twofortyfouram.assertion.BundleAssertions;
import com.twofortyfouram.log.Lumberjack;
import com.twofortyfouram.spackle.AppBuildInfo;
import net.jcip.annotations.ThreadSafe;
import java.util.UUID;
import static com.twofortyfouram.assertion.Assertions.assertNotEmpty;
import static com.twofortyfouram.assertion.Assertions.assertNotNull;
/**
* Manages the {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} for this
* plug-in.
*/
@ThreadSafe
public final class RenderTemplatePluginBundleValues {
/**
* Type: {@code String}.
* <p>
* Server UUID as string.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_SERVER
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_SERVER"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Template to render.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_TEMPLATE
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_TEMPLATE"; //$NON-NLS-1$
/**
* Type: {@code String}.
* <p>
* Variable to store result in.
*/
@NonNull
public static final String BUNDLE_EXTRA_STRING_VARIABLE
= "com.markadamson.taskerplugin.homeassistant.extra.STRING_VARIABLE"; //$NON-NLS-1$
/**
* Type: {@code int}.
* <p>
* versionCode of the plug-in that saved the Bundle.
*/
/*
* This extra is not strictly required, however it makes backward and forward compatibility
* significantly easier. For example, suppose a bug is found in how some version of the plug-in
* stored its Bundle. By having the version, the plug-in can better detect when such bugs occur.
*/
@NonNull
public static final String BUNDLE_EXTRA_INT_VERSION_CODE
= "com.markadamson.taskerplugin.homeassistant.extra.INT_VERSION_CODE"; //$NON-NLS-1$
/**
* Method to verify the content of the bundle are correct.
* <p>
* This method will not mutate {@code bundle}.
*
* @param bundle bundle to verify. May be null, which will always return false.
* @return true if the Bundle is valid, false if the bundle is invalid.
*/
public static boolean isBundleValid(@Nullable final Bundle bundle) {
if (null == bundle) {
return false;
}
try {
BundleAssertions.assertHasInt(bundle, Constants.BUNDLE_EXTRA_BUNDLE_TYPE,
Constants.BUNDLE_RENDER_TEMPLATE, Constants.BUNDLE_RENDER_TEMPLATE);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_SERVER, false, false);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_TEMPLATE, false, true);
BundleAssertions.assertHasString(bundle, BUNDLE_EXTRA_STRING_VARIABLE, false, false);
BundleAssertions.assertHasInt(bundle, BUNDLE_EXTRA_INT_VERSION_CODE);
int bundleVer = bundle.getInt(BUNDLE_EXTRA_INT_VERSION_CODE), expectedCount = 5;
// Bundle may now have replacement vars key:
if (TaskerPlugin.Setting.hasVariableReplaceKeys(bundle))
expectedCount++;
BundleAssertions.assertKeyCount(bundle, expectedCount);
} catch (final AssertionError e) {
Lumberjack.e("Bundle failed verification%s", e); //$NON-NLS-1$
return false;
}
return true;
}
/**
* @param context Application context.
* @param server The server UUID.
* @param template The domain/template to call.
* @param variable The template variable to send.
* @return A plug-in bundle.
*/
@NonNull
public static Bundle generateBundle(@NonNull final Context context,
@NonNull final UUID server,
@NonNull final String template,
@NonNull final String variable) {
assertNotNull(context, "context"); //$NON-NLS-1$
assertNotNull(server, "server"); //$NON-NLS-1$
assertNotNull(template, "template"); //$NON-NLS-1$
assertNotNull(variable, "variable"); //$NON-NLS-1$
assertNotEmpty(variable, "variable"); //$NON-NLS-1$
final Bundle result = new Bundle();
result.putInt(BUNDLE_EXTRA_INT_VERSION_CODE, AppBuildInfo.getVersionCode(context));
result.putInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE, Constants.BUNDLE_RENDER_TEMPLATE);
result.putString(BUNDLE_EXTRA_STRING_SERVER, server.toString());
result.putString(BUNDLE_EXTRA_STRING_TEMPLATE, template);
result.putString(BUNDLE_EXTRA_STRING_VARIABLE, variable);
TaskerPlugin.Setting.setVariableReplaceKeys(result, new String[] {BUNDLE_EXTRA_STRING_TEMPLATE});
return result;
}
/**
* @param bundle A valid plug-in bundle.
* @return The message inside the plug-in bundle.
*/
@NonNull
public static UUID getServer(@NonNull final Bundle bundle) {
return UUID.fromString(bundle.getString(BUNDLE_EXTRA_STRING_SERVER));
}
@NonNull
public static String getTemplate(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_TEMPLATE);
}
@NonNull
public static String getVariable(@NonNull final Bundle bundle) {
return bundle.getString(BUNDLE_EXTRA_STRING_VARIABLE);
}
/**
* Private constructor prevents instantiation
*
* @throws UnsupportedOperationException because this class cannot be instantiated.
*/
private RenderTemplatePluginBundleValues() {
throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPI.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class HAAPI {
private final HAServer mServer;
public HAAPI(HAServer mServer) {
this.mServer = mServer;
}
public boolean testServer() throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/");
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
InputStream inputStream = httpConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = bufferedReader.readLine();
JSONObject apiResult = new JSONObject(line);
return apiResult.has("message") && "API running.".equals(apiResult.getString("message"));
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
public List<String> getServices() throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/services");
httpConn = (HttpURLConnection) url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
InputStream inputStream = httpConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = bufferedReader.readLine();
List<String> result = new ArrayList<>();
JSONArray apiResults = new JSONArray(line);
for (int d = 0; d < apiResults.length(); d++) {
JSONObject jsonDomain = apiResults.getJSONObject(d);
String strDomain = jsonDomain.getString("domain");
JSONObject jsonServices = jsonDomain.getJSONObject("services");
Iterator<String> keys = jsonServices.keys();
while (keys.hasNext())
result.add(strDomain + "." + keys.next());
}
Collections.sort(result);
return result;
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
public void callService(String domain, String service, String data) throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/services/" + domain + "/" + service);
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
if (data != null &! data.isEmpty()) {
OutputStream os = httpConn.getOutputStream();
os.write(data.getBytes("UTF-8"));
os.close();
}
if (httpConn.getResponseCode() != 200) {
InputStream errorStream = httpConn.getErrorStream();
InputStreamReader errorStreamReader = new InputStreamReader(errorStream);
BufferedReader bufferedReader = new BufferedReader(errorStreamReader);
JSONObject jsonResult = new JSONObject(bufferedReader.readLine());
throw new HAAPIException(jsonResult.getString("message"));
}
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
public List<String> getEntities() throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/states");
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
InputStream inputStream = httpConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = bufferedReader.readLine();
List<String> result = new ArrayList<>();
JSONArray apiResults = new JSONArray(line);
for (int e = 0; e < apiResults.length(); e++)
result.add(apiResults.getJSONObject(e).getString("entity_id"));
Collections.sort(result);
return result;
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
public HAEntity getEntity(String entityId) throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/states/" + entityId);
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setRequestMethod("GET");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
InputStream inputStream = httpConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = bufferedReader.readLine();
JSONObject json = new JSONObject(line);
return new HAEntity(
json.getString("state"),
json.getString("attributes"));
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
public String renderTemplate(String template) throws HAAPIException {
HttpURLConnection httpConn = null;
try {
URL url = new URL(mServer.getBaseURL() + "/api/template");
httpConn = (HttpURLConnection)url.openConnection();
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Authorization", "Bearer " + mServer.getAccessToken());
httpConn.setRequestProperty("Content-Type", "application/json");
httpConn.setDoOutput(true);
JSONObject jsonBody = new JSONObject();
jsonBody.put("template", template);
byte[] outputBytes = jsonBody.toString().getBytes("UTF-8");
OutputStream os = httpConn.getOutputStream();
os.write(outputBytes);
os.close();
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
int c;
while ((c = reader.read()) != -1)
sb.append((char) c);
return sb.toString();
} catch (IOException e) {
if (httpConn != null)
try {
throw new HAAPIException("Network Error: ".concat(httpConn.getResponseMessage()), e);
} catch (IOException e1) {
throw new HAAPIException("Network Error", e);
}
else
throw new HAAPIException("IO Error", e);
} catch (JSONException e) {
throw new HAAPIException("JSON Error", e);
}
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIException.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
import java.lang.Exception;
public class HAAPIException extends Exception {
private static final long serialVersionUID = 7395216398898021862L;
public HAAPIException() { super(); }
public HAAPIException(String message) { super(message); }
public HAAPIException(String message, Throwable cause) { super(message, cause); }
public HAAPIException(Throwable cause) { super(cause); }
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIResult.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
public class HAAPIResult<T> {
private final HAAPIException mException ;
private final T mResult;
public HAAPIResult(T result) {
mResult = result;
mException = null;
}
public HAAPIResult(HAAPIException exception) {
mException = exception;
mResult = null;
}
public HAAPIException getException() {
return mException;
}
public T getResult() {
return mResult;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPITask.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
import android.os.AsyncTask;
public abstract class HAAPITask<Params,Progress,Result> extends AsyncTask<Params,Progress,HAAPIResult<Result>> {
private final HAAPI mAPI;
protected HAAPITask(HAServer server) {
this.mAPI = new HAAPI(server);
}
@Override @SafeVarargs
protected final HAAPIResult<Result> doInBackground(Params... params) {
try {
return new HAAPIResult<>(doAPIInBackground(mAPI, params));
} catch (HAAPIException e) {
return new HAAPIResult<>(e);
}
}
protected abstract Result doAPIInBackground(HAAPI api, Params... params) throws HAAPIException;
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAEntity.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
public class HAEntity {
private String mState, mAttributes;
public HAEntity(String mState, String mAttributes) {
this.mState = mState;
this.mAttributes = mAttributes;
}
public String getState() {
return mState;
}
public String getAttributes() {
return mAttributes;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServer.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
import org.json.JSONException;
import org.json.JSONObject;
public class HAServer {
private String name, baseURL, accessToken;
public HAServer(String name, String baseURL, String accessToken) {
this.name = name;
this.baseURL = baseURL;
this.accessToken = accessToken;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBaseURL() {
return baseURL;
}
public void setBaseURL(String baseURL) {
this.baseURL = baseURL;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
@Override
public String toString() {
return getName();
}
private static String NAME = "Name";
private static String BASE_URL = "BaseURL";
private static String ACCESS_TOKEN = "AccessToken";
public static HAServer fromJSON(JSONObject json) throws JSONException {
return new HAServer(
json.getString(NAME), json.getString(BASE_URL), json.getString(ACCESS_TOKEN)
);
}
public JSONObject toJSON() {
JSONObject result = new JSONObject();
try {
result.put(NAME, name);
result.put(BASE_URL, baseURL);
result.put(ACCESS_TOKEN, accessToken);
} catch (JSONException e) {
e.printStackTrace();
}
return result;
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServerStore.java
================================================
package com.markadamson.taskerplugin.homeassistant.model;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
public class HAServerStore {
private final Context mContext;
public HAServerStore(final Context context) {
mContext = context;
}
private static String PREFS_KEY = "com.markadamson.taskerplugin.homeassistant.model.HAServerStore.PREFS_KEY";
public Map<UUID, HAServer> getServers() {
Map<UUID,HAServer> result = new HashMap<>();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
if (prefs.contains(PREFS_KEY)) {
try {
JSONObject jsonServers = new JSONObject(prefs.getString(PREFS_KEY, ""));
Iterator<String> keys = jsonServers.keys();
while (keys.hasNext()) {
String uuid = keys.next();
result.put(UUID.fromString(uuid), HAServer.fromJSON((JSONObject) jsonServers.get(uuid)));
}
} catch (JSONException e) {
e.printStackTrace();
prefs.edit().remove(PREFS_KEY).apply();
}
}
return result;
}
public UUID addServer(HAServer server) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
JSONObject jsonServers;
if (prefs.contains(PREFS_KEY)) {
try {
jsonServers = new JSONObject(prefs.getString(PREFS_KEY, ""));
} catch (JSONException e) {
jsonServers = new JSONObject();
}
} else
jsonServers = new JSONObject();
UUID result = UUID.randomUUID();
try {
jsonServers.put(result.toString(), server.toJSON());
} catch (JSONException e) {
e.printStackTrace();
}
prefs.edit().putString(PREFS_KEY, jsonServers.toString()).apply();
return result;
}
public void deleteServer(UUID serverID) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
if (prefs.contains(PREFS_KEY)) {
JSONObject jsonServers;
try {
jsonServers = new JSONObject(prefs.getString(PREFS_KEY, ""));
} catch (JSONException e) {
e.printStackTrace();
prefs.edit().remove(PREFS_KEY).apply();
return;
}
if (jsonServers.has(serverID.toString())) {
jsonServers.remove(serverID.toString());
prefs.edit().putString(PREFS_KEY, jsonServers.toString()).apply();
}
}
}
public void updateServer(UUID serverID, HAServer server) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
if (!prefs.contains(PREFS_KEY))
throw new RuntimeException("No such server in storage!");
JSONObject jsonServers;
try {
jsonServers = new JSONObject(prefs.getString(PREFS_KEY, ""));
} catch (JSONException e) {
e.printStackTrace();
prefs.edit().remove(PREFS_KEY).apply();
throw new RuntimeException("No such server in storage!", e);
}
if (!jsonServers.has(serverID.toString()))
throw new RuntimeException("No such server in storage!");
jsonServers.remove(serverID.toString());
try {
jsonServers.put(serverID.toString(), server.toJSON());
} catch (JSONException e) {
e.printStackTrace();
}
prefs.edit().putString(PREFS_KEY, jsonServers.toString()).apply();
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractAsyncReceiver.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-plugin-client-sdk-for-locale https://github.com/twofortyfouram/android-plugin-client-sdk-for-locale
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.receiver;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.twofortyfouram.spackle.AndroidSdkVersion;
import com.twofortyfouram.spackle.ThreadUtil;
import com.twofortyfouram.spackle.ThreadUtil.ThreadPriority;
import net.jcip.annotations.ThreadSafe;
import static com.twofortyfouram.assertion.Assertions.assertInRangeInclusive;
import static com.twofortyfouram.assertion.Assertions.assertNotNull;
/**
* Simplifies asynchronous broadcast handling. Subclasses call
* {@link #goAsyncWithCallback(AsyncCallback, boolean)}, and the abstract class takes
* care of executing the callback on a background thread.
*/
@ThreadSafe
/* package */ abstract class AbstractAsyncReceiver extends BroadcastReceiver {
/*
* This method is package visible rather than protected so that it will be
* obfuscated by ProGuard.
*
* @param callback Callback to execute on a background thread.
* @param isOrdered Indicates whether an ordered broadcast is being processed.
*/
@TargetApi(VERSION_CODES.HONEYCOMB)
/* package */ final void goAsyncWithCallback(@NonNull final AsyncCallback callback,
final boolean isOrdered) {
assertNotNull(callback, "callback"); //$NON-NLS-1$
final PendingResult pendingResult = goAsync();
if (null == pendingResult) {
throw new AssertionError(
"PendingResult was null. Was goAsync() called previously?"); //$NON-NLS-1$
}
final Handler.Callback handlerCallback = new AsyncHandlerCallback();
final HandlerThread thread = ThreadUtil.newHandlerThread(getClass().getName(),
ThreadPriority.BACKGROUND);
final Handler handler = new Handler(thread.getLooper(), handlerCallback);
final Object obj = new Pair<PendingResult, AsyncCallback>(pendingResult, callback);
final int isOrderedInt = isOrdered ? 1 : 0;
final Message msg = handler
.obtainMessage(AsyncHandlerCallback.MESSAGE_HANDLE_CALLBACK, isOrderedInt, 0, obj);
final boolean isMessageSent = handler.sendMessage(msg);
if (!isMessageSent) {
throw new AssertionError();
}
}
@TargetApi(VERSION_CODES.HONEYCOMB)
private static final class AsyncHandlerCallback implements Handler.Callback {
/**
* Message MUST contain a {@code Pair<PendingResult, AsyncCallback>} as the {@code msg.obj}
* and a boolean encoded in the {@code msg.arg1} to indicate whether the broadcast was
* ordered.
*/
public static final int MESSAGE_HANDLE_CALLBACK = 0;
@Override
public boolean handleMessage(final Message msg) {
assertNotNull(msg, "msg"); //$NON-NLS-1$
switch (msg.what) {
case MESSAGE_HANDLE_CALLBACK: {
assertNotNull(msg.obj, "msg.obj"); //$NON-NLS-1$
assertInRangeInclusive(msg.arg1, 0, 1, "msg.arg1"); //$NON-NLS-1$
final Pair<PendingResult, AsyncCallback> pair = castObj(msg.obj);
final boolean isOrdered = 0 != msg.arg1;
final PendingResult pendingResult = pair.first;
final AsyncCallback asyncCallback = pair.second;
try {
final int resultCode = asyncCallback.runAsync();
if (isOrdered) {
pendingResult.setResultCode(resultCode);
}
} finally {
pendingResult.finish();
}
quit();
break;
}
}
return true;
}
@NonNull
@SuppressWarnings("unchecked")
private static Pair<PendingResult, AsyncCallback> castObj(@NonNull final Object o) {
return (Pair<PendingResult, AsyncCallback>) o;
}
private static void quit() {
if (AndroidSdkVersion.isAtLeastSdk(VERSION_CODES.JELLY_BEAN_MR2)) {
quitJellybeanMr2();
} else {
Looper.myLooper().quit();
}
}
@TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
private static void quitJellybeanMr2() {
Looper.myLooper().quitSafely();
}
}
/* package */static interface AsyncCallback {
/**
* @return The result code to be set if this is an ordered broadcast.
*/
public int runAsync();
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractPluginSettingReceiver.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-plugin-client-sdk-for-locale https://github.com/twofortyfouram/android-plugin-client-sdk-for-locale
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.receiver;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.twofortyfouram.log.Lumberjack;
import com.twofortyfouram.spackle.AndroidSdkVersion;
import com.twofortyfouram.spackle.bundle.BundleScrubber;
/**
* <p>Abstract superclass for a plug-in setting BroadcastReceiver implementation.</p>
* <p>The plug-in receiver lifecycle is as follows:</p>
* <ol>
* <li>{@link #onReceive(android.content.Context, android.content.Intent)} is called by the Android
* frameworks.
* onReceive() will verify that the Intent is valid. If the Intent is invalid, the receiver
* returns
* immediately. If the Intent appears to be valid, then the lifecycle continues.</li>
* <li>{@link #isBundleValid(android.os.Bundle)} is called to determine whether {@link
* com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} is valid. If the Bundle is
* invalid, then the
* receiver returns immediately. If the bundle is valid, then the lifecycle continues.</li>
* <li>{@link #isAsync()} is called to determine whether the remaining work should be performed on
* a
* background thread.</li>
* <li>{@link #firePluginSetting(android.content.Context, android.os.Bundle)} is called to trigger
* the plug-in setting's action.</li>
* </ol>
* <p>
* Implementations of this BroadcastReceiver must be registered in the Android
* Manifest with an Intent filter for
* {@link com.twofortyfouram.locale.api.Intent#ACTION_FIRE_SETTING ACTION_FIRE_SETTING}. The
* BroadcastReceiver must be exported, enabled, and cannot have permissions
* enforced on it.
* </p>
*/
public abstract class AbstractPluginSettingReceiver extends AbstractAsyncReceiver {
/*
* The multiple return statements in this method are a little gross, but the
* alternative of nested if statements is even worse :/
*/
@Override
public final void onReceive(final Context context, final Intent intent) {
if (BundleScrubber.scrub(intent)) {
return;
}
Lumberjack.v("Received %s", intent); //$NON-NLS-1$
/*
* Note: It is OK if a host sends an ordered broadcast for plug-in
* settings. Such a behavior would allow the host to optionally block until the
* plug-in setting finishes.
*/
if (!com.twofortyfouram.locale.api.Intent.ACTION_FIRE_SETTING.equals(intent.getAction())) {
Lumberjack
.e("Intent action is not %s",
com.twofortyfouram.locale.api.Intent.ACTION_FIRE_SETTING); //$NON-NLS-1$
return;
}
/*
* Ignore implicit intents, because they are not valid. It would be
* meaningless if ALL plug-in setting BroadcastReceivers installed were
* asked to handle queries not intended for them. Ideally this
* implementation here would also explicitly assert the class name as
* well, but then the unit tests would have trouble. In the end,
* asserting the package is probably good enough.
*/
if (!context.getPackageName().equals(intent.getPackage())
&& !new ComponentName(context, this.getClass().getName()).equals(intent
.getComponent())) {
Lumberjack.e("Intent is not explicit"); //$NON-NLS-1$
return;
}
final Bundle bundle = intent
.getBundleExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
if (BundleScrubber.scrub(intent)) {
return;
}
if (null == bundle) {
Lumberjack.e("%s is missing",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE); //$NON-NLS-1$
return;
}
if (!isBundleValid(bundle)) {
Lumberjack.e("%s is invalid",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE); //$NON-NLS-1$
return;
}
if (isAsync() && AndroidSdkVersion.isAtLeastSdk(Build.VERSION_CODES.HONEYCOMB)) {
final AsyncCallback callback = new AsyncCallback() {
@NonNull
private final Context mContext = context;
@NonNull
private final Bundle mBundle = bundle;
@NonNull
private final Intent mIntent = intent;
@Override
public int runAsync() {
firePluginSetting(mContext, mIntent, mBundle);
return Activity.RESULT_OK;
}
};
goAsyncWithCallback(callback, isOrderedBroadcast());
} else {
firePluginSetting(context, intent, bundle);
}
}
/**
* <p>Gives the plug-in receiver an opportunity to validate the Bundle, to
* ensure that a malicious application isn't attempting to pass
* an invalid Bundle.</p>
* <p>
* This method will be called on the BroadcastReceiver's Looper (normatively the main thread)
* </p>
*
* @param bundle The plug-in's Bundle previously returned by the edit
* Activity. {@code bundle} should not be mutated by this method.
* @return true if {@code bundle} appears to be valid. false if {@code bundle} appears to be
* invalid.
*/
protected abstract boolean isBundleValid(@NonNull final Bundle bundle);
/**
* Configures the receiver whether it should process the Intent in a
* background thread. Plug-ins should return true if their
* {@link #firePluginSetting(android.content.Context, android.os.Bundle)} method performs any
* sort of disk IO (ContentProvider query, reading SharedPreferences, etc.).
* or other work that may be slow.
* <p>
* Asynchronous BroadcastReceivers are not supported prior to Honeycomb, so
* with older platforms broadcasts will always be processed on the BroadcastReceiver's Looper
* (which for Manifest registered receivers will be the main thread).
*
* @return True if the receiver should process the Intent in a background
* thread. False if the plug-in should process the Intent on the
* BroadcastReceiver's Looper (normatively the main thread).
*/
protected abstract boolean isAsync();
/**
* If {@link #isAsync()} returns true, this method will be called on a
* background thread. If {@link #isAsync()} returns false, this method will
* be called on the main thread. Regardless of which thread this method is
* called on, this method MUST return within 10 seconds per the requirements
* for BroadcastReceivers.
*
* @param context BroadcastReceiver context.
* @param bundle The plug-in's Bundle previously returned by the edit
* Activity.
*/
protected abstract void firePluginSetting(@NonNull final Context context,
@NonNull final Intent intent,
@NonNull final Bundle bundle);
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiver.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.receiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.markadamson.taskerplugin.homeassistant.Constants;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.markadamson.taskerplugin.homeassistant.bundle.GetStatePluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.bundle.PluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.bundle.RenderTemplatePluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.service.ActionService;
import com.twofortyfouram.log.Lumberjack;
public final class FireReceiver extends AbstractPluginSettingReceiver {
@Override
protected boolean isBundleValid(@NonNull final Bundle bundle) {
if (bundle.getInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE) < 3 || bundle.getInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE) == Constants.BUNDLE_CALL_SERVICE)
return PluginBundleValues.isBundleValid(bundle);
else if (bundle.getInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE) == Constants.BUNDLE_GET_STATE)
return GetStatePluginBundleValues.isBundleValid(bundle);
else
return RenderTemplatePluginBundleValues.isBundleValid(bundle);
}
@Override
protected boolean isAsync() {
return false;
}
@Override
protected void firePluginSetting(@NonNull final Context context, @NonNull final Intent intent, @NonNull final Bundle bundle) {
Lumberjack.d("FireReceiver.firePluginSetting");
intent.putExtra(ActionService.EXT_BUNDLE, bundle);
ActionService.enqueueWork(context, intent);
Lumberjack.d("Set result code \" Pending\"");
if (isOrderedBroadcast())
setResultCode(TaskerPlugin.Setting.RESULT_CODE_PENDING);
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/service/ActionService.java
================================================
package com.markadamson.taskerplugin.homeassistant.service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
import com.markadamson.taskerplugin.homeassistant.Constants;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.markadamson.taskerplugin.homeassistant.bundle.GetStatePluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.bundle.PluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.bundle.RenderTemplatePluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.model.HAAPI;
import com.markadamson.taskerplugin.homeassistant.model.HAAPIException;
import com.markadamson.taskerplugin.homeassistant.model.HAEntity;
import com.markadamson.taskerplugin.homeassistant.model.HAServerStore;
import com.twofortyfouram.log.Lumberjack;
import java.util.UUID;
/**
* Created by marka on 14/06/2019.
*/
public class ActionService extends JobIntentService {
public static final String EXT_BUNDLE = "com.markadamson.taskerplugin.homeassistant.service.ActionService.EXT_BUNDLE";
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1000;
/**
* Convenience method for enqueuing work in to this service.
*/
public static void enqueueWork(Context context, Intent work) {
Lumberjack.d("ActionService.enqueueWork");
enqueueWork(context, ActionService.class, JOB_ID, work);
}
private void signalUnknownServer(Intent intent) {
Bundle vars = new Bundle();
vars.putString(TaskerPlugin.Setting.VARNAME_ERROR_MESSAGE, "Unknown Server - Has it been deleted?");
TaskerPlugin.Setting.signalFinish(this, intent, TaskerPlugin.Setting.RESULT_CODE_FAILED, vars);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
Lumberjack.d("ActionService.onHandleWork");
Bundle bundle = intent.getBundleExtra(EXT_BUNDLE);
HAServerStore servers = new HAServerStore(this);
try {
if (bundle.getInt(PluginBundleValues.BUNDLE_EXTRA_INT_VERSION_CODE) < 3 || bundle.getInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE) == Constants.BUNDLE_CALL_SERVICE) {
Lumberjack.d("Call Service", bundle);
UUID serverId = PluginBundleValues.getServer(bundle);
if (!servers.getServers().containsKey(serverId)) {
signalUnknownServer(intent);
return;
}
String[] service = PluginBundleValues.getService(bundle).split("\\.");
Lumberjack.d("Calling api...");
new HAAPI(servers.getServers().get(serverId))
.callService(service[0], service[1], PluginBundleValues.getData(bundle));
Lumberjack.d("Signalling finish...");
TaskerPlugin.Setting.signalFinish(this, intent, TaskerPlugin.Setting.RESULT_CODE_OK, null);
} else if (bundle.getInt(Constants.BUNDLE_EXTRA_BUNDLE_TYPE) == Constants.BUNDLE_GET_STATE) {
Lumberjack.d("Get State", bundle);
UUID serverId = GetStatePluginBundleValues.getServer(bundle);
if (!servers.getServers().containsKey(serverId)) {
signalUnknownServer(intent);
return;
}
String entityId = GetStatePluginBundleValues.getEntity(bundle);
Lumberjack.d("Calling api...");
HAEntity entity = new HAAPI(servers.getServers().get(serverId))
.getEntity(entityId);
Bundle vars = new Bundle();
vars.putString(GetStatePluginBundleValues.getStateVariable(bundle), entity.getState());
String attrsVar = GetStatePluginBundleValues.getAttrsVariable(bundle);
if (!attrsVar.isEmpty())
vars.putString(attrsVar, entity.getAttributes());
Lumberjack.d("Signalling finish...");
TaskerPlugin.Setting.signalFinish(this, intent, TaskerPlugin.Setting.RESULT_CODE_OK, vars);
} else {
Lumberjack.d("Render Template", bundle);
UUID serverId = RenderTemplatePluginBundleValues.getServer(bundle);
if (!servers.getServers().containsKey(serverId)) {
signalUnknownServer(intent);
return;
}
String template = RenderTemplatePluginBundleValues.getTemplate(bundle);
Lumberjack.d("Calling api...");
String result = new HAAPI(servers.getServers().get(serverId))
.renderTemplate(template);
Bundle vars = new Bundle();
vars.putString(RenderTemplatePluginBundleValues.getVariable(bundle), result);
Lumberjack.d("Signalling finish...");
TaskerPlugin.Setting.signalFinish(this, intent, TaskerPlugin.Setting.RESULT_CODE_OK, vars);
}
} catch (HAAPIException e) {
Lumberjack.e(e.getMessage());
e.printStackTrace();
Bundle vars = new Bundle();
vars.putString(TaskerPlugin.Setting.VARNAME_ERROR_MESSAGE, e.getMessage());
TaskerPlugin.Setting.signalFinish(this, intent, TaskerPlugin.Setting.RESULT_CODE_FAILED, vars);
}
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/ServerSelectionUI.java
================================================
package com.markadamson.taskerplugin.homeassistant.ui;
import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.markadamson.taskerplugin.homeassistant.R;
import com.markadamson.taskerplugin.homeassistant.model.HAServer;
import com.markadamson.taskerplugin.homeassistant.model.HAServerStore;
import com.markadamson.taskerplugin.homeassistant.ui.activity.EditServerActivity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class ServerSelectionUI {
private final CharSequence mHostName;
private final HAServerStore mServerStore;
private final Spinner spnServers;
private List<UUID> mIds;
private List<HAServer> mServers;
private ArrayAdapter<HAServer> mServerAdapter;
public ServerSelectionUI(final Activity activity, final CharSequence hostName, final OnServerSelectedListener serverListener) {
mHostName = hostName;
mServerStore = new HAServerStore(activity);
Map<UUID,HAServer> serverMap = mServerStore.getServers();
mIds = new ArrayList<>(serverMap.keySet());
mServers = new ArrayList<>();
for(UUID id : mIds)
mServers.add(serverMap.get(id));
mServerAdapter = new ArrayAdapter<>(activity, android.R.layout.simple_spinner_dropdown_item, mServers);
spnServers = activity.findViewById(R.id.spn_server);
spnServers.setAdapter(mServerAdapter);
spnServers.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
if (serverListener != null)
serverListener.onServerSelected(mServers.get(position));
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
if (serverListener != null)
serverListener.onNothingSelected();
}
});
activity.findViewById(R.id.btn_add_server).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
activity.startActivityForResult(
new Intent(activity, EditServerActivity.class)
.putExtra(EditServerActivity.EXT_HOST_NAME, mHostName)
.putExtra(EditServerActivity.EXT_REQUEST_CODE, EditServerActivity.REQ_NEW_SERVER),
EditServerActivity.REQ_NEW_SERVER);
}
}
);
activity.findViewById(R.id.btn_edit_server).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mServerAdapter.getCount() > 0) {
HAServer server = mServerAdapter.getItem(spnServers.getSelectedItemPosition());
activity.startActivityForResult(
new Intent(activity, EditServerActivity.class)
.putExtra(EditServerActivity.EXT_HOST_NAME, mHostName)
.putExtra(EditServerActivity.EXT_REQUEST_CODE, EditServerActivity.REQ_EDIT_SERVER)
.putExtra(EditServerActivity.EXT_SERVER_NAME, server.getName())
.putExtra(EditServerActivity.EXT_BASE_URL, server.getBaseURL())
.putExtra(EditServerActivity.EXT_ACCESS_TOKEN, server.getAccessToken()),
EditServerActivity.REQ_EDIT_SERVER
);
}
}
}
);
activity.findViewById(R.id.btn_delete_server).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mServers.size() > 0)
new AlertDialog.Builder(activity)
.setTitle(R.string.are_you_sure)
.setMessage(R.string.delete_server_warning)
.setPositiveButton(R.string.yes_delete_the_server, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int idx = spnServers.getSelectedItemPosition();
mServerStore.deleteServer(mIds.get(idx));
mIds.remove(idx);
mServers.remove(idx);
mServerAdapter.notifyDataSetChanged();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
}
);
}
public int serverCount() {
return mServers.size();
}
public HAServer currentServer() {
return mServers.get(spnServers.getSelectedItemPosition());
}
public UUID currentId() {
return mIds.get(spnServers.getSelectedItemPosition());
}
public void setSelection(UUID id) {
spnServers.setSelection(mIds.indexOf(id));
}
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (!Arrays.asList(new Integer[]{EditServerActivity.REQ_NEW_SERVER,EditServerActivity.REQ_EDIT_SERVER}).contains(requestCode))
return false;
if (resultCode == Activity.RESULT_OK) {
HAServer server = new HAServer(
data.getStringExtra(EditServerActivity.EXT_SERVER_NAME),
data.getStringExtra(EditServerActivity.EXT_BASE_URL),
data.getStringExtra(EditServerActivity.EXT_ACCESS_TOKEN)
);
switch (requestCode) {
case EditServerActivity.REQ_NEW_SERVER:
mIds.add(mServerStore.addServer(server));
mServerAdapter.add(server);
break;
case EditServerActivity.REQ_EDIT_SERVER:
int idx = spnServers.getSelectedItemPosition();
mServerStore.updateServer(mIds.get(idx), server);
mServers.set(idx, server);
mServerAdapter.notifyDataSetChanged();
break;
}
}
return true;
}
public interface OnServerSelectedListener {
void onServerSelected(HAServer server);
void onNothingSelected();
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivity.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.ui.activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.Toast;
import com.markadamson.locale.sdk.client.ui.activity.AbstractAppCompatPluginActivity;
import com.markadamson.taskerplugin.homeassistant.R;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.markadamson.taskerplugin.homeassistant.Utils;
import com.markadamson.taskerplugin.homeassistant.bundle.PluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.model.HAAPI;
import com.markadamson.taskerplugin.homeassistant.model.HAAPIException;
import com.markadamson.taskerplugin.homeassistant.model.HAAPIResult;
import com.markadamson.taskerplugin.homeassistant.model.HAAPITask;
import com.markadamson.taskerplugin.homeassistant.model.HAServer;
import com.markadamson.taskerplugin.homeassistant.ui.ServerSelectionUI;
import com.twofortyfouram.log.Lumberjack;
import net.jcip.annotations.NotThreadSafe;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@NotThreadSafe
public final class EditActivity extends AbstractAppCompatPluginActivity {
private ServerSelectionUI mServerUI;
private ArrayAdapter<String> mServiceAdapter;
private AutoCompleteTextView atvService;
private EditText etServiceData;
private abstract static class MyAPITask<Params,Progress,Result> extends HAAPITask<Params,Progress,Result> {
WeakReference<EditActivity> activityReference;
MyAPITask(EditActivity activity, HAServer server) {
super(server);
activityReference = new WeakReference<>(activity);
}
}
private static class GetServicesTask extends MyAPITask<Void,Void,List<String>> {
GetServicesTask(EditActivity activity, HAServer server) {
super(activity, server);
activity.mServiceAdapter.clear();
}
@Override
protected List<String> doAPIInBackground(HAAPI api, Void... voids) throws HAAPIException {
return api.getServices();
}
@Override
protected void onPostExecute(HAAPIResult<List<String>> services) {
EditActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
if (services.getException() != null) {
services.getException().printStackTrace();
Toast.makeText(activity, services.getException().getMessage(), Toast.LENGTH_SHORT).show();
return;
}
activity.mServiceAdapter.clear();
activity.mServiceAdapter.addAll(services.getResult());
}
}
private static class TestServiceTask extends MyAPITask<String,Void,Void> {
TestServiceTask(EditActivity context, HAServer server) {
super(context, server);
}
@Override
protected Void doAPIInBackground(HAAPI api, String... strings) throws HAAPIException {
api.callService(strings[0], strings[1], strings[2]);
return null;
}
@Override
protected void onPostExecute(HAAPIResult<Void> result) {
EditActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
if (result.getException() != null) {
result.getException().printStackTrace();
Toast.makeText(activity, result.getException().getMessage(), Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(activity, "Success!", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/*
* To help the user keep context, the title shows the host's name and the subtitle
* shows the plug-in's name.
*/
CharSequence callingApplicationLabel = null;
try {
callingApplicationLabel =
getPackageManager().getApplicationLabel(
getPackageManager().getApplicationInfo(getCallingPackage(),
0));
} catch (final PackageManager.NameNotFoundException e) {
Lumberjack.e("Calling package couldn't be found%s", e); //$NON-NLS-1$
}
if (null != callingApplicationLabel) {
setTitle(callingApplicationLabel);
}
Resources r = getResources();
getSupportActionBar().setSubtitle(r.getString(R.string.activity_subtitle, r.getString(R.string.call_service)));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mServerUI = new ServerSelectionUI(this, callingApplicationLabel, new ServerSelectionUI.OnServerSelectedListener() {
@Override
public void onServerSelected(HAServer server) {
new GetServicesTask(EditActivity.this, server).execute();
}
@Override
public void onNothingSelected() {
mServiceAdapter.clear();
}
});
String[] variablesFromHost = TaskerPlugin.getRelevantVariableList(getIntent().getExtras());
mServiceAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, new ArrayList<String>());
atvService = findViewById(R.id.atv_service);
atvService.setAdapter(mServiceAdapter);
Utils.initVariableSelectUI(variablesFromHost, findViewById(R.id.btn_service_variable), atvService);
etServiceData = findViewById(R.id.et_service_data);
Utils.initVariableSelectUI(variablesFromHost, findViewById(R.id.btn_service_data_variable), etServiceData);
findViewById(R.id.btn_test_service).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (atvService.getText().toString().contains("%")) {
Toast.makeText(EditActivity.this, "Cannot test using variables!", Toast.LENGTH_SHORT).show();
return;
}
String[] service = atvService.getText().toString().split("\\.");
if (service.length != 2) {
Toast.makeText(EditActivity.this, "Invalid service!", Toast.LENGTH_SHORT).show();
return;
}
new TestServiceTask(EditActivity.this, mServerUI.currentServer())
.execute(service[0], service[1], etServiceData.getText().toString());
}
}
);
Utils.checkBatteryOptimisation(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mServerUI.onActivityResult(requestCode, resultCode, data);
}
private void restoreState(UUID id, String service, String data) {
mServerUI.setSelection(id);
atvService.setText(service);
etServiceData.setText(data);
}
@Override
public void onPostCreateWithPreviousResult(@NonNull final Bundle previousBundle,
@NonNull final String previousBlurb) {
restoreState(
PluginBundleValues.getServer(previousBundle),
PluginBundleValues.getService(previousBundle),
PluginBundleValues.getData(previousBundle)
);
}
@Override
public boolean isBundleValid(@NonNull final Bundle bundle) {
return PluginBundleValues.isBundleValid(bundle);
}
@Override
public Bundle getResultBundle() {
return PluginBundleValues.generateBundle(getApplicationContext(),
mServerUI.currentId(),
atvService.getText().toString(),
etServiceData.getText().toString());
}
@NonNull
@Override
public String getResultBlurb(@NonNull final Bundle bundle) {
final String message = PluginBundleValues.getService(bundle);
final int maxBlurbLength = getResources().getInteger(
R.integer.com_twofortyfouram_locale_sdk_client_maximum_blurb_length);
if (message.length() > maxBlurbLength) {
return message.substring(0, maxBlurbLength - 1).concat("…");
}
return message;
}
@NonNull
@Override
public String[] getRelevantVariableList() {
return new String[0];
}
@Override
public int requestedTimeoutMS() {
return 10000;
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
private boolean canSave() {
if (mServerUI.serverCount() == 0) {
Toast.makeText(this, "Please select a Server", Toast.LENGTH_SHORT).show();
return false;
}
if (atvService.getText().toString().isEmpty()) {
Toast.makeText(this, "Please select a Service", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
@Override
public void onBackPressed() {
if (canSave())
super.onBackPressed();
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
if (android.R.id.home == item.getItemId()) {
if (canSave())
finish();
}
else if (R.id.menu_cancel == item.getItemId()) {
mIsCancelled = true;
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
================================================
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditGetStateActivity.java
================================================
/*
* home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>
* Copyright 2019 Mark Adamson
*
* Original author:
* android-toast-setting-plugin-for-locale <https://github.com/twofortyfouram/android-toast-setting-plugin-for-locale>
* Copyright 2014 two forty four a.m. LLC
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.markadamson.taskerplugin.homeassistant.ui.activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.Toast;
import com.markadamson.locale.sdk.client.ui.activity.AbstractAppCompatPluginActivity;
import com.markadamson.taskerplugin.homeassistant.R;
import com.markadamson.taskerplugin.homeassistant.TaskerPlugin;
import com.markadamson.taskerplugin.homeassistant.Utils;
import com.markadamson.taskerplugin.homeassistant.bundle.GetStatePluginBundleValues;
import com.markadamson.taskerplugin.homeassistant.model.HAAPI;
import com.markadamson.taskerplugin.homeassistant.model.HAAPIException;
import com.markadamson.taskerplugin.homeassistant.model.HAAPIResult;
import com.markadamson.taskerplugin.homeassistant.model.HAAPITask;
import com.markadamson.taskerplugin.homeassistant.model.HAEntity;
import com.markadamson.taskerplugin.homeassistant.model.HAServer;
import com.markadamson.taskerplugin.homeassistant.ui.ServerSelectionUI;
import com.twofortyfouram.log.Lumberjack;
import net.jcip.annotations.NotThreadSafe;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@NotThreadSafe
public final class EditGetStateActivity extends AbstractAppCompatPluginActivity {
private ServerSelectionUI mServerUI;
private ArrayAdapter<String> mEntityAdapter;
private AutoCompleteTextView atvEntity;
private EditText etStateVariable, etAttrsVariable;
private abstract static class MyAPITask<Params,Progress,Result> extends HAAPITask<Params,Progress,Result> {
WeakReference<EditGetStateActivity> activityReference;
MyAPITask(EditGetStateActivity context, HAServer server) {
super(server);
activityReference = new WeakReference<>(context);
}
}
private static class GetEntitiesTask extends MyAPITask<Void,Void,List<String>> {
GetEntitiesTask(EditGetStateActivity activity, HAServer server) {
super(activity, server);
activity.mEntityAdapter.clear();
}
@Override
protected List<String> doAPIInBackground(HAAPI api, Void... voids) throws HAAPIException {
return api.getEntities();
}
@Override
protected void onPostExecute(HAAPIResult<List<String>> entities) {
EditGetStateActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
if (entities.getException() != null) {
entities.getException().printStackTrace();
Toast.makeText(activity, entities.getException().getMessage(), Toast.LENGTH_SHORT).show();
return;
}
activity.mEntityAdapter.clear();
activity.mEntityAdapter.addAll(entities.getResult());
}
}
private static class TestEntityTask extends MyAPITask<String,Void,HAEntity> {
TestEntityTask(EditGetStateActivity context, HAServer server) {
super(context, server);
}
@Override
protected HAEntity doAPIInBackground(HAAPI api, String... strings) throws HAAPIException {
return api.getEntity(strings[0]);
}
@Override
protected void onPostExecute(HAAPIResult<HAEntity> state) {
EditGetStateActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
if (state.getException() != null) {
state.getException().printStackTrace();
Toast.makeText(activity, state.getException().getMessage(), Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(activity, state.getResult().getState(), Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_get_state);
/*
* To help the user keep context, the title shows the host's name and the subtitle
* shows the plug-in's name.
*/
CharSequence callingApplicationLabel = null;
try {
callingApplicationLabel =
getPackageManager().getApplicationLabel(
getPackageManager().getApplicationInfo(getCallingPackage(),
0));
} catch (final PackageManager.NameNotFoundException e) {
Lumberjack.e("Calling package couldn't be found%s", e); //$NON-NLS-1$
}
if (null != callingApplicationLabel) {
setTitle(callingApplicationLabel);
}
Resources r = getResources();
getSupportActionBar().setSubtitle(r.getString(R.string.activity_subtitle, r.getString(R.string.get_state)));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mServerUI = new ServerSelectionUI(this, callingApplicationLabel, new ServerSelectionUI.OnServerSelectedListener() {
@Override
public void onServerSelected(HAServer server) {
new GetEntitiesTask(EditGetStateActivity.this, server).execute();
}
@Override
public void onNothingSelected() {
mEntityAdapter.clear();
}
});
String[] variablesFromHost = TaskerPlugin.getRelevantVariableList(getIntent().getExtras());
mEntityAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, new ArrayList<String>());
atvEntity = findViewById(R.id.atv_entity);
atvEntity.setAdapter(mEntityAdapter);
Utils.initVariableSelectUI(variablesFromHost, findViewById(R.id.btn_entity_variable), atvEntity);
etStateVariable = findViewById(R.id.et_state_variable);
Utils.initVariableSelectUI(variablesFromHost, findViewById(R.id.btn_state_variable), etStateVariable);
etAttrsVariable = findViewById(R.id.et_attrs_variable);
Utils.initVariableSelectUI(variablesFromHost, findViewById(R.id.btn_attrs_variable), etAttrsVariable);
findViewById(R.id.btn_test_entity).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View view) {
if (atvEntity.getText().toString().contains("%")) {
Toast.makeText(EditGetStateActivity.this, "Cannot test using variables in entity id!", Toast.LENGTH_SHORT).show();
return;
}
new TestEntityTask(EditGetStateActivity.this, mServerUI.currentServer())
.execute(atvEntity.getText().toString());
}
}
);
Utils.checkBatteryOptimisation(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onAc
gitextract_wh_8w5zl/
├── .gitignore
├── HomeAssistantTaskerPlugin/
│ ├── build.gradle
│ ├── lint.xml
│ ├── proguard-project.txt
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── markadamson/
│ │ └── taskerplugin/
│ │ └── homeassistant/
│ │ ├── bundle/
│ │ │ └── PluginBundleValuesTest.java
│ │ ├── receiver/
│ │ │ └── FireReceiverTest.java
│ │ ├── setting/
│ │ │ └── toast/
│ │ │ └── test/
│ │ │ ├── InstallLocation.java
│ │ │ └── ManifestTest.java
│ │ └── ui/
│ │ └── activity/
│ │ └── EditActivityTest.java
│ ├── debug/
│ │ └── AndroidManifest.xml
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── markadamson/
│ │ ├── locale/
│ │ │ └── sdk/
│ │ │ └── client/
│ │ │ ├── internal/
│ │ │ │ └── PluginActivityDelegate.java
│ │ │ └── ui/
│ │ │ └── activity/
│ │ │ ├── AbstractAppCompatPluginActivity.java
│ │ │ └── IPluginActivity.java
│ │ └── taskerplugin/
│ │ └── homeassistant/
│ │ ├── Constants.java
│ │ ├── PluginApplication.java
│ │ ├── TaskerPlugin.java
│ │ ├── Utils.java
│ │ ├── bundle/
│ │ │ ├── GetStatePluginBundleValues.java
│ │ │ ├── PluginBundleValues.java
│ │ │ └── RenderTemplatePluginBundleValues.java
│ │ ├── model/
│ │ │ ├── HAAPI.java
│ │ │ ├── HAAPIException.java
│ │ │ ├── HAAPIResult.java
│ │ │ ├── HAAPITask.java
│ │ │ ├── HAEntity.java
│ │ │ ├── HAServer.java
│ │ │ └── HAServerStore.java
│ │ ├── receiver/
│ │ │ ├── AbstractAsyncReceiver.java
│ │ │ ├── AbstractPluginSettingReceiver.java
│ │ │ └── FireReceiver.java
│ │ ├── service/
│ │ │ └── ActionService.java
│ │ └── ui/
│ │ ├── ServerSelectionUI.java
│ │ └── activity/
│ │ ├── EditActivity.java
│ │ ├── EditGetStateActivity.java
│ │ ├── EditRenderTemplateActivity.java
│ │ └── EditServerActivity.java
│ └── res/
│ ├── drawable/
│ │ ├── baseline_add_24.xml
│ │ ├── baseline_cancel_24.xml
│ │ ├── baseline_delete_24.xml
│ │ ├── baseline_edit_24.xml
│ │ └── outline_label_24.xml
│ ├── layout/
│ │ ├── edit_get_state.xml
│ │ ├── edit_render_template.xml
│ │ ├── edit_server.xml
│ │ ├── main.xml
│ │ └── select_server.xml
│ ├── menu/
│ │ └── menu.xml
│ └── values/
│ └── strings.xml
├── LICENSE
├── README.md
├── build.gradle
├── circle.yml
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── tools/
├── ci/
│ ├── android-sdk-license
│ ├── android-sdk-setup.sh
│ ├── firebase-test-lab-test-module.sh
│ ├── google-cloud-test-lab-setup.sh
│ └── repositories.cfg
├── findbugs/
│ └── android-filter.xml
└── gcloud.yml
SYMBOL INDEX (293 symbols across 31 files)
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValuesTest.java
class PluginBundleValuesTest (line 31) | public final class PluginBundleValuesTest extends AndroidTestCase {
method testExtraConstants (line 33) | @SmallTest
method testGenerateBundle (line 51) | @SmallTest
method testVerifyBundle_correct (line 64) | @SmallTest
method testVerifyBundle_null (line 73) | @SmallTest
method testVerifyBundle_missing_extra (line 78) | @SmallTest
method testVerifyBundle_extra_items (line 83) | @SmallTest
method testVerifyBundle_null_message (line 93) | @SmallTest
method testVerifyBundle_empty_message (line 101) | @SmallTest
method testVerifyBundle_wrong_type (line 109) | @SmallTest
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiverTest.java
class FireReceiverTest (line 33) | public final class FireReceiverTest extends AndroidTestCase {
method testNullMessage (line 43) | @SmallTest
method testNormal (line 59) | @SmallTest
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/InstallLocation.java
type InstallLocation (line 43) | @ThreadSafe
method getInstallLocation (line 113) | @NonNull
method getManifestInstallLocation (line 142) | @NonNull
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/ManifestTest.java
class ManifestTest (line 35) | public final class ManifestTest extends AndroidTestCase {
method testApplicationEnabled (line 37) | @SmallTest
method testPluginActivityPresent (line 42) | @SmallTest
method testPluginReceiver (line 68) | @SmallTest
method testManifestInstallLocation (line 88) | @SmallTest
method getPluginActivities (line 105) | private static List<ResolveInfo> getPluginActivities(@NonNull final Co...
method getPluginReceivers (line 128) | private static List<ResolveInfo> getPluginReceivers(@NonNull final Con...
FILE: HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivityTest.java
class EditActivityTest (line 41) | public final class EditActivityTest extends ActivityInstrumentationTestC...
method EditActivityTest (line 56) | public EditActivityTest() {
method setUp (line 63) | @Override
method testNewSettingCancel (line 106) | @MediumTest
method testNewSettingSave (line 119) | @MediumTest
method testOldSetting (line 134) | @MediumTest
method testBadBundle (line 160) | @MediumTest
method assertActivityResultAutoSync (line 178) | private void assertActivityResultAutoSync(final String message) throws...
method assertMessageAutoSync (line 221) | private void assertMessageAutoSync(final String message) throws Throwa...
method assertHintAutoSync (line 239) | private void assertHintAutoSync(final String hint) throws Throwable {
method setMessageAutoSync (line 257) | private void setMessageAutoSync(final String message) throws Throwable {
method autoSyncRunnable (line 277) | protected final void autoSyncRunnable(final Runnable runnable) {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/internal/PluginActivityDelegate.java
class PluginActivityDelegate (line 31) | @Immutable
method isLocalePluginIntent (line 37) | public static boolean isLocalePluginIntent(@NonNull final Intent inten...
method onCreate (line 46) | public void onCreate(@NonNull final T activity, @Nullable final Bundle...
method onPostCreate (line 67) | public void onPostCreate(@NonNull final T activity, @Nullable final Bu...
method finish (line 81) | public void finish(@NonNull final T activity, final boolean isCancelle...
method getPreviousBlurb (line 112) | @Nullable
method getPreviousBundle (line 120) | @Nullable
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/AbstractAppCompatPluginActivity.java
class AbstractAppCompatPluginActivity (line 70) | public abstract class AbstractAppCompatPluginActivity extends AppCompatA...
method onCreate (line 86) | @Override
method onPostCreate (line 93) | @Override
method finish (line 100) | @Override
method getPreviousBundle (line 117) | @Nullable
method getPreviousBlurb (line 128) | @Nullable
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/IPluginActivity.java
type IPluginActivity (line 10) | public interface IPluginActivity {
method getPreviousBundle (line 17) | @Nullable
method getPreviousBlurb (line 26) | @Nullable
method isBundleValid (line 37) | boolean isBundleValid(@NonNull final Bundle bundle);
method onPostCreateWithPreviousResult (line 57) | void onPostCreateWithPreviousResult(
method getResultBundle (line 64) | @Nullable
method getResultBlurb (line 71) | @NonNull
method getRelevantVariableList (line 77) | @NonNull
method requestedTimeoutMS (line 80) | int requestedTimeoutMS();
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Constants.java
class Constants (line 5) | public class Constants {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/PluginApplication.java
class PluginApplication (line 33) | public final class PluginApplication extends Application {
method onCreate (line 34) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/TaskerPlugin.java
class TaskerPlugin (line 54) | public class TaskerPlugin {
type Encoding (line 142) | public enum Encoding { JSON }
method hostSupportsKeyEncoding (line 146) | public static boolean hostSupportsKeyEncoding( Bundle extrasFromHost, ...
method hostSupportsRelevantVariables (line 176) | public static boolean hostSupportsRelevantVariables( Bundle extrasFrom...
method addRelevantVariableList (line 188) | public static void addRelevantVariableList( Intent intentToHost, Strin...
method variableNameValid (line 199) | public static boolean variableNameValid( String varName ) {
method getRelevantVariableList (line 231) | public static String [] getRelevantVariableList( Bundle fromHostIntent...
method addVariableBundle (line 257) | public static void addVariableBundle( Bundle resultExtras, Bundle vari...
method setKeyEncoding (line 275) | public static void setKeyEncoding( Bundle resultBundleToHost, String [...
class Setting (line 286) | public static class Setting {
method hostSupportsOnFireVariableReplacement (line 380) | public static boolean hostSupportsOnFireVariableReplacement( Bundle ...
method hostSupportsOnFireVariableReplacement (line 396) | public static boolean hostSupportsOnFireVariableReplacement( Activit...
method hostSupportsSynchronousExecution (line 420) | public static boolean hostSupportsSynchronousExecution( Bundle extra...
method requestTimeoutMS (line 438) | public static void requestTimeoutMS( Intent intentToHost, int timeou...
method setVariableReplaceKeys (line 463) | public static void setVariableReplaceKeys( Bundle resultBundleToHost...
method hasVariableReplaceKeys (line 470) | public static boolean hasVariableReplaceKeys(Bundle resultBundleToHo...
method hostSupportsVariableReturn (line 482) | public static boolean hostSupportsVariableReturn( Bundle extrasFromH...
method signalFinish (line 498) | public static boolean signalFinish( Context context, Intent original...
method getHintTimeoutMS (line 561) | public static int getHintTimeoutMS( Bundle extrasFromHost ) {
class Condition (line 581) | public static class Condition {
method hostSupportsVariableReturn (line 595) | public static boolean hostSupportsVariableReturn( Bundle extrasFromH...
method getResultReceiver (line 599) | public static ResultReceiver getResultReceiver(Intent intentFromHost) {
class Event (line 610) | public static class Event {
method hostSupportsRequestQueryDataPassThrough (line 620) | public static boolean hostSupportsRequestQueryDataPassThrough( Bundl...
method addPassThroughData (line 652) | public static void addPassThroughData( Intent requestQueryIntent, Bu...
method retrievePassThroughData (line 671) | public static Bundle retrievePassThroughData( Intent queryConditionI...
method addPassThroughMessageID (line 695) | public static int addPassThroughMessageID( Intent requestQueryIntent...
method retrievePassThroughMessageID (line 715) | public static int retrievePassThroughMessageID( Intent queryConditio...
method retrieveOrCreatePassThroughBundle (line 737) | private static Bundle retrieveOrCreatePassThroughBundle( Intent requ...
class Host (line 753) | public static class Host {
method addCapabilities (line 762) | public static Intent addCapabilities( Intent toPlugin, int capabilit...
method addCompletionIntent (line 774) | public static void addCompletionIntent(Intent fireIntent, Intent com...
method getSettingResultCode (line 793) | public static int getSettingResultCode( Intent completionIntent ) {
method getVariablesBundle (line 810) | public static Bundle getVariablesBundle( Bundle resultExtras ) {
method addHintTimeoutMS (line 824) | public static void addHintTimeoutMS( Intent toPlugin, int timeoutMS ) {
method getHintsBundle (line 828) | private static Bundle getHintsBundle( Intent intent, String funcName...
method haveRequestedTimeout (line 840) | public static boolean haveRequestedTimeout( Bundle extrasFromPluginE...
method getRequestedTimeoutMS (line 844) | public static int getRequestedTimeoutMS( Bundle extrasFromPluginEdit...
method getSettingVariableReplaceKeys (line 852) | public static String [] getSettingVariableReplaceKeys( Bundle fromPl...
method getKeysWithEncoding (line 859) | public static String [] getKeysWithEncoding( Bundle fromPluginEditAc...
method haveRelevantVariables (line 874) | public static boolean haveRelevantVariables( Bundle b ) {
method cleanRelevantVariables (line 878) | public static void cleanRelevantVariables( Bundle b ) {
method cleanHints (line 882) | public static void cleanHints( Bundle extras ) {
method cleanRequestedTimeout (line 886) | public static void cleanRequestedTimeout( Bundle extras ) {
method cleanSettingReplaceVariables (line 890) | public static void cleanSettingReplaceVariables( Bundle b ) {
method getBundleValueSafe (line 897) | private static Object getBundleValueSafe( Bundle b, String key, Class<...
method getExtraValueSafe (line 914) | private static Object getExtraValueSafe( Intent i, String key, Class<?...
method hostSupports (line 920) | private static boolean hostSupports( Bundle extrasFromHost, int capabi...
method getPackageVersionCode (line 928) | public static int getPackageVersionCode( PackageManager pm, String pac...
method variableNameIsLocal (line 946) | private static boolean variableNameIsLocal( String varName ) {
method getStringArrayFromBundleString (line 966) | private static String [] getStringArrayFromBundleString( Bundle bundle...
method addStringArrayToBundleAsString (line 978) | private static void addStringArrayToBundleAsString( String [] toAdd, B...
method getPositiveNonRepeatingRandomInteger (line 1012) | public static int getPositiveNonRepeatingRandomInteger() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Utils.java
class Utils (line 15) | public class Utils {
method initVariableSelectUI (line 17) | public static void initVariableSelectUI(String[] variables, View butto...
method checkBatteryOptimisation (line 49) | public static void checkBatteryOptimisation(final Activity activity) {
method Utils (line 70) | private Utils() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/GetStatePluginBundleValues.java
class GetStatePluginBundleValues (line 44) | @ThreadSafe
method isBundleValid (line 105) | public static boolean isBundleValid(@Nullable final Bundle bundle) {
method generateBundle (line 146) | @NonNull
method getServer (line 172) | @NonNull
method getEntity (line 177) | @NonNull
method getStateVariable (line 182) | @NonNull
method getAttrsVariable (line 187) | @NonNull
method GetStatePluginBundleValues (line 197) | private GetStatePluginBundleValues() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValues.java
class PluginBundleValues (line 44) | @ThreadSafe
method isBundleValid (line 96) | public static boolean isBundleValid(@Nullable final Bundle bundle) {
method generateBundle (line 136) | @NonNull
method getServer (line 160) | @NonNull
method getService (line 165) | @NonNull
method getData (line 170) | @NonNull
method PluginBundleValues (line 180) | private PluginBundleValues() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/RenderTemplatePluginBundleValues.java
class RenderTemplatePluginBundleValues (line 40) | @ThreadSafe
method isBundleValid (line 92) | public static boolean isBundleValid(@Nullable final Bundle bundle) {
method generateBundle (line 128) | @NonNull
method getServer (line 154) | @NonNull
method getTemplate (line 159) | @NonNull
method getVariable (line 164) | @NonNull
method RenderTemplatePluginBundleValues (line 174) | private RenderTemplatePluginBundleValues() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPI.java
class HAAPI (line 19) | public class HAAPI {
method HAAPI (line 22) | public HAAPI(HAServer mServer) {
method testServer (line 26) | public boolean testServer() throws HAAPIException {
method getServices (line 55) | public List<String> getServices() throws HAAPIException {
method callService (line 96) | public void callService(String domain, String service, String data) th...
method getEntities (line 134) | public List<String> getEntities() throws HAAPIException {
method getEntity (line 168) | public HAEntity getEntity(String entityId) throws HAAPIException {
method renderTemplate (line 200) | public String renderTemplate(String template) throws HAAPIException {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIException.java
class HAAPIException (line 5) | public class HAAPIException extends Exception {
method HAAPIException (line 8) | public HAAPIException() { super(); }
method HAAPIException (line 9) | public HAAPIException(String message) { super(message); }
method HAAPIException (line 10) | public HAAPIException(String message, Throwable cause) { super(message...
method HAAPIException (line 11) | public HAAPIException(Throwable cause) { super(cause); }
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIResult.java
class HAAPIResult (line 3) | public class HAAPIResult<T> {
method HAAPIResult (line 7) | public HAAPIResult(T result) {
method HAAPIResult (line 12) | public HAAPIResult(HAAPIException exception) {
method getException (line 17) | public HAAPIException getException() {
method getResult (line 21) | public T getResult() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPITask.java
class HAAPITask (line 5) | public abstract class HAAPITask<Params,Progress,Result> extends AsyncTas...
method HAAPITask (line 8) | protected HAAPITask(HAServer server) {
method doInBackground (line 12) | @Override @SafeVarargs
method doAPIInBackground (line 21) | protected abstract Result doAPIInBackground(HAAPI api, Params... param...
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAEntity.java
class HAEntity (line 3) | public class HAEntity {
method HAEntity (line 6) | public HAEntity(String mState, String mAttributes) {
method getState (line 11) | public String getState() {
method getAttributes (line 15) | public String getAttributes() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServer.java
class HAServer (line 6) | public class HAServer {
method HAServer (line 9) | public HAServer(String name, String baseURL, String accessToken) {
method getName (line 15) | public String getName() {
method setName (line 19) | public void setName(String name) {
method getBaseURL (line 23) | public String getBaseURL() {
method setBaseURL (line 27) | public void setBaseURL(String baseURL) {
method getAccessToken (line 31) | public String getAccessToken() {
method setAccessToken (line 35) | public void setAccessToken(String accessToken) {
method toString (line 39) | @Override
method fromJSON (line 48) | public static HAServer fromJSON(JSONObject json) throws JSONException {
method toJSON (line 54) | public JSONObject toJSON() {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServerStore.java
class HAServerStore (line 15) | public class HAServerStore {
method HAServerStore (line 18) | public HAServerStore(final Context context) {
method getServers (line 24) | public Map<UUID, HAServer> getServers() {
method addServer (line 46) | public UUID addServer(HAServer server) {
method deleteServer (line 72) | public void deleteServer(UUID serverID) {
method updateServer (line 92) | public void updateServer(UUID serverID, HAServer server) {
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractAsyncReceiver.java
class AbstractAsyncReceiver (line 46) | @ThreadSafe
method goAsyncWithCallback (line 56) | @TargetApi(VERSION_CODES.HONEYCOMB)
class AsyncHandlerCallback (line 83) | @TargetApi(VERSION_CODES.HONEYCOMB)
method handleMessage (line 93) | @Override
method castObj (line 125) | @NonNull
method quit (line 131) | private static void quit() {
method quitJellybeanMr2 (line 139) | @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
type AsyncCallback (line 145) | static interface AsyncCallback {
method runAsync (line 150) | public int runAsync();
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractPluginSettingReceiver.java
class AbstractPluginSettingReceiver (line 62) | public abstract class AbstractPluginSettingReceiver extends AbstractAsyn...
method onReceive (line 68) | @Override
method isBundleValid (line 160) | protected abstract boolean isBundleValid(@NonNull final Bundle bundle);
method isAsync (line 177) | protected abstract boolean isAsync();
method firePluginSetting (line 190) | protected abstract void firePluginSetting(@NonNull final Context context,
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiver.java
class FireReceiver (line 35) | public final class FireReceiver extends AbstractPluginSettingReceiver {
method isBundleValid (line 37) | @Override
method isAsync (line 47) | @Override
method firePluginSetting (line 52) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/service/ActionService.java
class ActionService (line 26) | public class ActionService extends JobIntentService {
method enqueueWork (line 37) | public static void enqueueWork(Context context, Intent work) {
method signalUnknownServer (line 42) | private void signalUnknownServer(Intent intent) {
method onHandleWork (line 48) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/ServerSelectionUI.java
class ServerSelectionUI (line 23) | public class ServerSelectionUI {
method ServerSelectionUI (line 32) | public ServerSelectionUI(final Activity activity, final CharSequence h...
method serverCount (line 118) | public int serverCount() {
method currentServer (line 122) | public HAServer currentServer() {
method currentId (line 126) | public UUID currentId() {
method setSelection (line 130) | public void setSelection(UUID id) {
method onActivityResult (line 134) | public boolean onActivityResult(int requestCode, int resultCode, Inten...
type OnServerSelectedListener (line 162) | public interface OnServerSelectedListener {
method onServerSelected (line 163) | void onServerSelected(HAServer server);
method onNothingSelected (line 164) | void onNothingSelected();
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivity.java
class EditActivity (line 58) | @NotThreadSafe
class MyAPITask (line 66) | private abstract static class MyAPITask<Params,Progress,Result> extend...
method MyAPITask (line 69) | MyAPITask(EditActivity activity, HAServer server) {
class GetServicesTask (line 75) | private static class GetServicesTask extends MyAPITask<Void,Void,List<...
method GetServicesTask (line 76) | GetServicesTask(EditActivity activity, HAServer server) {
method doAPIInBackground (line 81) | @Override
method onPostExecute (line 86) | @Override
class TestServiceTask (line 102) | private static class TestServiceTask extends MyAPITask<String,Void,Voi...
method TestServiceTask (line 104) | TestServiceTask(EditActivity context, HAServer server) {
method doAPIInBackground (line 108) | @Override
method onPostExecute (line 114) | @Override
method onCreate (line 129) | @Override
method onActivityResult (line 204) | @Override
method restoreState (line 210) | private void restoreState(UUID id, String service, String data) {
method onPostCreateWithPreviousResult (line 216) | @Override
method isBundleValid (line 226) | @Override
method getResultBundle (line 231) | @Override
method getResultBlurb (line 239) | @NonNull
method getRelevantVariableList (line 254) | @NonNull
method requestedTimeoutMS (line 260) | @Override
method onCreateOptionsMenu (line 265) | @Override
method canSave (line 272) | private boolean canSave() {
method onBackPressed (line 286) | @Override
method onOptionsItemSelected (line 292) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditGetStateActivity.java
class EditGetStateActivity (line 56) | @NotThreadSafe
class MyAPITask (line 64) | private abstract static class MyAPITask<Params,Progress,Result> extend...
method MyAPITask (line 67) | MyAPITask(EditGetStateActivity context, HAServer server) {
class GetEntitiesTask (line 73) | private static class GetEntitiesTask extends MyAPITask<Void,Void,List<...
method GetEntitiesTask (line 74) | GetEntitiesTask(EditGetStateActivity activity, HAServer server) {
method doAPIInBackground (line 79) | @Override
method onPostExecute (line 84) | @Override
class TestEntityTask (line 100) | private static class TestEntityTask extends MyAPITask<String,Void,HAEn...
method TestEntityTask (line 102) | TestEntityTask(EditGetStateActivity context, HAServer server) {
method doAPIInBackground (line 106) | @Override
method onPostExecute (line 111) | @Override
method onCreate (line 126) | @Override
method onActivityResult (line 197) | @Override
method restoreState (line 203) | private void restoreState(UUID id, String entity, String stateVariable...
method onPostCreateWithPreviousResult (line 212) | @Override
method isBundleValid (line 223) | @Override
method getResultBundle (line 228) | @Override
method getResultBlurb (line 237) | @NonNull
method getRelevantVariableList (line 252) | @NonNull
method requestedTimeoutMS (line 262) | @Override
method onCreateOptionsMenu (line 267) | @Override
method canSave (line 274) | private boolean canSave() {
method onBackPressed (line 291) | @Override
method onOptionsItemSelected (line 297) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditRenderTemplateActivity.java
class EditRenderTemplateActivity (line 47) | @NotThreadSafe
class MyAPITask (line 53) | private abstract static class MyAPITask<Params,Progress,Result> extend...
method MyAPITask (line 56) | MyAPITask(EditRenderTemplateActivity activity, HAServer server) {
class TestTemplateTask (line 62) | private static class TestTemplateTask extends MyAPITask<String,Void,St...
method TestTemplateTask (line 64) | TestTemplateTask(EditRenderTemplateActivity context, HAServer server) {
method doAPIInBackground (line 68) | @Override
method onPostExecute (line 73) | @Override
method onCreate (line 88) | @Override
method onActivityResult (line 152) | @Override
method restoreState (line 158) | private void restoreState(UUID id, String template, String variable) {
method onPostCreateWithPreviousResult (line 164) | @Override
method isBundleValid (line 174) | @Override
method getResultBundle (line 179) | @Override
method getResultBlurb (line 187) | @NonNull
method getRelevantVariableList (line 202) | @NonNull
method requestedTimeoutMS (line 208) | @Override
method onCreateOptionsMenu (line 213) | @Override
method canSave (line 220) | private boolean canSave() {
method onBackPressed (line 233) | @Override
method onOptionsItemSelected (line 239) | @Override
FILE: HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditServerActivity.java
class EditServerActivity (line 23) | public class EditServerActivity extends AppCompatActivity {
class MyAPITask (line 38) | private abstract static class MyAPITask<Params,Progress,Result> extend...
method MyAPITask (line 41) | MyAPITask(EditServerActivity context, HAServer server) {
class TestServerTask (line 47) | private static class TestServerTask extends MyAPITask<Void,Void,Boolea...
method TestServerTask (line 48) | TestServerTask(EditServerActivity context, HAServer server) {
method doAPIInBackground (line 52) | @Override
method onPostExecute (line 57) | @Override
method onCreate (line 72) | @Override
method onCreateOptionsMenu (line 114) | @Override
method onBackPressed (line 121) | @Override
method onOptionsItemSelected (line 131) | @Override
Condensed preview — 66 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (284K chars).
[
{
"path": ".gitignore",
"chars": 148,
"preview": "syntax: glob\n.crashlytics_data/\n.DS_Store\n.gradle/\n.settings\n.idea\n*.iml\n*.apk\nbin/\nbuild/\ngen/\nlocal.properties\nHomeAss"
},
{
"path": "HomeAssistantTaskerPlugin/build.gradle",
"chars": 3504,
"preview": "apply plugin: 'com.android.application'\napply plugin: 'findbugs'\n\ndependencies {\n implementation group:'net.jcip', na"
},
{
"path": "HomeAssistantTaskerPlugin/lint.xml",
"chars": 292,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n <!-- App indexing for this app makes no sense. -->\n <issue id=\"Goog"
},
{
"path": "HomeAssistantTaskerPlugin/proguard-project.txt",
"chars": 268,
"preview": "# This improves obfuscation and moves non-public classes to their own namespace.\n-repackageclasses 'com.markadamson.task"
},
{
"path": "HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValuesTest.java",
"chars": 5224,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiverTest.java",
"chars": 2941,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/InstallLocation.java",
"chars": 6807,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/setting/toast/test/ManifestTest.java",
"chars": 5116,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/androidTest/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivityTest.java",
"chars": 10793,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/debug/AndroidManifest.xml",
"chars": 373,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xm"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/AndroidManifest.xml",
"chars": 8670,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n Because plug-ins interact with the host in the background, plug-ins MUS"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/internal/PluginActivityDelegate.java",
"chars": 5190,
"preview": "package com.markadamson.locale.sdk.client.internal;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport "
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/AbstractAppCompatPluginActivity.java",
"chars": 5834,
"preview": "/*\n * android-plugin-client-sdk-for-locale https://github.com/twofortyfouram/android-plugin-client-sdk-for-locale\n * Cop"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/locale/sdk/client/ui/activity/IPluginActivity.java",
"chars": 3094,
"preview": "package com.markadamson.locale.sdk.client.ui.activity;\n\nimport android.os.Bundle;\nimport android.support.annotation.NonN"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Constants.java",
"chars": 445,
"preview": "package com.markadamson.taskerplugin.homeassistant;\n\nimport android.support.annotation.NonNull;\n\npublic class Constants "
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/PluginApplication.java",
"chars": 1384,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/TaskerPlugin.java",
"chars": 42087,
"preview": "//package com.yourcompany.yourcondition;\n//package com.yourcompany.yoursetting;\npackage com.markadamson.taskerplugin.hom"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/Utils.java",
"chars": 3285,
"preview": "package com.markadamson.taskerplugin.homeassistant;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/GetStatePluginBundleValues.java",
"chars": 7611,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/PluginBundleValues.java",
"chars": 6840,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/bundle/RenderTemplatePluginBundleValues.java",
"chars": 6725,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPI.java",
"chars": 10017,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimp"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIException.java",
"chars": 460,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\nimport java.lang.Exception;\n\npublic class HAAPIException exte"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPIResult.java",
"chars": 510,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\npublic class HAAPIResult<T> {\n private final HAAPIExceptio"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAAPITask.java",
"chars": 706,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\nimport android.os.AsyncTask;\n\npublic abstract class HAAPITask"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAEntity.java",
"chars": 391,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\npublic class HAEntity {\n private String mState, mAttribute"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServer.java",
"chars": 1590,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\np"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/model/HAServerStore.java",
"chars": 3899,
"preview": "package com.markadamson.taskerplugin.homeassistant.model;\n\nimport android.content.Context;\nimport android.content.Shared"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractAsyncReceiver.java",
"chars": 5741,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/AbstractPluginSettingReceiver.java",
"chars": 8082,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/receiver/FireReceiver.java",
"chars": 2764,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/service/ActionService.java",
"chars": 5459,
"preview": "package com.markadamson.taskerplugin.homeassistant.service;\n\nimport android.content.Context;\nimport android.content.Inte"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/ServerSelectionUI.java",
"chars": 7262,
"preview": "package com.markadamson.taskerplugin.homeassistant.ui;\n\nimport android.app.Activity;\nimport android.content.DialogInterf"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditActivity.java",
"chars": 11091,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditGetStateActivity.java",
"chars": 12238,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditRenderTemplateActivity.java",
"chars": 9368,
"preview": "/*\n * home-assistant-plugin-for-tasker <https://github.com/MarkAdamson/home-assistant-plugin-for-tasker>\n * Copyright 20"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/java/com/markadamson/taskerplugin/homeassistant/ui/activity/EditServerActivity.java",
"chars": 6120,
"preview": "package com.markadamson.taskerplugin.homeassistant.ui.activity;\n\nimport android.app.Activity;\nimport android.content.Int"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/drawable/baseline_add_24.xml",
"chars": 384,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\""
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/drawable/baseline_cancel_24.xml",
"chars": 527,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\""
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/drawable/baseline_delete_24.xml",
"chars": 432,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\""
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/drawable/baseline_edit_24.xml",
"chars": 515,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\""
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/drawable/outline_label_24.xml",
"chars": 516,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\""
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/layout/edit_get_state.xml",
"chars": 5217,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n** Copyright 2019 Mark Adamson\n**\n** Original Author:\n** Copyright 2013 two "
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/layout/edit_render_template.xml",
"chars": 3857,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n** Copyright 2019 Mark Adamson\n**\n** Licensed under the Apache License, Vers"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/layout/edit_server.xml",
"chars": 2529,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n androi"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/layout/main.xml",
"chars": 4062,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n** Copyright 2019 Mark Adamson\n**\n** Original Author:\n** Copyright 2013 two "
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/layout/select_server.xml",
"chars": 1499,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n and"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/menu/menu.xml",
"chars": 417,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tool"
},
{
"path": "HomeAssistantTaskerPlugin/src/main/res/values/strings.xml",
"chars": 2630,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n <!-- App name -->\n <string name=\"app_name\">Home Assistant Plu"
},
{
"path": "LICENSE",
"chars": 11053,
"preview": " home-assistant-plugin-for-tasker\n https://github.com/MarkAdamson/home-assistant-plugin-for-tasker\n Copyright 2019"
},
{
"path": "README.md",
"chars": 2278,
"preview": "# Home Assistant Plug-In for Tasker\nThis is a Tasker plug-in to allow calling services on and getting entity states from"
},
{
"path": "build.gradle",
"chars": 977,
"preview": "apply plugin: 'com.github.ben-manes.versions'\n\nbuildscript {\n repositories {\n maven { url 'https://maven.googl"
},
{
"path": "circle.yml",
"chars": 3012,
"preview": "# Many CI systems start with the intention of having convention over configuration, and trying to make things look nice "
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 230,
"preview": "#Mon Jul 08 14:35:42 BST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_"
},
{
"path": "gradle.properties",
"chars": 1110,
"preview": "// Moar memory is required for in-process dex.\norg.gradle.jvmargs=-Xmx2g\n\n// Enable automatic download of Android SDK co"
},
{
"path": "gradlew",
"chars": 5299,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2260,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "settings.gradle",
"chars": 37,
"preview": "include ':HomeAssistantTaskerPlugin'\n"
},
{
"path": "tools/ci/android-sdk-license",
"chars": 40,
"preview": "8933bad161af4178b1185d1a37fbf41ea5269c55"
},
{
"path": "tools/ci/android-sdk-setup.sh",
"chars": 1912,
"preview": "#!/bin/bash\n\n# Sets up the Android SDK.\n# Marks a flag to remember whether the SDK has been installed previously\n# and w"
},
{
"path": "tools/ci/firebase-test-lab-test-module.sh",
"chars": 2131,
"preview": "#!/bin/bash\n\n# Executes tests on a specified module and extracts the results into the \n# standard Android test directori"
},
{
"path": "tools/ci/google-cloud-test-lab-setup.sh",
"chars": 1721,
"preview": "#!/bin/bash\n\n# Sets up gcloud. Marks a flag to remember whether the SDK has been installed previously\n# and will bypass"
},
{
"path": "tools/ci/repositories.cfg",
"chars": 79,
"preview": "### User Sources for Android SDK Manager\n#Mon Jan 30 14:33:04 PST 2017\ncount=0\n"
},
{
"path": "tools/findbugs/android-filter.xml",
"chars": 197,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<FindBugsFilter>\n <Match>\n <Class name=\"~.*\\.R\\$.*\"/>\n </Match>\n "
},
{
"path": "tools/gcloud.yml",
"chars": 475,
"preview": "toastPluginSettingApp:\n type: instrumentation\n app: toastPluginSettingApp/build/outputs/apk/toastPluginSettingApp-debu"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the MarkAdamson/home-assistant-plugin-for-tasker GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 66 files (261.5 KB), approximately 59.4k tokens, and a symbol index with 293 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.