├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── libraries │ └── support_v4_19_1_0.xml ├── misc.xml ├── modules.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── PlayStoreImages ├── FeatureGraphic.png └── FeatureGraphic.xcf ├── README.md ├── UDP Sender ├── .gitignore ├── UDP Sender.iml ├── build.gradle ├── proguard-rules.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── hastarin │ │ └── android │ │ └── udpsender │ │ ├── Constants.java │ │ ├── HexHelper.java │ │ ├── MainActivity.java │ │ ├── PluginApplication.java │ │ ├── SendToUriActivity.java │ │ ├── TaskerPlugin.java │ │ ├── UdpSender.java │ │ ├── receiver │ │ └── FireReceiver.java │ │ └── ui │ │ ├── AbstractPluginActivity.java │ │ ├── EditActivity.java │ │ ├── EditTextHex.java │ │ ├── EditTextIPAddress.java │ │ └── bundle │ │ ├── BundleScrubber.java │ │ └── PluginBundleManager.java │ └── res │ ├── drawable-hdpi │ ├── ic_launcher.png │ └── ic_locale_plugin.png │ ├── drawable-ldpi │ └── ic_local_plugin.png │ ├── drawable-mdpi │ ├── ic_launcher.png │ └── ic_local_plugin.png │ ├── drawable-xhdpi │ ├── ic_launcher.png │ └── ic_local_plugin.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout-land │ └── activity_main.xml │ ├── layout-large-land │ └── activity_main.xml │ ├── layout │ └── activity_main.xml │ ├── menu │ └── main.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── android-udpsender.iml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libraries └── locale-api │ ├── AndroidManifest.xml │ ├── License.txt │ ├── Read Me.txt │ ├── build.gradle │ ├── build │ ├── bundles │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ ├── R.txt │ │ │ ├── classes.jar │ │ │ └── res │ │ │ │ ├── drawable-hdpi-v14 │ │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ │ ├── drawable-ldpi-v14 │ │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ │ ├── drawable-mdpi-v14 │ │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ │ ├── drawable-xhdpi-v14 │ │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ │ ├── drawable │ │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.xml │ │ │ │ ├── twofortyfouram_locale_ic_menu_help.xml │ │ │ │ └── twofortyfouram_locale_ic_menu_save.xml │ │ │ │ ├── menu │ │ │ │ └── twofortyfouram_locale_help_save_dontsave.xml │ │ │ │ ├── values-cs │ │ │ │ └── values.xml │ │ │ │ ├── values-de │ │ │ │ └── values.xml │ │ │ │ ├── values-es │ │ │ │ └── values.xml │ │ │ │ ├── values-fr │ │ │ │ └── values.xml │ │ │ │ ├── values-hdpi │ │ │ │ └── values.xml │ │ │ │ ├── values-it │ │ │ │ └── values.xml │ │ │ │ ├── values-ja │ │ │ │ └── values.xml │ │ │ │ ├── values-ko │ │ │ │ └── values.xml │ │ │ │ ├── values-ldpi │ │ │ │ └── values.xml │ │ │ │ ├── values-mdpi │ │ │ │ └── values.xml │ │ │ │ ├── values-nb │ │ │ │ └── values.xml │ │ │ │ ├── values-nl │ │ │ │ └── values.xml │ │ │ │ ├── values-pt-rBR │ │ │ │ └── values.xml │ │ │ │ ├── values-ru │ │ │ │ └── values.xml │ │ │ │ ├── values-sv │ │ │ │ └── values.xml │ │ │ │ ├── values-v11 │ │ │ │ └── values.xml │ │ │ │ ├── values-v14 │ │ │ │ └── values.xml │ │ │ │ ├── values-v9 │ │ │ │ └── values.xml │ │ │ │ ├── values-vi │ │ │ │ └── values.xml │ │ │ │ └── values │ │ │ │ └── values.xml │ │ └── release │ │ │ ├── AndroidManifest.xml │ │ │ ├── R.txt │ │ │ ├── classes.jar │ │ │ └── res │ │ │ ├── drawable-hdpi-v14 │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ ├── drawable-ldpi-v14 │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ ├── drawable-mdpi-v14 │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ ├── drawable-xhdpi-v14 │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ │ └── twofortyfouram_locale_ic_menu_save.png │ │ │ ├── drawable │ │ │ ├── twofortyfouram_locale_ic_menu_dontsave.xml │ │ │ ├── twofortyfouram_locale_ic_menu_help.xml │ │ │ └── twofortyfouram_locale_ic_menu_save.xml │ │ │ ├── menu │ │ │ └── twofortyfouram_locale_help_save_dontsave.xml │ │ │ ├── values-cs │ │ │ └── values.xml │ │ │ ├── values-de │ │ │ └── values.xml │ │ │ ├── values-es │ │ │ └── values.xml │ │ │ ├── values-fr │ │ │ └── values.xml │ │ │ ├── values-hdpi │ │ │ └── values.xml │ │ │ ├── values-it │ │ │ └── values.xml │ │ │ ├── values-ja │ │ │ └── values.xml │ │ │ ├── values-ko │ │ │ └── values.xml │ │ │ ├── values-ldpi │ │ │ └── values.xml │ │ │ ├── values-mdpi │ │ │ └── values.xml │ │ │ ├── values-nb │ │ │ └── values.xml │ │ │ ├── values-nl │ │ │ └── values.xml │ │ │ ├── values-pt-rBR │ │ │ └── values.xml │ │ │ ├── values-ru │ │ │ └── values.xml │ │ │ ├── values-sv │ │ │ └── values.xml │ │ │ ├── values-v11 │ │ │ └── values.xml │ │ │ ├── values-v14 │ │ │ └── values.xml │ │ │ ├── values-v9 │ │ │ └── values.xml │ │ │ ├── values-vi │ │ │ └── values.xml │ │ │ └── values │ │ │ └── values.xml │ ├── incremental │ │ ├── aidl │ │ │ ├── debug │ │ │ │ └── dependency.store │ │ │ └── release │ │ │ │ └── dependency.store │ │ ├── mergeAssets │ │ │ ├── debug │ │ │ │ └── merger.xml │ │ │ └── release │ │ │ │ └── merger.xml │ │ └── packageResources │ │ │ ├── debug │ │ │ └── merger.xml │ │ │ └── release │ │ │ └── merger.xml │ ├── libs │ │ └── locale-api.aar │ ├── source │ │ ├── buildConfig │ │ │ ├── debug │ │ │ │ └── com │ │ │ │ │ └── twofortyfouram │ │ │ │ │ └── locale │ │ │ │ │ └── api │ │ │ │ │ └── BuildConfig.java │ │ │ └── release │ │ │ │ └── com │ │ │ │ └── twofortyfouram │ │ │ │ └── locale │ │ │ │ └── api │ │ │ │ └── BuildConfig.java │ │ └── r │ │ │ ├── debug │ │ │ └── com │ │ │ │ └── twofortyfouram │ │ │ │ └── locale │ │ │ │ └── api │ │ │ │ └── R.java │ │ │ └── release │ │ │ └── com │ │ │ └── twofortyfouram │ │ │ └── locale │ │ │ └── api │ │ │ └── R.java │ └── tmp │ │ ├── packageDebugJar │ │ └── MANIFEST.MF │ │ └── packageReleaseJar │ │ └── MANIFEST.MF │ ├── lint.xml │ ├── locale-api.iml │ ├── proguard-project.txt │ ├── res │ ├── drawable-hdpi-v14 │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ └── twofortyfouram_locale_ic_menu_save.png │ ├── drawable-ldpi-v14 │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ └── twofortyfouram_locale_ic_menu_save.png │ ├── drawable-mdpi-v14 │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ └── twofortyfouram_locale_ic_menu_save.png │ ├── drawable-xhdpi-v14 │ │ ├── twofortyfouram_locale_ic_menu_dontsave.png │ │ ├── twofortyfouram_locale_ic_menu_help.png │ │ └── twofortyfouram_locale_ic_menu_save.png │ ├── drawable │ │ ├── twofortyfouram_locale_ic_menu_dontsave.xml │ │ ├── twofortyfouram_locale_ic_menu_help.xml │ │ └── twofortyfouram_locale_ic_menu_save.xml │ ├── menu │ │ └── twofortyfouram_locale_help_save_dontsave.xml │ ├── values-cs │ │ └── strings-cs.xml │ ├── values-de │ │ └── strings-de.xml │ ├── values-es │ │ └── strings-es.xml │ ├── values-fr │ │ └── strings-fr.xml │ ├── values-hdpi │ │ └── integers.xml │ ├── values-it │ │ └── strings-it.xml │ ├── values-ja │ │ └── strings-ja.xml │ ├── values-ko │ │ └── strings-ko.xml │ ├── values-ldpi │ │ └── integers.xml │ ├── values-mdpi │ │ └── integers.xml │ ├── values-nb │ │ └── strings-nb.xml │ ├── values-nl │ │ └── strings-nl.xml │ ├── values-pt-rBR │ │ └── strings-pt-rBR.xml │ ├── values-ru │ │ └── strings-ru.xml │ ├── values-sv │ │ └── strings-sv.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ ├── values-v9 │ │ └── styles.xml │ ├── values-vi │ │ └── strings-vi.xml │ └── values │ │ ├── id.xml │ │ ├── strings-en.xml │ │ └── styles.xml │ └── src │ └── com │ └── twofortyfouram │ └── locale │ ├── BreadCrumber.java │ ├── Constants.java │ ├── Intent.java │ └── PackageUtilities.java ├── proguard-project.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | # IntelliJ IDEA user settings 23 | *.iws 24 | .idea/workspace.xml 25 | .idea/tasks.xml 26 | out/ 27 | 28 | # Gradle build folder 29 | .gradle/ 30 | 31 | # Proguard logs 32 | proguard_logs/ 33 | 34 | # Misc 35 | Thumbs.db 36 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_19_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PlayStoreImages/FeatureGraphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/PlayStoreImages/FeatureGraphic.png -------------------------------------------------------------------------------- /PlayStoreImages/FeatureGraphic.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/PlayStoreImages/FeatureGraphic.xcf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-udpsender 2 | ================= 3 | 4 | [UDP Sender](https://play.google.com/store/apps/details?id=com.hastarin.android.udpsender) is a simple application to allow sending UDP packets. 5 | 6 | ![Landscape Screenshot](https://raw.github.com/hastarin/android-udpsender/screenshots/screenshots/S4Landscape.png) 7 | 8 | It supports the implicit [Intent](http://developer.android.com/reference/android/content/Intent.html) Intent.ACTION_SENDTO with a udp://host:port/data format URI. 9 | 10 | * data is treated as hex if it starts with 0x. 11 | * If a string starting with 0x is to be sent it can be escaped as \0x 12 | 13 | Added in v1.3.1 - Current in Beta see http://goo.gl/reibxw to join 14 | * Allow broadcast packets to be sent. 15 | * Fixed a bug causing a period to be prematurely inserted when entering an IP. 16 | 17 | Added in v1.3 18 | * Added support for Tasker to do variable replacement for the host/port as well as the text. 19 | * Remembers your settings when you quit and re-launch the app. 20 | 21 | Added in v1.2 22 | * Now supports multi line text messages. 23 | * Bugfix for Send button not working in Landscape mode. 24 | 25 | Added in v1.1 26 | 27 | * Also acts as a plugin for Locale and Tasker. 28 | * Variable replacement is supported for the Tasker plugin in the text field. 29 | * Changed theme to Holo (Dark) 30 | * Removed unused permissions 31 | * Various tweaks/fixes 32 | 33 | 34 | Tasker Integration 35 | ------------------ 36 | Tasker plugin support is now complete and should be straight forward to use. 37 | 38 | ![Tasker Plugin](https://raw.github.com/hastarin/android-udpsender/screenshots/screenshots/TaskerPlugin.png) 39 | 40 | 41 | Alternatively UDP packets can be sent via [Tasker](http://tasker.dinglisch.net/) with the Misc/Send Intent action. 42 | 43 | Just set it up as follows: 44 | * Action = android.intent.action.SENDTO 45 | * Data = udp://host:port/data 46 | * Target = Activity 47 | 48 | ![Tasker Intent](https://raw.github.com/hastarin/android-udpsender/screenshots/screenshots/TaskerIntent.png) 49 | 50 | Example for a www.limitlessled.com Wi-Fi bridge at 192.168.1.100 to turn on all the white LEDs 51 | * Action = android.intent.action.SENDTO 52 | * Data = udp://192.168.1.100:50000/0x350055 53 | * Target = Activity 54 | 55 | 56 | Tasker Example Project 57 | ---------------------- 58 | 59 | ![Lights Scene](https://raw.github.com/hastarin/android-udpsender/screenshots/screenshots/TaskerLightsScene.png) 60 | 61 | You can get a copy of my current Tasker project (or at least a portion of it) [Lights.prj.xml](https://raw.github.com/hastarin/android-udpsender/screenshots/screenshots/Lights.prj.xml) that I'm using for controlling my lights. 62 | 63 | To give you an idea of how I'm using it I have the following: 64 | * Gesture control via my launcher (Nova) to turn all lights on/off/full bright/night mode and dim/brighten lights. 65 | * Using the Scene shown above I can turn all/individual zones on/off and control bright/dim/warm/cool. 66 | * NOTE: A swipe up/down in the colored square controls a bright/dim ramp and left/right for warm/cool. 67 | * An NFC tag on my bed that when tapped will turn the lights off, or keep some in night mode if I have guests. 68 | * When my phone connects to the Wi-Fi, and it's between sunrise and sunset, my entry hall light is turned on. 69 | 70 | 71 | NOTE: *It should be obvious but you will need to edit this to use it* 72 | 73 | * Insert your own Weather Underground API key instead of <YOURAPIKEYHERE>. 74 | 75 | See http://www.wunderground.com/weather/api/d/docs for more info on getting one. 76 | 77 | It's used by the "Get Sunrise/set" which is called once a day to update the global variables. I use this in "Check Night Time" to only turn my entry light on at night when I arrive home and connect to my Wi-Fi. 78 | 79 | * You'll want to replace the string for my SSID with your own. Search and replace C86241. Or remove the Stop commands that prevent things working when not on the correct Wi-Fi. 80 | 81 | * You'll obviously need to change IPs and Names to suit your setup. 82 | 83 | * Finally if you're using a device that has a different size screen (my S4 is 1920x1080) you'll need to edit the scene so it fits and adjust the maths for the gestures. 84 | 85 | -------------------------------------------------------------------------------- /UDP Sender/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /UDP Sender/UDP Sender.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /UDP Sender/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'android' 2 | android { 3 | compileSdkVersion 19 4 | buildToolsVersion '19.0.3' 5 | 6 | defaultConfig { 7 | minSdkVersion 8 8 | targetSdkVersion 19 9 | } 10 | 11 | signingConfigs { 12 | release { 13 | storeFile file(RELEASE_STORE_FILE) 14 | storePassword RELEASE_STORE_PASSWORD 15 | keyAlias RELEASE_KEY_ALIAS 16 | keyPassword RELEASE_KEY_PASSWORD 17 | } 18 | } 19 | 20 | buildTypes { 21 | release { 22 | runProguard true 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 24 | debuggable false 25 | signingConfig signingConfigs.release 26 | } 27 | debug { 28 | debuggable true 29 | runProguard false 30 | } 31 | } 32 | } 33 | dependencies { 34 | compile project(':libraries:locale-api') 35 | } -------------------------------------------------------------------------------- /UDP Sender/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:/Development/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /UDP Sender/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 19 | 20 | 21 | 27 | 28 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 62 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /UDP Sender/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 two forty four a.m. LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * 8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | package com.hastarin.android.udpsender; 14 | 15 | import android.content.Context; 16 | 17 | /** 18 | * Class of constants used by this Locale plug-in. 19 | */ 20 | public final class Constants 21 | { 22 | /** 23 | * Log tag for logcat messages. 24 | */ 25 | public static final String LOG_TAG = "UDPSender"; //$NON-NLS-1$ 26 | 27 | /** 28 | * Flag to enable logcat messages. 29 | */ 30 | public static final boolean IS_LOGGABLE = BuildConfig.DEBUG; 31 | 32 | /** 33 | * Flag to enable runtime checking of method parameters. 34 | */ 35 | public static final boolean IS_PARAMETER_CHECKING_ENABLED = BuildConfig.DEBUG; 36 | 37 | /** 38 | * Flag to enable runtime checking of whether a method is called on the correct thread. 39 | */ 40 | public static final boolean IS_CORRECT_THREAD_CHECKING_ENABLED = BuildConfig.DEBUG; 41 | 42 | /** 43 | * Determines the "versionCode" in the {@code AndroidManifest}. 44 | * 45 | * @param context to read the versionCode. 46 | * @return versionCode of the app. 47 | */ 48 | public static int getVersionCode(final Context context) 49 | { 50 | if (Constants.IS_PARAMETER_CHECKING_ENABLED) 51 | { 52 | if (null == context) 53 | { 54 | throw new IllegalArgumentException("context cannot be null"); //$NON-NLS-1$ 55 | } 56 | } 57 | 58 | try 59 | { 60 | return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode; 61 | } 62 | catch (final UnsupportedOperationException e) 63 | { 64 | /* 65 | * This exception is thrown by test contexts 66 | */ 67 | 68 | return 1; 69 | } 70 | catch (final Exception e) 71 | { 72 | throw new RuntimeException(e); 73 | } 74 | } 75 | 76 | /** 77 | * Private constructor prevents instantiation. 78 | * 79 | * @throws UnsupportedOperationException because this class cannot be instantiated. 80 | */ 81 | private Constants() 82 | { 83 | throw new UnsupportedOperationException("This class is non-instantiable"); //$NON-NLS-1$ 84 | } 85 | } -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/HexHelper.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender; 2 | 3 | import java.util.Locale; 4 | 5 | public class HexHelper { 6 | public static byte[] hexStringToBytes(String input) { 7 | input = input.toLowerCase(Locale.US); 8 | int n = input.length() / 2; 9 | byte[] output = new byte[n]; 10 | int l = 0; 11 | for (int k = 0; k < n; k++) { 12 | char c = input.charAt(l++); 13 | byte b = (byte) ((c >= 'a' ? (c - 'a' + 10) : (c - '0')) << 4); 14 | c = input.charAt(l++); 15 | b |= (byte) (c >= 'a' ? (c - 'a' + 10) : (c - '0')); 16 | output[k] = b; 17 | } 18 | return output; 19 | } 20 | 21 | public static String bytesToHex(byte[] bytes) { 22 | final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 23 | char[] hexChars = new char[bytes.length * 2]; 24 | int v; 25 | for ( int j = 0; j < bytes.length; j++ ) { 26 | v = bytes[j] & 0xFF; 27 | hexChars[j * 2] = hexArray[v >>> 4]; 28 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 29 | } 30 | return new String(hexChars); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.text.InputType; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.view.View; 13 | import android.widget.EditText; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | import android.widget.ToggleButton; 17 | 18 | public class MainActivity extends Activity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | 25 | // Restore preferences 26 | SharedPreferences settings = getPreferences(0); 27 | ToggleButton toggleButton = ((ToggleButton) findViewById(R.id.toggleButton)); 28 | boolean checked = settings.getBoolean("toggleChecked", false); 29 | toggleButton.setChecked(checked); 30 | toggleButton.setVisibility(View.GONE); 31 | EditText editText = (EditText) findViewById(R.id.editTextIP); 32 | if (checked) { 33 | editText.setInputType(InputType.TYPE_CLASS_TEXT); 34 | } 35 | editText.setText(settings.getString("host", ""), TextView.BufferType.EDITABLE); 36 | editText = (EditText) findViewById(R.id.editTextPort); 37 | if (checked) { 38 | editText.setInputType(InputType.TYPE_CLASS_TEXT); 39 | } 40 | editText.setText(settings.getString("port", ""), TextView.BufferType.EDITABLE); 41 | editText = (EditText) findViewById(R.id.editTextData); 42 | editText.setText(settings.getString("dataText", ""), TextView.BufferType.EDITABLE); 43 | editText = (EditText) findViewById(R.id.editTextHexData); 44 | editText.setText(settings.getString("dataHex", ""), TextView.BufferType.EDITABLE); 45 | } 46 | 47 | @Override 48 | public void onPause() { 49 | super.onPause(); 50 | 51 | // Get current values 52 | EditText editText = (EditText) findViewById(R.id.editTextIP); 53 | String host = editText.getText().toString(); 54 | editText = (EditText) findViewById(R.id.editTextPort); 55 | String port = editText.getText().toString(); 56 | editText = (EditText) findViewById(R.id.editTextData); 57 | String dataText = editText.getText().toString(); 58 | editText = (EditText) findViewById(R.id.editTextHexData); 59 | String dataHex = editText.getText().toString(); 60 | 61 | // We need an Editor object to make preference changes. 62 | // All objects are from android.context.Context 63 | SharedPreferences settings = getPreferences(0); 64 | SharedPreferences.Editor editor = settings.edit(); 65 | editor.putString("host", host); 66 | editor.putString("port", port); 67 | editor.putString("dataText", dataText); 68 | editor.putString("dataHex", dataHex); 69 | editor.putBoolean("toggleChecked", ((ToggleButton) findViewById(R.id.toggleButton)).isChecked()); 70 | 71 | // Commit the edits! 72 | editor.commit(); 73 | } 74 | 75 | @Override 76 | public boolean onCreateOptionsMenu(Menu menu) { 77 | // Inflate the menu; this adds items to the action bar if it is present. 78 | getMenuInflater().inflate(R.menu.main, menu); 79 | return true; 80 | } 81 | 82 | @Override 83 | public boolean onOptionsItemSelected(MenuItem item) { 84 | // Handle item selection 85 | switch (item.getItemId()) { 86 | case R.id.action_send: 87 | this.sendData(null); 88 | return true; 89 | default: 90 | return super.onOptionsItemSelected(item); 91 | } 92 | } 93 | 94 | public void sendData(View view) { 95 | Context context = getApplicationContext(); 96 | 97 | EditText editText = (EditText) findViewById(R.id.editTextIP); 98 | String host = editText.getText().toString(); 99 | if (!host.matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) { 100 | CharSequence text = "Error: Invalid IP Address"; 101 | Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); 102 | toast.show(); 103 | return; 104 | } 105 | editText = (EditText) findViewById(R.id.editTextPort); 106 | String port = editText.getText().toString(); 107 | if (!port.matches("^(6553[0-5]|655[0-2]\\d|65[0-4]\\d\\d|6[0-4]\\d{3}|[1-5]\\d{4}|[1-9]\\d{0,3}|0)$")) { 108 | CharSequence text = "Error: Invalid Port Number"; 109 | Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); 110 | toast.show(); 111 | return; 112 | } 113 | editText = (EditText) findViewById(R.id.editTextData); 114 | String dataText = editText.getText().toString(); 115 | editText = (EditText) findViewById(R.id.editTextHexData); 116 | String dataHex = editText.getText().toString(); 117 | if (dataText.length() < 1 && dataHex.length() < 2) { 118 | CharSequence text = "Error: Text/Hex required to send"; 119 | Toast toast = Toast.makeText(context, text, Toast.LENGTH_SHORT); 120 | toast.show(); 121 | return; 122 | } 123 | String uriString = "udp://" + host + ":" + port + "/"; 124 | if (dataHex.length() >= 2) { 125 | uriString += Uri.encode("0x" + dataHex); 126 | } else { 127 | uriString += Uri.encode(dataText); 128 | } 129 | Uri uri = Uri.parse(uriString); 130 | Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 131 | intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); 132 | intent.addCategory(Intent.CATEGORY_DEFAULT); 133 | 134 | startActivity(intent); 135 | } 136 | 137 | public void onToggleClicked(View view) { 138 | boolean on = ((ToggleButton) view).isChecked(); 139 | 140 | 141 | EditText editTextIp = (EditText) findViewById(R.id.editTextIP); 142 | EditText editTextPort = (EditText) findViewById(R.id.editTextPort); 143 | if (on) { 144 | editTextIp.setInputType(InputType.TYPE_CLASS_TEXT); 145 | editTextPort.setInputType(InputType.TYPE_CLASS_TEXT); 146 | } else { 147 | editTextIp.setInputType(InputType.TYPE_CLASS_PHONE); 148 | editTextPort.setInputType(InputType.TYPE_CLASS_PHONE); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/PluginApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 two forty four a.m. LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * 8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | package com.hastarin.android.udpsender; 14 | 15 | import android.annotation.TargetApi; 16 | import android.app.Application; 17 | import android.os.Build; 18 | import android.util.Log; 19 | 20 | /** 21 | * Implements an application object for the plug-in. 22 | *

