├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pddstudio │ │ └── tinyifttt │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── pddstudio │ │ │ └── tinyifttt │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── adapter │ │ │ └── TinyActionItem.java │ │ │ ├── connection │ │ │ ├── ConnectionInfo.java │ │ │ ├── SendActionRequest.java │ │ │ └── ServerConnection.java │ │ │ └── utils │ │ │ └── Dialog.java │ └── res │ │ ├── drawable │ │ └── logo.png │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ └── item_tiny_action.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-v19 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── pddstudio │ └── tinyifttt │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme_logo.png ├── settings.gradle ├── test-tinyIFTTT.sh ├── tinyIFTTT-android-app-sample.png ├── tinyifttt-model ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── pddstudio │ └── tinyifttt │ └── models │ ├── TinyAction.java │ └── TinyActionReceivedListener.java └── tinyifttt-server ├── .gitignore ├── build.gradle ├── src └── main │ └── java │ └── com │ └── pddstudio │ └── tinyifttt │ └── server │ ├── ActionExecutor.java │ ├── ServerRunner.java │ ├── TinyIFTTT.java │ ├── async │ └── ClientConnectionListener.java │ └── utils │ ├── Log.java │ └── Logger.java └── tiny-sample.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | tinyIFTT -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.7 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # tinyIFTTT - If This Then That 6 | A tiny, lightweight, easy to use and customize IFTTT (if this, then that) server and (Android) client. 7 | 8 | The ideal thing for hackers and home-automation freaks. 9 | 10 | ### What is IFTTT? 11 | 12 | IFTTT is a mechanism that allows users to create chains of simple conditional statements, called "recipes", which are triggered based on changes to other user interactions. IFTTT is an abbreviation of "If This Then That". 13 | 14 | *Original Source: [Wikipedia](https://en.wikipedia.org/wiki/IFTTT) | Some changes where made in the definition above* 15 | 16 | ### What does tinyIFTTT do? 17 | 18 | Basically, tinyIFTTT can do **everything** that you could do on your personal computer or server, like for example upgrading your server's linux packages, shutting down or rebooting your server, triggering self-written scripts on your RaspberryPi and much more. 19 | 20 | tinyIFTTT aims to let you easily configure different kind of receipes/actions which you can execute remotely from your smartphone using the tinyIFTTT Android-App. 21 | 22 | This repository contains the source and pre-packaged binaries for **both** components, the **Android-App** and the **tinyIFTTT Server Module** 23 | 24 | ### Why does tinyIFTTT exist? 25 | 26 | One day, I was hacking on my RaspberryPi and wrote some scripts to automatically make backups on my NAS. 27 | 28 | As soon as I was done and got everything set up as I needed it, I got sick of using a SSH Client on my smartphone for typing all the commands I need for my script to be executed. 29 | 30 | As an Android Developer I thought it would be nice to develop a kind of IFTTT mechanism which allows me to control whatever I want to (as long as I configured it) directly from my smartphone without using any kind of SSH session. 31 | 32 | All I wanted to do was to press a button and the action should be executed. 33 | 34 | This was the beginning of tinyIFTTT. 35 | 36 | ### What's in the package 37 | tinyIFTTT comes with a plug-and-play server component that you can immediately run on your machine. 38 | 39 | All you need is a machine with a JVM installed, nothing else is required to get everything up and running (well, except of your receipes of course). 40 | 41 | ### Getting Started with tinyIFTTT 42 | 43 | This section gets more into detail how you can get the tinyIFTTT Server Module and the tinyIFTTT Android-App up and running. 44 | 45 | #### Getting the Server Module up and running 46 | 47 | There are two ways to get started with the tinyIFTTT Server Module. 48 | 49 | Either download the [prepackaged binary from the release page](https://github.com/PDDStudio/tinyIFTTT/releases) or build it yourself using gradle: 50 | 51 | ``` 52 | ./gradlew tinyIFTTTserver 53 | ``` 54 | 55 | All you have to do now is running the `.jar` file using the desired port as parameter (1337 by default) and the location to your config file which includes all specified receipes. 56 | 57 | For example: 58 | 59 | ```java 60 | java -jar tinyIFTTT-server-full-1.0.jar --port 1337 --config ./tinyifttt-config.json 61 | ``` 62 | 63 | If you need help: 64 | 65 | ```java 66 | java -jar tinyIFTTT-server-full-1.0.jar --help 67 | ``` 68 | 69 | #### Getting the Android App up and running 70 | 71 | There are to ways to get the Android App up and running. 72 | Either download the [prepackaged apk from the release page](https://github.com/PDDStudio/tinyIFTTT/releases) or import the project into AndroidStudio and build it yourself. 73 | 74 | Simply install the application and you're ready to go. 75 | 76 | ### Configuring tinyIFTTT 77 | To configure tinyIFTTT all you have to do is writing a simple file which contains your receipes using `json`-format. 78 | For a life preview check out [the sample json config file here](https://github.com/PDDStudio/tinyIFTTT/blob/master/tinyifttt-server/tiny-sample.json). 79 | The config file holds an array of your receipes. This can contain as much receipes as you want to configure. 80 | Every receipe is build using the same mechanism: 81 | 82 | ```json 83 | { 84 | "actionIdentifier" : 1, 85 | "actionTitle" : "Home PC Shutdown", 86 | "actionDescription" : "Power off my machine upstairs.", 87 | "actionExec" : [ "shutdown", "-h", "now" ] 88 | } 89 | ``` 90 | 91 | Explanation: 92 | 93 | - **actionIdentifier**: This identifier is used to validate the action you want to be executed. **Make sure this number is unique and not used twice.** 94 | - **actionTitle**: Here you can specify the title you want to be displayed for this receipe on your smartphone. 95 | - **actionDescription**: Here you can specify the description you want to be displayed for this receipe on your smartphone. 96 | - **actionExec**: An array of String which represents the commands you would execute in your terminal / the process you want to be started with (optional) parameters. 97 | 98 | ### Usage 99 | 100 | As soon as you have both modules up and running you can start connecting to your server using the tinyIFTTT Android-App. 101 | 102 | Once your tinyIFTTT Server is running you should see something similar to this in your terminal: 103 | 104 | ``` 105 | [03-30-2016|18:26:57][TinyIFTTT::/D] tinyIFTTT Server configuration loaded. 106 | [03-30-2016|18:26:57][TinyIFTTT::/D] Starting tinyIFTTT Server... 107 | ``` 108 | 109 | This means you're good to go and can connect to your tinyIFTTT Server via the Android-App. 110 | 111 | Enter the IP-Address of the machine you're running the tinyIFTTT Server Module on and the Port you specified when launching the tinyIFTTT Server. 112 | 113 | If desired you can save the server connection information in case you don't want to re-enter it all the time. 114 | 115 | Simply click the `Connect` Button and you'll see a list of your specified receipes. 116 | 117 | Clicking on the specified receipe will execute the desired action. 118 | 119 | More samples and demonstrations can be found in the [Samples & Demonstration](https://github.com/PDDStudio/tinyIFTTT#samples--demonstration) Section. 120 | 121 | ### Samples & Demonstration 122 | 123 | Assuming we have the following config file filled with the receipes we want to use: 124 | 125 | ```json 126 | [ 127 | { 128 | "actionIdentifier" : 1, 129 | "actionTitle" : "Home PC Shutdown", 130 | "actionDescription" : "Power off my machine upstairs.", 131 | "actionExec" : [ "shutdown", "-h", "now" ] 132 | }, 133 | { 134 | "actionIdentifier" : 2, 135 | "actionTitle" : "Home PC Reboot", 136 | "actionDescription" : "Reboot my machine upstairs.", 137 | "actionExec" : [ "reboot" ] 138 | }, 139 | { 140 | "actionIdentifier" : 3, 141 | "actionTitle" : "Raspberry Pi Update", 142 | "actionDescription" : "Update the packages on my Pi.", 143 | "actionExec" : [ "apt-get", "update" ] 144 | }, 145 | { 146 | "actionIdentifier" : 4, 147 | "actionTitle" : "Raspberry Pi XBMC", 148 | "actionDescription" : "Start XBMC on my Pi.", 149 | "actionExec" : [ "xbmc" ] 150 | }, 151 | { 152 | "actionIdentifier" : 5, 153 | "actionTitle" : "Run Backup Script", 154 | "actionDescription" : "Run the backup script to save my data stored on my NAS.", 155 | "actionExec" : [ "sh", "/opt/backup/backup_all.sh" ] 156 | }, 157 | { 158 | "actionIdentifier" : 6, 159 | "actionTitle" : "Kitchen Light", 160 | "actionDescription" : "Switch on the Light in the kitchen.", 161 | "actionExec" : [ "python", "/opt/mods/switch_light.py" ] 162 | }, 163 | { 164 | "actionIdentifier" : 7, 165 | "actionTitle" : "Android TV", 166 | "actionDescription" : "Power on/off my AndroidTV.", 167 | "actionExec" : [ "sh", "/opt/mods/tv_power.sh" ] 168 | }, 169 | { 170 | "actionIdentifier" : 8, 171 | "actionTitle" : "Ping Google.com", 172 | "actionDescription" : "Ping Google's Website to make sure the internet connection is available.", 173 | "actionExec" : [ "ping", "google.com" ] 174 | } 175 | ] 176 | ``` 177 | 178 | This is how it would look like inside the tinyIFTTT Android-App 179 | 180 | ![](https://raw.githubusercontent.com/PDDStudio/tinyIFTTT/master/tinyIFTTT-android-app-sample.png?token=AKb0QK2BJdoWGpn7J9B58upx6b5XnwTEks5XBTzbwA%3D%3D) 181 | 182 | 183 | In case you want to see a live demonstration, feel free to have a look at the [YouTube Video](https://www.youtube.com/watch?v=HpCYYrplYZ8) I recorded. 184 | 185 | 186 | ### About & Contact 187 | - In case you've a question feel free to hit me up via E-Mail (patrick.pddstudio/gmail) 188 | 189 | ### Special Thanks & Contributors 190 | - A Special Thanks goes to [Manuel Labrador Vianthi (MLV)](https://plus.google.com/+ManuelLabradorVianthi/) for creating the tinyIFTTT Logo for the Android-App 191 | 192 | ### License 193 | Copyright 2016 Patrick J 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "24.0.0 rc1" 6 | 7 | defaultConfig { 8 | applicationId "com.pddstudio.tinyifttt" 9 | minSdkVersion 16 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.2.1' 26 | compile 'com.android.support:design:23.2.1' 27 | compile 'com.android.support:cardview-v7:23.2.1' 28 | compile 'com.google.code.gson:gson:2.6.2' 29 | compile('com.mikepenz:fastadapter:1.3.0@aar') { 30 | transitive = true 31 | } 32 | compile project(':tinyifttt-model') 33 | } 34 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/pddstudio/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pddstudio/tinyifttt/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/tinyIFTTT/62ad6c11f885cac18ecae354c95414bd7d7fad63/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt; 2 | 3 | import android.content.Intent; 4 | import android.content.SharedPreferences; 5 | import android.os.Bundle; 6 | import android.preference.PreferenceManager; 7 | import android.support.design.widget.TextInputLayout; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.widget.CheckBox; 12 | import android.widget.EditText; 13 | import android.widget.Toast; 14 | 15 | import com.pddstudio.tinyifttt.connection.ConnectionInfo; 16 | 17 | public class LoginActivity extends AppCompatActivity implements View.OnClickListener { 18 | 19 | private static final String SAVED_HOST_IP = "remoteHost"; 20 | private static final String SAVED_HOST_PORT = "remotePort"; 21 | public static final int SESSION_END_CODE = 42; 22 | 23 | private TextInputLayout mIpInputLayout; 24 | private TextInputLayout mPortInputLayout; 25 | private EditText mIpAddressEditText; 26 | private EditText mPortEditText; 27 | private Button mConnectButton; 28 | private CheckBox mSaveLoginsCheckBox; 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_login); 34 | //assign the views 35 | mIpInputLayout = (TextInputLayout) findViewById(R.id.ipInputLayout); 36 | if(mIpInputLayout != null) mIpInputLayout.setErrorEnabled(true); 37 | 38 | mPortInputLayout = (TextInputLayout) findViewById(R.id.portInputLayout); 39 | if(mPortInputLayout != null) mPortInputLayout.setErrorEnabled(true); 40 | 41 | mIpAddressEditText = (EditText) findViewById(R.id.ipAddressEditText); 42 | mPortEditText = (EditText) findViewById(R.id.portEditText); 43 | mSaveLoginsCheckBox = (CheckBox) findViewById(R.id.saveLoginCheckbox); 44 | 45 | mConnectButton = (Button) findViewById(R.id.connectButton); 46 | if(mConnectButton != null) mConnectButton.setOnClickListener(this); 47 | 48 | //load the configuration - if saved 49 | loadConnectionInfo(); 50 | } 51 | 52 | @Override 53 | public void onClick(View v) { 54 | //get the ip and port and set the connection info 55 | if(mIpAddressEditText.getText().toString().isEmpty()) mIpInputLayout.setError(getString(R.string.error_ip_missing)); 56 | else if(mPortEditText.getText().toString().isEmpty()) mPortInputLayout.setError(getString(R.string.error_port_missing)); 57 | else { 58 | try { 59 | int serverPort = Integer.parseInt(mPortEditText.getText().toString()); 60 | if(serverPort < 0) throw new NumberFormatException("Can't be lower than 0"); 61 | String serverIp = mIpAddressEditText.getText().toString(); 62 | 63 | if(mSaveLoginsCheckBox.isChecked()) saveConnectionInfo(serverIp, serverPort); 64 | ConnectionInfo.setConnectionData(serverIp, serverPort); 65 | Intent data = new Intent(this, MainActivity.class); 66 | startActivityForResult(data, SESSION_END_CODE); 67 | } catch (NumberFormatException nnf) { 68 | mPortInputLayout.setError(getString(R.string.error_port_missing)); 69 | } 70 | } 71 | } 72 | 73 | @Override 74 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 75 | // Check which request we're responding to 76 | if (requestCode == SESSION_END_CODE) { 77 | // Make sure the request was successful 78 | if (resultCode == RESULT_OK) { 79 | //FIXME: let the dialog not crash the application due to onSaveInstance 80 | //new Dialog().setContext(this) 81 | // .show(getSupportFragmentManager(), R.string.dialog_connection_closed_title, R.string.dialog_connection_closed_content); 82 | Toast.makeText(this, R.string.dialog_connection_closed_content, Toast.LENGTH_SHORT).show(); 83 | } 84 | } 85 | } 86 | 87 | private void loadConnectionInfo() { 88 | //load the saved date (if any) 89 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 90 | String hostIp = sharedPreferences.getString(SAVED_HOST_IP, null); 91 | if(hostIp != null) mIpAddressEditText.setText(hostIp); 92 | int hostPort = sharedPreferences.getInt(SAVED_HOST_PORT, -1); 93 | if(hostPort != -1) mPortEditText.setText(""+hostPort); 94 | } 95 | 96 | private void saveConnectionInfo(String hostName, int hostPort) { 97 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 98 | sharedPreferences.edit().putString(SAVED_HOST_IP, hostName).putInt(SAVED_HOST_PORT, hostPort).apply(); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt; 2 | 3 | import android.app.Activity; 4 | import android.content.DialogInterface; 5 | import android.os.AsyncTask; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.LinearLayoutManager; 10 | import android.support.v7.widget.RecyclerView; 11 | import android.support.v7.widget.Toolbar; 12 | import android.util.Log; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.Toast; 16 | 17 | import com.mikepenz.fastadapter.FastAdapter; 18 | import com.mikepenz.fastadapter.IAdapter; 19 | import com.mikepenz.fastadapter.IItem; 20 | import com.mikepenz.fastadapter.adapters.FastItemAdapter; 21 | import com.pddstudio.tinyifttt.adapter.TinyActionItem; 22 | import com.pddstudio.tinyifttt.connection.ConnectionInfo; 23 | import com.pddstudio.tinyifttt.connection.SendActionRequest; 24 | import com.pddstudio.tinyifttt.connection.ServerConnection; 25 | import com.pddstudio.tinyifttt.models.TinyAction; 26 | import com.pddstudio.tinyifttt.utils.Dialog; 27 | 28 | public class MainActivity extends AppCompatActivity implements ServerConnection.ConnectionCallback, FastAdapter.OnClickListener, SendActionRequest.Callback { 29 | 30 | private Toolbar mToolbar; 31 | 32 | private RecyclerView recyclerView; 33 | private RecyclerView.LayoutManager layoutManager; 34 | private FastItemAdapter fastAdapter; 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_main); 40 | //assign the views 41 | mToolbar = (Toolbar) findViewById(R.id.toolbar); 42 | setSupportActionBar(mToolbar); 43 | if(getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); 44 | 45 | //execute the request 46 | ServerConnection serverConnection = new ServerConnection(ConnectionInfo.getConnectionInfo().getmRemoteHost(), ConnectionInfo.getConnectionInfo().getmRemotePort(), this); 47 | serverConnection.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 48 | 49 | recyclerView = (RecyclerView) findViewById(R.id.itemRecyclerView); 50 | layoutManager = new LinearLayoutManager(this); 51 | fastAdapter = new FastItemAdapter<>(); 52 | fastAdapter.withOnClickListener(this); 53 | fastAdapter.withSelectable(false); 54 | recyclerView.setHasFixedSize(true); 55 | recyclerView.setLayoutManager(layoutManager); 56 | recyclerView.setAdapter(fastAdapter); 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | switch (item.getItemId()) { 62 | // Respond to the action bar's Up/Home button 63 | case android.R.id.home: 64 | onBackPressed(); 65 | return true; 66 | } 67 | return super.onOptionsItemSelected(item); 68 | } 69 | 70 | @Override 71 | public boolean onClick(View v, IAdapter adapter, IItem item, int position) { 72 | String serverIp = ConnectionInfo.getConnectionInfo().getmRemoteHost(); 73 | int serverPort = ConnectionInfo.getConnectionInfo().getmRemotePort(); 74 | new SendActionRequest(serverIp, serverPort, fastAdapter.getAdapterItem(position).getTinyAction()).sendRequest(this); 75 | return true; 76 | } 77 | 78 | @Override 79 | public void onActionSend(boolean success) { 80 | Log.d("MainActivity", "Action Send : " + success); 81 | if(success) showToast(getString(R.string.toast_action_send_success)); 82 | else showToast(getString(R.string.toast_action_send_fail)); 83 | } 84 | 85 | @Override 86 | public void onConnectingStarted() { 87 | Log.d("MainActivity", "onConnectingStarted()"); 88 | } 89 | 90 | @Override 91 | public void onConnectingFailed(@Nullable Throwable throwable) { 92 | Log.d("MainActivity", "onConnectionFailed()"); 93 | if(throwable != null) throwable.printStackTrace(); 94 | new Dialog().setContext(this) 95 | .setOnClickListener(new DialogInterface.OnClickListener() { 96 | @Override 97 | public void onClick(DialogInterface dialog, int which) { 98 | MainActivity.this.finish(); 99 | } 100 | }) 101 | .show(getSupportFragmentManager(), getString(R.string.dialog_connection_failed_title), getString(R.string.dialog_connection_failed_content, throwable != null ? throwable.getMessage() : "")); 102 | } 103 | 104 | @Override 105 | public void onConnectingFinished() { 106 | //set the result code and finish the activity 107 | Log.d("MainActivity", "onConnectionFinished() called"); 108 | setResult(Activity.RESULT_OK); 109 | finish(); 110 | } 111 | 112 | @Override 113 | public void onTinyActionFound(TinyAction tinyAction) { 114 | Log.d("MainActivity", "TinyAction received: " + tinyAction.getActionIdentifier() + " Title: " + tinyAction.getActionTitle() + " Description: " + tinyAction.getActionDescription()); 115 | fastAdapter.add(new TinyActionItem(tinyAction)); 116 | } 117 | 118 | private void showToast(String message) { 119 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/adapter/TinyActionItem.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import com.mikepenz.fastadapter.items.AbstractItem; 8 | import com.pddstudio.tinyifttt.R; 9 | import com.pddstudio.tinyifttt.models.TinyAction; 10 | 11 | /** 12 | * This Class was created by Patrick J 13 | * on 29.03.16. For more Details and Licensing 14 | * have a look at the README.md 15 | */ 16 | public class TinyActionItem extends AbstractItem { 17 | 18 | private final TinyAction mTinyAction; 19 | 20 | public TinyActionItem(TinyAction tinyAction) { 21 | this.mTinyAction = tinyAction; 22 | } 23 | 24 | public TinyAction getTinyAction() { 25 | return mTinyAction; 26 | } 27 | 28 | @Override 29 | public int getType() { 30 | return R.id.tiny_action_item; 31 | } 32 | 33 | @Override 34 | public int getLayoutRes() { 35 | return R.layout.item_tiny_action; 36 | } 37 | 38 | @Override 39 | public void bindView(ViewHolder viewHolder) { 40 | super.bindView(viewHolder); 41 | viewHolder.actionTitle.setText(mTinyAction.getActionTitle()); 42 | viewHolder.actionDescription.setText(mTinyAction.getActionDescription()); 43 | } 44 | 45 | protected static class ViewHolder extends RecyclerView.ViewHolder { 46 | 47 | TextView actionTitle; 48 | TextView actionDescription; 49 | 50 | public ViewHolder(View itemView) { 51 | super(itemView); 52 | this.actionTitle = (TextView) itemView.findViewById(R.id.actionName); 53 | this.actionDescription = (TextView) itemView.findViewById(R.id.actionDescription); 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/connection/ConnectionInfo.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt.connection; 2 | 3 | /** 4 | * This Class was created by Patrick J 5 | * on 30.03.16. For more Details and Licensing 6 | * have a look at the README.md 7 | */ 8 | public class ConnectionInfo { 9 | 10 | private static ConnectionInfo mConnectionInfo; 11 | 12 | private String mRemoteHost; 13 | private int mRemotePort; 14 | 15 | private ConnectionInfo(String remoteHost, int remotePort) { 16 | this.mRemoteHost = remoteHost; 17 | this.mRemotePort = remotePort; 18 | } 19 | 20 | public static ConnectionInfo setConnectionData(String remoteHost, int remotePort) { 21 | mConnectionInfo = new ConnectionInfo(remoteHost, remotePort); 22 | return mConnectionInfo; 23 | } 24 | 25 | public static ConnectionInfo getConnectionInfo() { 26 | return mConnectionInfo; 27 | } 28 | 29 | public String getmRemoteHost() { 30 | return mRemoteHost; 31 | } 32 | 33 | public int getmRemotePort() { 34 | return mRemotePort; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/connection/SendActionRequest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt.connection; 2 | 3 | import android.os.AsyncTask; 4 | import android.support.annotation.Nullable; 5 | import android.util.Log; 6 | 7 | import com.google.gson.Gson; 8 | import com.pddstudio.tinyifttt.models.TinyAction; 9 | 10 | import java.io.BufferedWriter; 11 | import java.io.IOException; 12 | import java.io.OutputStream; 13 | import java.io.OutputStreamWriter; 14 | import java.net.Socket; 15 | 16 | /** 17 | * This Class was created by Patrick J 18 | * on 29.03.16. For more Details and Licensing 19 | * have a look at the README.md 20 | */ 21 | public class SendActionRequest extends AsyncTask { 22 | 23 | public interface Callback { 24 | void onActionSend(boolean success); 25 | } 26 | 27 | private final String mServerHost; 28 | private final int mServerPort; 29 | private final TinyAction mTinyAction; 30 | 31 | private Callback mCallback; 32 | 33 | public SendActionRequest(String serverHost, int serverPort, TinyAction tinyAction) { 34 | this.mServerHost = serverHost; 35 | this.mServerPort = serverPort; 36 | this.mTinyAction = tinyAction; 37 | } 38 | 39 | public void sendRequest(@Nullable Callback callback) { 40 | Log.d("SendActionRequest", "sendRequest() called. Executing job. [" + mServerHost + ":" + mServerPort +"]"); 41 | this.mCallback = callback; 42 | this.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 43 | } 44 | 45 | @Override 46 | protected Void doInBackground(Void... params) { 47 | //get the output stream and send the action we want to be executed 48 | try { 49 | Socket socket = new Socket(mServerHost, mServerPort); 50 | if(!socket.isConnected()) this.cancel(true); 51 | OutputStream outputStream = socket.getOutputStream(); 52 | OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); 53 | BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); 54 | Gson gson = new Gson(); 55 | bufferedWriter.write(gson.toJson(mTinyAction)); 56 | bufferedWriter.newLine(); 57 | bufferedWriter.flush(); 58 | bufferedWriter.close(); 59 | socket.close(); 60 | } catch (IOException io) { 61 | io.printStackTrace(); 62 | this.cancel(true); 63 | } 64 | Log.d("SendActionRequest", "Action Send!"); 65 | return null; 66 | } 67 | 68 | @Override 69 | protected void onCancelled() { 70 | //notify the callback if set 71 | if(mCallback != null) mCallback.onActionSend(false); 72 | } 73 | 74 | @Override 75 | public void onPostExecute(Void v) { 76 | //notify the callback if set 77 | if(mCallback != null) mCallback.onActionSend(true); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/connection/ServerConnection.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt.connection; 2 | 3 | import android.os.AsyncTask; 4 | import android.support.annotation.Nullable; 5 | 6 | import com.google.gson.Gson; 7 | import com.pddstudio.tinyifttt.models.TinyAction; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.Serializable; 14 | import java.net.Socket; 15 | 16 | /** 17 | * This Class was created by Patrick J 18 | * on 29.03.16. For more Details and Licensing 19 | * have a look at the README.md 20 | */ 21 | public class ServerConnection extends AsyncTask implements Serializable { 22 | 23 | public interface ConnectionCallback { 24 | void onConnectingStarted(); 25 | void onConnectingFailed(@Nullable Throwable throwable); 26 | void onConnectingFinished(); 27 | void onTinyActionFound(TinyAction tinyAction); 28 | } 29 | 30 | private final String mRemoteHost; 31 | private final int mRemotePort; 32 | private final ConnectionCallback mConnectionCallback; 33 | 34 | private Throwable mErrorThrowable; 35 | 36 | public ServerConnection(String hostName, int port, ConnectionCallback connectionCallback) { 37 | this.mRemoteHost = hostName; 38 | this.mRemotePort = port; 39 | this.mConnectionCallback = connectionCallback; 40 | } 41 | 42 | @Override 43 | public void onPreExecute() { 44 | mConnectionCallback.onConnectingStarted(); 45 | } 46 | 47 | @Override 48 | protected Void doInBackground(Void... params) { 49 | //get the input stream and receive the available commands 50 | try { 51 | Socket socket = new Socket(mRemoteHost, mRemotePort); 52 | if(!socket.isConnected()) return null; 53 | 54 | InputStream inputStream = socket.getInputStream(); 55 | InputStreamReader inputStreamReader = new InputStreamReader(inputStream); 56 | BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 57 | Gson gson = new Gson(); 58 | String response; 59 | while ((response = bufferedReader.readLine()) != null) { 60 | //parse the incoming string and notify the callback if it's not null 61 | TinyAction tinyAction = gson.fromJson(response, TinyAction.class); 62 | if(tinyAction != null) publishProgress(tinyAction); 63 | } 64 | 65 | bufferedReader.close(); 66 | socket.close(); 67 | 68 | } catch (IOException io) { 69 | this.mErrorThrowable = io; 70 | this.cancel(true); 71 | } 72 | 73 | return null; 74 | } 75 | 76 | @Override 77 | protected void onCancelled() { 78 | //notify the callback that something went wrong 79 | mConnectionCallback.onConnectingFailed(mErrorThrowable); 80 | } 81 | 82 | @Override 83 | protected void onProgressUpdate(TinyAction... values) { 84 | //we can be sure here that the array contains only one item 85 | mConnectionCallback.onTinyActionFound(values[0]); 86 | } 87 | 88 | @Override 89 | public void onPostExecute(Void v) { 90 | //notify the callback that the connection doesn't exist anymore 91 | mConnectionCallback.onConnectingFinished(); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/tinyifttt/utils/Dialog.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.tinyifttt.utils; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.support.annotation.StringRes; 7 | import android.support.v4.app.DialogFragment; 8 | import android.support.v4.app.FragmentManager; 9 | import android.support.v7.app.AlertDialog; 10 | import android.util.Log; 11 | 12 | /** 13 | * This Class was created by Patrick J 14 | * on 30.03.16. For more Details and Licensing 15 | * have a look at the README.md 16 | */ 17 | public class Dialog extends DialogFragment { 18 | 19 | private final String LOG_TAG = getClass().getSimpleName(); 20 | 21 | private Context mContext; 22 | @StringRes private int mDialogTitle; 23 | @StringRes private int mDialogContent; 24 | private DialogInterface.OnClickListener mOnClickListener; 25 | 26 | private String mTitle; 27 | private String mContent; 28 | 29 | public Dialog setContext(Context context) { 30 | this.mContext = context; 31 | return this; 32 | } 33 | 34 | public Dialog setOnClickListener(DialogInterface.OnClickListener onClickListener) { 35 | this.mOnClickListener = onClickListener; 36 | return this; 37 | } 38 | 39 | public void show(FragmentManager fragmentManager, @StringRes int dialogTitle, @StringRes int dialogContent) { 40 | this.mDialogContent = dialogContent; 41 | this.mDialogTitle = dialogTitle; 42 | show(fragmentManager, "DIALOG"); 43 | } 44 | 45 | public void show(FragmentManager fragmentManager, String dialogTitle, String dialogContent) { 46 | this.mTitle = dialogTitle; 47 | this.mContent = dialogContent; 48 | show(fragmentManager, "DIALOG"); 49 | } 50 | 51 | @Override 52 | public android.app.Dialog onCreateDialog(Bundle savedInstance) { 53 | AlertDialog alertDialog; 54 | if(mTitle == null && mContent == null) { 55 | alertDialog = new AlertDialog.Builder(mContext) 56 | .setTitle(mDialogTitle) 57 | .setMessage(mDialogContent) 58 | .setPositiveButton(android.R.string.ok, mOnClickListener == null ? new DialogInterface.OnClickListener() { 59 | @Override 60 | public void onClick(DialogInterface dialog, int which) { 61 | Log.d(LOG_TAG, "onClick() for positive dialog action called"); 62 | } 63 | } : mOnClickListener) 64 | .setCancelable(false) 65 | .create(); 66 | } else { 67 | alertDialog = new AlertDialog.Builder(mContext) 68 | .setTitle(mTitle) 69 | .setMessage(mContent) 70 | .setPositiveButton(android.R.string.ok, mOnClickListener == null ? new DialogInterface.OnClickListener() { 71 | @Override 72 | public void onClick(DialogInterface dialog, int which) { 73 | Log.d(LOG_TAG, "onClick() for positive dialog action called"); 74 | } 75 | } : mOnClickListener) 76 | .setCancelable(false) 77 | .create(); 78 | } 79 | return alertDialog; 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PDDStudio/tinyIFTTT/62ad6c11f885cac18ecae354c95414bd7d7fad63/app/src/main/res/drawable/logo.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 19 | 20 | 25 | 26 | 30 | 31 | 37 | 38 | 39 | 40 | 44 | 45 | 51 | 52 | 53 | 54 | 60 | 61 |