23 | * This application is non-essential for the plug-in's operation; it simply enables debugging options globally 24 | * for the app. 25 | */ 26 | public final class PluginApplication extends Application 27 | { 28 | @Override 29 | public void onCreate() 30 | { 31 | super.onCreate(); 32 | 33 | if (BuildConfig.DEBUG) 34 | { 35 | if (Constants.IS_LOGGABLE) 36 | { 37 | Log.v(Constants.LOG_TAG, "Application is debuggable. Enabling additional debug logging"); //$NON-NLS-1$ 38 | } 39 | 40 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) 41 | { 42 | enableApiLevel9Debugging(); 43 | } 44 | 45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 46 | { 47 | enableApiLevel11Debugging(); 48 | } 49 | 50 | /* 51 | * If using the Fragment compatibility library, enable debug logging here 52 | */ 53 | // android.support.v4.app.FragmentManager.enableDebugLogging(true); 54 | // android.support.v4.app.LoaderManager.enableDebugLogging(true); 55 | } 56 | } 57 | 58 | @TargetApi(Build.VERSION_CODES.GINGERBREAD) 59 | private static void enableApiLevel9Debugging() 60 | { 61 | android.os.StrictMode.enableDefaults(); 62 | } 63 | 64 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 65 | private static void enableApiLevel11Debugging() 66 | { 67 | android.app.LoaderManager.enableDebugLogging(true); 68 | android.app.FragmentManager.enableDebugLogging(true); 69 | } 70 | } -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/SendToUriActivity.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | import android.view.Menu; 9 | 10 | public class SendToUriActivity extends Activity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | 16 | Intent intent = getIntent(); 17 | final Uri uri = intent.getData(); 18 | if (Constants.IS_LOGGABLE) { 19 | Log.v(getString(R.string.app_name), "Intent received " + uri.toString()); 20 | } 21 | UdpSender udpSender = new UdpSender(); 22 | udpSender.SendTo(this.getApplicationContext(), uri); 23 | finish(); 24 | } 25 | 26 | @Override 27 | public boolean onCreateOptionsMenu(Menu menu) { 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/UdpSender.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Handler; 6 | import android.util.Log; 7 | import android.widget.Toast; 8 | 9 | import java.io.IOException; 10 | import java.net.*; 11 | 12 | import static com.hastarin.android.udpsender.HexHelper.bytesToHex; 13 | import static com.hastarin.android.udpsender.HexHelper.hexStringToBytes; 14 | 15 | public class UdpSender { 16 | final Handler toastHandler = new Handler(); 17 | public void SendTo(final Context context, final Uri uri) { 18 | 19 | if (uri == null) return; 20 | String msg = Uri.decode(uri.getLastPathSegment()); 21 | if(msg == null) return; 22 | byte[] msgBytes = msg.getBytes(); 23 | if (msg.startsWith("\\0x")) { 24 | msg = msg.replace("\\0x", "0x"); 25 | msgBytes = msg.getBytes(); 26 | } else if (msg.startsWith("0x")) { 27 | msg = msg.replace("0x", ""); 28 | if(!msg.matches("[a-fA-F0-9]+")) { 29 | Toast.makeText(context, "ERROR: Invalid hex values", Toast.LENGTH_LONG).show(); 30 | return; 31 | } 32 | msgBytes = hexStringToBytes(msg); 33 | } 34 | 35 | final byte[] buf = msgBytes; 36 | 37 | String appName = context.getString(R.string.app_name); 38 | if(Constants.IS_LOGGABLE) { 39 | Log.d(appName, new String(msgBytes)); 40 | Log.d(appName, "0x" + bytesToHex(msgBytes)); 41 | } 42 | 43 | new Thread(new Runnable() { 44 | public void run() { 45 | try { 46 | InetAddress serverAddress = InetAddress.getByName(uri 47 | .getHost()); 48 | //Log.v(getString(R.string.app_name), serverAddress.getHostAddress()); 49 | DatagramSocket socket = new DatagramSocket(); 50 | if (!socket.getBroadcast()) socket.setBroadcast(true); 51 | DatagramPacket packet = new DatagramPacket(buf, buf.length, 52 | serverAddress, uri.getPort()); 53 | socket.send(packet); 54 | socket.close(); 55 | } catch (final UnknownHostException e) { 56 | toastHandler.post(new Runnable() { 57 | @Override 58 | public void run() { 59 | Toast.makeText(context, e.toString(), 60 | Toast.LENGTH_SHORT).show(); 61 | } 62 | }); 63 | e.printStackTrace(); 64 | } catch (final SocketException e) { 65 | toastHandler.post(new Runnable() { 66 | @Override 67 | public void run() { 68 | Toast.makeText(context, e.toString(), 69 | Toast.LENGTH_SHORT).show(); 70 | } 71 | }); 72 | e.printStackTrace(); 73 | } catch (final IOException e) { 74 | toastHandler.post(new Runnable() { 75 | @Override 76 | public void run() { 77 | Toast.makeText(context, e.toString(), 78 | Toast.LENGTH_SHORT).show(); 79 | } 80 | }); 81 | e.printStackTrace(); 82 | } 83 | } 84 | }).start(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/receiver/FireReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 two forty four a.m. LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * 8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | package com.hastarin.android.udpsender.receiver; 14 | 15 | import android.content.BroadcastReceiver; 16 | import android.content.Context; 17 | import android.content.Intent; 18 | import android.net.Uri; 19 | import android.os.Bundle; 20 | import android.util.Log; 21 | import com.hastarin.android.udpsender.Constants; 22 | import com.hastarin.android.udpsender.UdpSender; 23 | import com.hastarin.android.udpsender.ui.bundle.BundleScrubber; 24 | import com.hastarin.android.udpsender.ui.bundle.PluginBundleManager; 25 | 26 | import java.util.Locale; 27 | 28 | /** 29 | * This is the "fire" BroadcastReceiver for a Locale Plug-in setting. 30 | * 31 | * @see com.twofortyfouram.locale.Intent#ACTION_FIRE_SETTING 32 | * @see com.twofortyfouram.locale.Intent#EXTRA_BUNDLE 33 | */ 34 | public final class FireReceiver extends BroadcastReceiver 35 | { 36 | 37 | /** 38 | * @param context {@inheritDoc}. 39 | * @param intent the incoming {@link com.twofortyfouram.locale.Intent#ACTION_FIRE_SETTING} Intent. This 40 | * should contain the {@link com.twofortyfouram.locale.Intent#EXTRA_BUNDLE} that was saved by 41 | * {@link com.hastarin.android.udpsender.ui.EditActivity} and later broadcast by Locale. 42 | */ 43 | @Override 44 | public void onReceive(final Context context, final Intent intent) 45 | { 46 | /* 47 | * Always be strict on input parameters! A malicious third-party app could send a malformed Intent. 48 | */ 49 | 50 | if (!com.twofortyfouram.locale.Intent.ACTION_FIRE_SETTING.equals(intent.getAction())) 51 | { 52 | if (Constants.IS_LOGGABLE) 53 | { 54 | Log.e(Constants.LOG_TAG, 55 | String.format(Locale.US, "Received unexpected Intent action %s", intent.getAction())); //$NON-NLS-1$ 56 | } 57 | return; 58 | } 59 | 60 | BundleScrubber.scrub(intent); 61 | 62 | final Bundle bundle = intent.getBundleExtra(com.twofortyfouram.locale.Intent.EXTRA_BUNDLE); 63 | BundleScrubber.scrub(bundle); 64 | 65 | if (PluginBundleManager.isBundleValid(bundle)) 66 | { 67 | final String host = 68 | bundle.getString(PluginBundleManager.BUNDLE_EXTRA_STRING_HOST); 69 | String portText = ""; 70 | if (bundle.containsKey(PluginBundleManager.BUNDLE_EXTRA_STRING_PORT)) 71 | { 72 | portText = bundle.getString(PluginBundleManager.BUNDLE_EXTRA_STRING_PORT); 73 | } 74 | else 75 | { 76 | final int port = 77 | bundle.getInt(PluginBundleManager.BUNDLE_EXTRA_INT_PORT); 78 | portText = Integer.toString(port); 79 | } 80 | String dataText = 81 | bundle.getString(PluginBundleManager.BUNDLE_EXTRA_STRING_TEXT); 82 | final String dataHex = 83 | bundle.getString(PluginBundleManager.BUNDLE_EXTRA_STRING_HEX); 84 | String uriString = "udp://" + host + ":" + portText + "/"; 85 | 86 | if (dataHex.length() >= 2) { 87 | uriString += Uri.encode("0x" + dataHex); 88 | } else { 89 | uriString += Uri.encode(dataText); 90 | } 91 | Uri uri = Uri.parse(uriString); 92 | UdpSender udpSender = new UdpSender(); 93 | udpSender.SendTo(context, uri); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/ui/AbstractPluginActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 two forty four a.m. LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * 8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | package com.hastarin.android.udpsender.ui; 14 | 15 | import android.annotation.TargetApi; 16 | import android.app.Activity; 17 | import android.content.pm.PackageManager.NameNotFoundException; 18 | import android.os.Build; 19 | import android.os.Bundle; 20 | import android.util.Log; 21 | import android.view.Menu; 22 | import android.view.MenuItem; 23 | import com.hastarin.android.udpsender.Constants; 24 | import com.hastarin.android.udpsender.R; 25 | import com.twofortyfouram.locale.BreadCrumber; 26 | 27 | /** 28 | * Superclass for plug-in Activities. This class takes care of initializing aspects of the plug-in's UI to 29 | * look more integrated with the plug-in host. 30 | */ 31 | public abstract class AbstractPluginActivity extends Activity 32 | { 33 | /** 34 | * Flag boolean that can only be set to true via the "Don't Save" 35 | * {@link com.twofortyfouram.locale.api.R.id#twofortyfouram_locale_menu_dontsave} menu item in 36 | * {@link #onMenuItemSelected(int, MenuItem)}. 37 | */ 38 | /* 39 | * There is no need to save/restore this field's state. 40 | */ 41 | private boolean mIsCancelled = false; 42 | 43 | @Override 44 | protected void onCreate(final Bundle savedInstanceState) 45 | { 46 | super.onCreate(savedInstanceState); 47 | 48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 49 | { 50 | setupTitleApi11(); 51 | } 52 | else 53 | { 54 | setTitle(BreadCrumber.generateBreadcrumb(getApplicationContext(), getIntent(), 55 | getString(R.string.plugin_name))); 56 | } 57 | } 58 | 59 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 60 | private void setupTitleApi11() 61 | { 62 | CharSequence callingApplicationLabel = null; 63 | try 64 | { 65 | callingApplicationLabel = 66 | getPackageManager().getApplicationLabel(getPackageManager().getApplicationInfo(getCallingPackage(), 67 | 0)); 68 | } 69 | catch (final NameNotFoundException e) 70 | { 71 | if (Constants.IS_LOGGABLE) 72 | { 73 | Log.e(Constants.LOG_TAG, "Calling package couldn't be found", e); //$NON-NLS-1$ 74 | } 75 | } 76 | if (null != callingApplicationLabel) 77 | { 78 | setTitle(callingApplicationLabel); 79 | } 80 | } 81 | 82 | @Override 83 | public boolean onCreateOptionsMenu(final Menu menu) 84 | { 85 | super.onCreateOptionsMenu(menu); 86 | 87 | getMenuInflater().inflate(R.menu.twofortyfouram_locale_help_save_dontsave, menu); 88 | 89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) 90 | { 91 | setupActionBarApi11(); 92 | } 93 | 94 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) 95 | { 96 | setupActionBarApi14(); 97 | } 98 | 99 | return true; 100 | } 101 | 102 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 103 | private void setupActionBarApi11() 104 | { 105 | getActionBar().setSubtitle(BreadCrumber.generateBreadcrumb(getApplicationContext(), getIntent(), 106 | getString(R.string.plugin_name))); 107 | } 108 | 109 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 110 | private void setupActionBarApi14() 111 | { 112 | getActionBar().setDisplayHomeAsUpEnabled(true); 113 | 114 | /* 115 | * Note: There is a small TOCTOU error here, in that the host could be uninstalled right after 116 | * launching the plug-in. That would cause getApplicationIcon() to return the default application 117 | * icon. It won't fail, but it will return an incorrect icon. 118 | * 119 | * In practice, the chances that the host will be uninstalled while the plug-in UI is running are very 120 | * slim. 121 | */ 122 | try 123 | { 124 | getActionBar().setIcon(getPackageManager().getApplicationIcon(getCallingPackage())); 125 | } 126 | catch (final NameNotFoundException e) 127 | { 128 | if (Constants.IS_LOGGABLE) 129 | { 130 | Log.w(Constants.LOG_TAG, "An error occurred loading the host's icon", e); //$NON-NLS-1$ 131 | } 132 | } 133 | } 134 | 135 | @Override 136 | public boolean onMenuItemSelected(final int featureId, final MenuItem item) 137 | { 138 | final int id = item.getItemId(); 139 | 140 | if (android.R.id.home == id) 141 | { 142 | finish(); 143 | return true; 144 | } 145 | else if (R.id.twofortyfouram_locale_menu_dontsave == id) 146 | { 147 | mIsCancelled = true; 148 | finish(); 149 | return true; 150 | } 151 | else if (R.id.twofortyfouram_locale_menu_save == id) 152 | { 153 | finish(); 154 | return true; 155 | } 156 | 157 | return super.onOptionsItemSelected(item); 158 | } 159 | 160 | /** 161 | * During {@link #finish()}, subclasses can call this method to determine whether the Activity was 162 | * canceled. 163 | * 164 | * @return True if the Activity was canceled. False if the Activity was not canceled. 165 | */ 166 | protected boolean isCanceled() 167 | { 168 | return mIsCancelled; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/ui/EditTextHex.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender.ui; 2 | 3 | import android.content.Context; 4 | import android.text.Editable; 5 | import android.text.InputFilter; 6 | import android.text.InputType; 7 | import android.text.TextWatcher; 8 | import android.util.AttributeSet; 9 | import android.widget.EditText; 10 | 11 | public class EditTextHex extends EditText { 12 | 13 | public EditTextHex(Context context) { 14 | super(context); 15 | initialize(); 16 | } 17 | 18 | public EditTextHex(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | initialize(); 21 | } 22 | 23 | public EditTextHex(Context context, AttributeSet attrs, int defStyle) { 24 | super(context, attrs, defStyle); 25 | initialize(); 26 | } 27 | 28 | private void initialize() { 29 | setInputType(InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS|InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS|InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); 30 | setFilters(new InputFilter[] { new InputFilter() { 31 | @Override 32 | public CharSequence filter(CharSequence source, int start, int end, 33 | android.text.Spanned dest, int dstart, int dend) { 34 | if (end > start) { 35 | String destTxt = dest.toString(); 36 | String resultingTxt = destTxt.substring(0, dstart) 37 | + source.subSequence(start, end) 38 | + destTxt.substring(dend); 39 | if (!resultingTxt 40 | .matches("[a-fA-F0-9]+")) { 41 | return ""; 42 | } 43 | } 44 | return null; 45 | } 46 | } }); 47 | 48 | addTextChangedListener(new TextWatcher() { 49 | @Override 50 | public void afterTextChanged(Editable s) { 51 | } 52 | 53 | @Override 54 | public void onTextChanged(CharSequence s, int start, int before, 55 | int count) { 56 | } 57 | 58 | @Override 59 | public void beforeTextChanged(CharSequence s, int start, int count, 60 | int after) { 61 | // Nothing happens here 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/ui/EditTextIPAddress.java: -------------------------------------------------------------------------------- 1 | package com.hastarin.android.udpsender.ui; 2 | 3 | import android.content.Context; 4 | import android.text.Editable; 5 | import android.text.InputFilter; 6 | import android.text.InputType; 7 | import android.text.TextWatcher; 8 | import android.util.AttributeSet; 9 | import android.widget.EditText; 10 | 11 | public class EditTextIPAddress extends EditText { 12 | 13 | public EditTextIPAddress(Context context) { 14 | super(context); 15 | initialize(); 16 | } 17 | 18 | public EditTextIPAddress(Context context, AttributeSet attrs) { 19 | 20 | super(context, attrs); 21 | initialize(); 22 | } 23 | 24 | public EditTextIPAddress(Context context, AttributeSet attrs, int defStyle) { 25 | 26 | super(context, attrs, defStyle); 27 | initialize(); 28 | } 29 | 30 | @Override 31 | public void setInputType(int type) 32 | { 33 | super.setInputType(type); 34 | HandleInputType(type); 35 | } 36 | 37 | private void HandleInputType(int type) 38 | { 39 | switch (type) { 40 | case InputType.TYPE_CLASS_PHONE: 41 | setFilters(new InputFilter[] { new InputFilter() { 42 | @Override 43 | public CharSequence filter(CharSequence source, int start, int end, 44 | android.text.Spanned destination, int dstart, int dend) { 45 | if (end > start) { 46 | String destinationString= destination.toString(); 47 | String resultingText = destinationString.substring(0, dstart) 48 | + source.subSequence(start, end) 49 | + destinationString.substring(dend); 50 | if (!resultingText 51 | .matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) { 52 | return ""; 53 | } else { 54 | String[] splits = resultingText.split("\\."); 55 | for (int i = 0; i < splits.length; i++) { 56 | if (Integer.valueOf(splits[i]) > 255) { 57 | return ""; 58 | } 59 | } 60 | } 61 | } 62 | return null; 63 | } 64 | } }); 65 | break; 66 | default: 67 | setFilters(new InputFilter[]{}); 68 | break; 69 | } 70 | } 71 | 72 | private void initialize() { 73 | 74 | HandleInputType(getInputType()); 75 | 76 | addTextChangedListener(new TextWatcher() { 77 | boolean deleting = false; 78 | int lastCount = 0; 79 | 80 | @Override 81 | public void afterTextChanged(Editable s) { 82 | boolean check = getInputType() == InputType.TYPE_CLASS_PHONE; 83 | if (check && !deleting) { 84 | String working = s.toString(); 85 | String[] split = working.split("\\."); 86 | String string = split[split.length - 1]; 87 | if (string.length() == 3 88 | || string.equalsIgnoreCase("0") 89 | || (string.length() == 2 && Integer.parseInt(string) > 25)) 90 | { 91 | s.append('.'); 92 | return; 93 | } 94 | } 95 | } 96 | 97 | @Override 98 | public void onTextChanged(CharSequence s, int start, int before, 99 | int count) { 100 | if (lastCount < count) { 101 | deleting = false; 102 | } else { 103 | deleting = true; 104 | } 105 | } 106 | 107 | @Override 108 | public void beforeTextChanged(CharSequence s, int start, int count, 109 | int after) { 110 | // Nothing happens here 111 | } 112 | }); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /UDP Sender/src/main/java/com/hastarin/android/udpsender/ui/bundle/BundleScrubber.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 two forty four a.m. LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in 5 | * compliance with the License. You may obtain a copy of the License at 6 | * 7 | * 8 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and limitations under the License. 11 | */ 12 | 13 | package com.hastarin.android.udpsender.ui.bundle; 14 | 15 | import android.content.Intent; 16 | import android.os.Bundle; 17 | 18 | /** 19 | * Helper class to scrub Bundles of invalid extras. This is a workaround for an Android bug: 20 | * . 21 | */ 22 | public final class BundleScrubber 23 | { 24 | 25 | /** 26 | * Scrubs Intents for private serializable subclasses in the Intent extras. If the Intent's extras contain 27 | * a private serializable subclass, the Bundle is cleared. The Bundle will not be set to null. If the 28 | * Bundle is null, has no extras, or the extras do not contain a private serializable subclass, the Bundle 29 | * is not mutated. 30 | * 31 | * @param intent {@code Intent} to scrub. This parameter may be mutated if scrubbing is necessary. This 32 | * parameter may be null. 33 | * @return true if the Intent was scrubbed, false if the Intent was not modified. 34 | */ 35 | public static boolean scrub(final Intent intent) 36 | { 37 | if (null == intent) 38 | { 39 | return false; 40 | } 41 | 42 | return scrub(intent.getExtras()); 43 | } 44 | 45 | /** 46 | * Scrubs Bundles for private serializable subclasses in the extras. If the Bundle's extras contain a 47 | * private serializable subclass, the Bundle is cleared. If the Bundle is null, has no extras, or the 48 | * extras do not contain a private serializable subclass, the Bundle is not mutated. 49 | * 50 | * @param bundle {@code Bundle} to scrub. This parameter may be mutated if scrubbing is necessary. This 51 | * parameter may be null. 52 | * @return true if the Bundle was scrubbed, false if the Bundle was not modified. 53 | */ 54 | public static boolean scrub(final Bundle bundle) 55 | { 56 | if (null == bundle) 57 | { 58 | return false; 59 | } 60 | 61 | /* 62 | * Note: This is a hack to work around a private serializable classloader attack 63 | */ 64 | try 65 | { 66 | // if a private serializable exists, this will throw an exception 67 | bundle.containsKey(null); 68 | } 69 | catch (final Exception e) 70 | { 71 | bundle.clear(); 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | } -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-hdpi/ic_locale_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-hdpi/ic_locale_plugin.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-ldpi/ic_local_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-ldpi/ic_local_plugin.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-mdpi/ic_local_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-mdpi/ic_local_plugin.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-xhdpi/ic_local_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-xhdpi/ic_local_plugin.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hastarin/android-udpsender/5bb2d45edddf95c6f89d4fda9fd07c1f3f80dd56/UDP Sender/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /UDP Sender/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 25 | 26 | 37 | 38 | 48 | 49 | 58 | 59 |