├── README.md ├── arduino └── ir.ino ├── assets ├── 1.gif ├── 1.jpeg ├── 2.jpeg ├── 3.jpeg ├── 4.jpeg ├── 5.jpeg ├── how.png ├── phone_1.gif ├── phone_2.gif └── sch.jpg ├── mobile ├── .gitignore ├── Readme.md ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── mbakgun │ │ │ └── mobile │ │ │ ├── App.kt │ │ │ ├── data │ │ │ ├── IrData.kt │ │ │ └── NearbyMessage.kt │ │ │ ├── di │ │ │ ├── InjectionViewModelProvider.kt │ │ │ ├── components │ │ │ │ └── AppComponent.kt │ │ │ ├── modules │ │ │ │ ├── ActivityInjectorsModule.kt │ │ │ │ ├── AppModule.kt │ │ │ │ └── MainActivityModule.kt │ │ │ └── qualifiers │ │ │ │ └── ViewModelInjection.kt │ │ │ ├── ui │ │ │ ├── IrDataAdapter.kt │ │ │ ├── MainActivity.kt │ │ │ └── MainActivityVM.kt │ │ │ └── util │ │ │ ├── AlertDialog.kt │ │ │ ├── MarginItemDecoration.kt │ │ │ └── NearbyCommunication.kt │ │ └── res │ │ ├── drawable │ │ ├── ic_add.xml │ │ ├── ic_delete.xml │ │ ├── ic_edit.xml │ │ ├── ic_location_disabled.xml │ │ └── ic_sync.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── empty_item.xml │ │ └── ir_item.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 │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── buildSrc │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── Libs.kt │ │ └── Versions.kt ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew └── settings.gradle └── things ├── .gitignore ├── Readme.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── models.aar │ └── tts.aar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── mbakgun │ │ └── things │ │ ├── App.kt │ │ ├── data │ │ ├── IrDao.kt │ │ ├── IrData.kt │ │ ├── IrDatabase.kt │ │ └── NearbyMessage.kt │ │ ├── di │ │ ├── components │ │ │ └── AppComponent.kt │ │ └── modules │ │ │ ├── ActivityInjectorsModule.kt │ │ │ └── AppModule.kt │ │ ├── ui │ │ └── MainActivity.kt │ │ └── util │ │ ├── NearbyCommunication.kt │ │ ├── PocketSphinx.kt │ │ └── SerialCommunication.kt │ └── res │ └── values │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── buildSrc ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── Libs.kt │ └── Versions.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | ## Android Things IR Remote Hacker 2 | 3 | 4 | 5 | 6 | This Android Things project integrates `Nearby Communication` , `Serial Communication` and `Voice Recognition` together to 7 | build a connected IR remote that explores the relationships between surfaces and content. 8 | 9 | This repo contains all the app code that powers IR Remote Controled Android Things powered Raspberry Pi. 10 | 11 | The project is split into three modules: 12 | 13 | - `/things` - the Android Things app 14 | - `/mobile` - the companion mobile app 15 | - `/arduino` - the Arduino code 16 | 17 | 18 | 19 | ### Pre-requisites 20 | 21 | - Android Things compatible board 22 | - Android Studio 3.2+ 23 | - [Arduino](https://www.arduino.cc/) and the following individual components: 24 | - 1 IR Receiver 25 | - 1 IR Transmitter 26 | - Jumper wires 27 | - (optional) 1 MI-305 - USB Microphone 28 | 29 |

30 | 31 | 32 |


33 | 34 | ## How it works 35 | 36 |

37 | 38 |


39 | 40 | The IR Remote Hacker imagines that you can control all remote controled electronic devices by voice and also with a Mobile Application. The Companion Mobile application allows you to configure the embedded software. Capture and record the Signal with the mobile app. You can now hack as many remote controls as you want. Controlling IR Remote signals over network is planned for next releases. 41 | 42 |

43 | 44 |


45 | 46 | Android Things device and its components is set-up and controlled using the companion app for Android. They communicate using *Nearby Connections*, a protocol developed by Google to facilitate local peer-to-peer communication with nearby devices. 47 | 48 | This application is built around the Voice controlled Raspberry Pi that capable of talking with the Arduino (which can process the data of IR Remote sensors) and control over the companion mobile application. Once your Android phone and Android Things are connected, you can take control of all the remote controls around you. In fact, the whole story consists of user interfaces that control the ability to capture and repeat infrared signals. 49 | 50 | While accomplishing this creative idea, many features has been developed to show different connection methods and technologies with other IOT devices. Both the Android Things code and the companion app are written in Kotlin using Jetpack components and industrial best practises , which has been a joy to work with. 51 | 52 | ## Technical overview 53 | 54 | 55 | 56 | There are two main components to the IR Remote Hacker software - the ‘Things’ app (`/things`), which runs on Android Things on a Raspberry Pi, and the Companion app (`/mobile`) which runs on an Android phone. 57 | 58 | The hardware is built as an voice controlled remote device, with an Arduino Uno, a Raspberry Pi, a USB Microphone, an IR Receiver & Transmitter and a few off-the-shelf wires and connectors. 59 | 60 | ## Schematics 61 | 62 | 63 | 64 | - If you have the Arduino, just plug it onto your Raspberry Pi 3. 65 | 66 | ### Next steps 67 | - Remote Control : Over Network communication 68 | 69 | ### Found this project lovely :heart: 70 | * Support by clicking the :star: button on the upper right of this page. 71 | * Contribute and Make Android Things Great Again :v: 72 | 73 | ### Contact me 74 | - [Twitter](https://twitter.com/mbakguns) 75 | - [LinkedIn](https://www.linkedin.com/in/mbakgun/) 76 | - [Medium](https://medium.com/@mbakgun) 77 | -------------------------------------------------------------------------------- /arduino/ir.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int RECV_PIN = 2; 4 | IRrecv irrecv(RECV_PIN); 5 | IRsend irsend; 6 | 7 | decode_results results; 8 | 9 | long hstol(String recv) { 10 | char c[recv.length() + 1]; 11 | recv.toCharArray(c, recv.length() + 1); 12 | return strtol(c, NULL, 16); 13 | } 14 | 15 | void readIr(String str) { 16 | if (irrecv.decode(&results)) { 17 | str.trim(); 18 | Serial.print("saved:" + str + "-"); 19 | Serial.println(results.value, HEX); 20 | irrecv.resume(); 21 | } else { 22 | Serial.println("No Signal - No capture signal found"); 23 | } 24 | } 25 | 26 | void sendIr(String str) { 27 | irsend.sendNEC(hstol(str), 32); 28 | irrecv.enableIRIn(); 29 | Serial.println("Command Sent"); 30 | delay(100); 31 | } 32 | 33 | void setup() 34 | { 35 | Serial.begin(9600); 36 | irrecv.enableIRIn(); 37 | } 38 | 39 | void loop() { 40 | if (Serial.available() > 0) { 41 | String incoming = Serial.readString(); 42 | if (incoming.startsWith("read:")) { 43 | readIr(incoming.substring(5)); // read: 44 | } else if (incoming.startsWith("send:")) { 45 | sendIr(incoming.substring(5)); // send: 46 | } 47 | Serial.flush(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /assets/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/1.gif -------------------------------------------------------------------------------- /assets/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/1.jpeg -------------------------------------------------------------------------------- /assets/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/2.jpeg -------------------------------------------------------------------------------- /assets/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/3.jpeg -------------------------------------------------------------------------------- /assets/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/4.jpeg -------------------------------------------------------------------------------- /assets/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/5.jpeg -------------------------------------------------------------------------------- /assets/how.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/how.png -------------------------------------------------------------------------------- /assets/phone_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/phone_1.gif -------------------------------------------------------------------------------- /assets/phone_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/phone_2.gif -------------------------------------------------------------------------------- /assets/sch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/assets/sch.jpg -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /mobile/Readme.md: -------------------------------------------------------------------------------- 1 | ## Companion Android Application 2 | 3 | Mobile application is easy to use with simple steps. 4 | 5 | - Power on Android Things Device with application 6 | - Install Application to your phone then run 7 | - Give permission to use Nearby Api 8 | - Wait for the connection between devices 9 | 10 |

11 | 12 | - Press Float Action Button (+) to capture a new IR Signal 13 | - Push any button to send Remote(Radio)-Control signal to Android Things 14 | - In dialog give a name to recently recorded signal 15 | - In order to send IR signal select list item 16 | 17 | 18 | -------------------------------------------------------------------------------- /mobile/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mobile/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 28 8 | defaultConfig { 9 | applicationId "com.mbakgun.mobile" 10 | minSdkVersion 16 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | dataBinding { 22 | enabled = true 23 | } 24 | } 25 | 26 | dependencies { 27 | //android 28 | implementation Libs.appcompat 29 | implementation Libs.constraintlayout 30 | api Libs.play_services_nearby 31 | implementation Libs.lifecycle_extensions 32 | implementation Libs.material 33 | implementation Libs.kotlin_stdlib_jdk7 34 | 35 | //dagger 36 | kapt Libs.dagger_compiler 37 | kapt Libs.dagger_android_processor 38 | implementation Libs.dagger_android_support 39 | 40 | //Gson 41 | implementation Libs.gson 42 | } 43 | -------------------------------------------------------------------------------- /mobile/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /mobile/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/App.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile 2 | 3 | import android.app.Application 4 | import com.mbakgun.mobile.di.components.DaggerAppComponent 5 | import dagger.android.AndroidInjector 6 | import dagger.android.DispatchingAndroidInjector 7 | import dagger.android.HasAndroidInjector 8 | import javax.inject.Inject 9 | 10 | /** 11 | * Created by burakakgun on 8.06.2019. 12 | */ 13 | class App : Application(), HasAndroidInjector { 14 | 15 | @Inject 16 | lateinit var androidInjector: DispatchingAndroidInjector 17 | 18 | override fun onCreate() { 19 | super.onCreate() 20 | 21 | DaggerAppComponent 22 | .builder() 23 | .application(this) 24 | .build() 25 | .inject(this) 26 | } 27 | 28 | override fun androidInjector(): AndroidInjector = androidInjector 29 | } 30 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/data/IrData.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.data 2 | 3 | /** 4 | * Created by burakakgun on 8.06.2019. 5 | */ 6 | data class IrData(var id: Int = 0, var name: String, var hexCode: String) 7 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/data/NearbyMessage.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.data 2 | 3 | /** 4 | * Created by burakakgun on 9.06.2019. 5 | */ 6 | 7 | enum class NearbyType { 8 | UPDATE, DELETE, GET_ALL, MESSAGE 9 | } 10 | 11 | data class NearbyMessage(val nearbyType: NearbyType, val value: String = "") 12 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/InjectionViewModelProvider.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.fragment.app.FragmentActivity 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.ViewModelProvider 7 | import androidx.lifecycle.ViewModelProviders 8 | import javax.inject.Inject 9 | import kotlin.reflect.KClass 10 | 11 | typealias ViewModelInjectionField = dagger.Lazy 12 | 13 | class InjectionViewModelProvider @Inject constructor( 14 | private val lazyViewModel: dagger.Lazy 15 | ) { 16 | 17 | @Suppress("UNCHECKED_CAST") 18 | @SuppressWarnings("unchecked") 19 | private val viewModelFactory = object : ViewModelProvider.Factory { 20 | override fun create(modelClass: Class) = lazyViewModel.get() as T 21 | } 22 | 23 | fun get(activity: ACTIVITY, viewModelClass: KClass) = 24 | ViewModelProviders.of(activity, viewModelFactory).get(viewModelClass.java) 25 | 26 | fun get(fragment: FRAGMENT, viewModelClass: KClass) = 27 | ViewModelProviders.of(fragment, viewModelFactory).get(viewModelClass.java) 28 | } 29 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/components/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di.components 2 | 3 | import com.mbakgun.mobile.App 4 | import com.mbakgun.mobile.di.modules.ActivityInjectorsModule 5 | import com.mbakgun.mobile.di.modules.AppModule 6 | import dagger.BindsInstance 7 | import dagger.Component 8 | import dagger.android.support.AndroidSupportInjectionModule 9 | import javax.inject.Singleton 10 | 11 | /** 12 | * Created by burakakgun on 8.06.2019. 13 | */ 14 | @Singleton 15 | @Component( 16 | modules = [ 17 | AndroidSupportInjectionModule::class, 18 | ActivityInjectorsModule::class, 19 | AppModule::class] 20 | ) 21 | @SuppressWarnings("unchecked") 22 | interface AppComponent { 23 | @Component.Builder 24 | interface Builder { 25 | @BindsInstance 26 | fun application(application: App): Builder 27 | 28 | fun build(): AppComponent 29 | } 30 | 31 | fun inject(app: App) 32 | } 33 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/modules/ActivityInjectorsModule.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di.modules 2 | 3 | import com.mbakgun.mobile.ui.MainActivity 4 | import dagger.Module 5 | import dagger.android.ContributesAndroidInjector 6 | 7 | /** 8 | * Created by burakakgun on 8.06.2019. 9 | */ 10 | @Module 11 | abstract class ActivityInjectorsModule { 12 | 13 | @ContributesAndroidInjector(modules = [MainActivityModule::class]) 14 | abstract fun mainActivityInjector(): MainActivity 15 | } 16 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/modules/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di.modules 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import com.mbakgun.mobile.App 6 | import dagger.Module 7 | import dagger.Provides 8 | import javax.inject.Singleton 9 | 10 | /** 11 | * Created by burakakgun on 8.06.2019. 12 | */ 13 | @Module 14 | class AppModule { 15 | 16 | @Provides 17 | @Singleton 18 | fun provideApplication(app: App): Application = app 19 | 20 | @Provides 21 | @Singleton 22 | fun provideApplicationContext(app: App): Context = app.applicationContext 23 | } 24 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/modules/MainActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di.modules 2 | 3 | import com.mbakgun.mobile.di.InjectionViewModelProvider 4 | import com.mbakgun.mobile.di.qualifiers.ViewModelInjection 5 | import com.mbakgun.mobile.ui.MainActivity 6 | import com.mbakgun.mobile.ui.MainActivityVM 7 | import dagger.Module 8 | import dagger.Provides 9 | 10 | /** 11 | * Created by burakakgun on 8.06.2019. 12 | */ 13 | @Module 14 | class MainActivityModule { 15 | 16 | @Provides 17 | @ViewModelInjection 18 | fun provideMainActivityVM( 19 | activity: MainActivity, 20 | viewModelProvider: InjectionViewModelProvider 21 | ) = viewModelProvider.get(activity, MainActivityVM::class) 22 | } 23 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/di/qualifiers/ViewModelInjection.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.di.qualifiers 2 | 3 | import javax.inject.Qualifier 4 | 5 | @Qualifier 6 | @MustBeDocumented 7 | @Retention(AnnotationRetention.RUNTIME) 8 | @Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION) 9 | annotation class ViewModelInjection 10 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/ui/IrDataAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.ui 2 | 3 | /** 4 | * Created by burakakgun on 9.06.2019. 5 | */ 6 | import android.util.Log 7 | import android.view.LayoutInflater 8 | import android.view.ViewGroup 9 | import androidx.databinding.DataBindingUtil.inflate 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.google.gson.Gson 12 | import com.mbakgun.mobile.R 13 | import com.mbakgun.mobile.data.IrData 14 | import com.mbakgun.mobile.data.NearbyMessage 15 | import com.mbakgun.mobile.data.NearbyType 16 | import com.mbakgun.mobile.databinding.EmptyItemBinding 17 | import com.mbakgun.mobile.databinding.IrItemBinding 18 | import com.mbakgun.mobile.util.showAlertWithTextInputLayout 19 | import javax.inject.Inject 20 | 21 | class IrDataAdapter @Inject constructor(private val vm: MainActivityVM) : 22 | RecyclerView.Adapter() { 23 | 24 | private val irDataList = mutableListOf() 25 | 26 | companion object { 27 | const val EMPTY = 0 28 | const val IR_ITEM = 1 29 | } 30 | 31 | fun updateList(list: List) { 32 | Log.d("IrDataAdapter", "current list: $list") 33 | irDataList.clear() 34 | if (list.isEmpty()) { 35 | irDataList.add(null) 36 | } else { 37 | irDataList.addAll(list) 38 | } 39 | notifyDataSetChanged() 40 | } 41 | 42 | override fun getItemViewType(position: Int): Int = if (irDataList[0] == null) EMPTY else IR_ITEM 43 | 44 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 45 | val inflater = LayoutInflater.from(parent.context) 46 | return when (viewType) { 47 | IR_ITEM -> { 48 | val binding = inflate( 49 | inflater, R.layout.ir_item, parent, false 50 | ) 51 | IrViewHolder(binding) 52 | } 53 | else -> { // EMPTY 54 | val binding = inflate( 55 | inflater, R.layout.empty_item, parent, false 56 | ) 57 | EmptyViewHolder(binding) 58 | } 59 | } 60 | } 61 | 62 | override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 63 | when (getItemViewType(position)) { 64 | EMPTY -> holder as EmptyViewHolder 65 | IR_ITEM -> { 66 | irDataList[position]?.let { 67 | holder as IrViewHolder 68 | holder.bind(it) 69 | } 70 | } 71 | } 72 | } 73 | 74 | override fun getItemCount(): Int { 75 | return irDataList.size 76 | } 77 | 78 | inner class IrViewHolder(private val binding: IrItemBinding) : 79 | RecyclerView.ViewHolder(binding.root) { 80 | fun bind(item: IrData) { 81 | binding.root.setOnClickListener { 82 | vm.send(NearbyMessage(NearbyType.MESSAGE, "send:${item.hexCode}")) 83 | } 84 | binding.imageViewDelete.setOnClickListener { 85 | vm.send(NearbyMessage(NearbyType.DELETE, Gson().toJson(item))) 86 | } 87 | binding.imageViewEdit.setOnClickListener { 88 | showAlertWithTextInputLayout(binding.root.context, vm, item) 89 | } 90 | binding.item = item 91 | binding.executePendingBindings() 92 | } 93 | } 94 | 95 | inner class EmptyViewHolder(binding: EmptyItemBinding) : RecyclerView.ViewHolder(binding.root) 96 | } 97 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.ui 2 | 3 | import android.content.pm.PackageManager 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | import android.view.animation.Animation 8 | import android.view.animation.RotateAnimation 9 | import android.widget.Toast 10 | import androidx.core.app.ActivityCompat 11 | import androidx.core.content.ContextCompat 12 | import androidx.lifecycle.Observer 13 | import com.google.android.material.snackbar.Snackbar 14 | import com.google.gson.Gson 15 | import com.google.gson.reflect.TypeToken 16 | import com.mbakgun.mobile.R 17 | import com.mbakgun.mobile.data.IrData 18 | import com.mbakgun.mobile.data.NearbyMessage 19 | import com.mbakgun.mobile.data.NearbyType.GET_ALL 20 | import com.mbakgun.mobile.data.NearbyType.MESSAGE 21 | import com.mbakgun.mobile.di.ViewModelInjectionField 22 | import com.mbakgun.mobile.di.qualifiers.ViewModelInjection 23 | import com.mbakgun.mobile.util.MarginItemDecoration 24 | import com.mbakgun.mobile.util.showAlertWithTextInputLayout 25 | import dagger.android.support.DaggerAppCompatActivity 26 | import kotlinx.android.synthetic.main.activity_main.* 27 | import javax.inject.Inject 28 | 29 | /** 30 | * Created by burakakgun on 8.06.2019. 31 | */ 32 | class MainActivity : DaggerAppCompatActivity() { 33 | 34 | @Inject 35 | @ViewModelInjection 36 | lateinit var vm: ViewModelInjectionField 37 | private val mainActivityVM: MainActivityVM = vm.get() 38 | 39 | @Inject 40 | lateinit var adapter: IrDataAdapter 41 | 42 | override fun onCreate(savedInstanceState: Bundle?) { 43 | super.onCreate(savedInstanceState) 44 | setContentView(R.layout.activity_main) 45 | mainActivityVM.nearByMessageObserver().observe(this, Observer { message -> 46 | val data = Gson().fromJson(message, NearbyMessage::class.java) 47 | if (data.nearbyType == GET_ALL) { 48 | progressBar.visibility = View.GONE 49 | adapter.updateList( 50 | Gson().fromJson( 51 | data.value, 52 | object : TypeToken>() {}.type 53 | ) 54 | ) 55 | } else if (data.nearbyType == MESSAGE) { 56 | Snackbar.make(root, data.value, Snackbar.LENGTH_LONG).show() 57 | // request new list after all events 58 | mainActivityVM.send(NearbyMessage(GET_ALL)) 59 | } 60 | }) 61 | mainActivityVM.nearByConnectivityObserver().observe(this, Observer { value -> applyFloatActionButton(value) }) 62 | setRecyclerView() 63 | } 64 | 65 | override fun onStart() { 66 | super.onStart() 67 | if (checkPermission()) vm.get().connect() 68 | } 69 | 70 | private fun setRecyclerView() { 71 | recyclerView.setHasFixedSize(true) 72 | recyclerView.adapter = adapter 73 | recyclerView.addItemDecoration(MarginItemDecoration(resources.getDimension(R.dimen.dp_8).toInt())) 74 | } 75 | 76 | private fun applyFloatActionButton(isConnected: Boolean) { 77 | Log.d("MainActivity", "status : $isConnected") 78 | if (checkPermission()) { 79 | if (isConnected) { 80 | floatingActionButton.clearAnimation() 81 | floatingActionButton.setImageResource(R.drawable.ic_add) 82 | floatingActionButton.setOnClickListener { 83 | showAlertWithTextInputLayout(this, vm.get()) 84 | } 85 | vm.get().send(NearbyMessage(GET_ALL)) 86 | recyclerView.visibility = View.VISIBLE 87 | progressBar.visibility = View.VISIBLE 88 | } else { 89 | recyclerView.visibility = View.GONE 90 | floatingActionButton.setImageResource(R.drawable.ic_sync) 91 | val rotate = RotateAnimation( 92 | float_max, 0f, 93 | Animation.RELATIVE_TO_SELF, animation_pivotXValue, 94 | Animation.RELATIVE_TO_SELF, animation_pivotXValue 95 | ) 96 | rotate.duration = animation_duration 97 | rotate.repeatCount = Animation.INFINITE 98 | floatingActionButton.startAnimation(rotate) 99 | vm.get().connect() 100 | floatingActionButton.setOnClickListener { 101 | Toast.makeText(this, "Connecting...", Toast.LENGTH_SHORT).show() 102 | } 103 | } 104 | } else { 105 | floatingActionButton.setImageResource(R.drawable.ic_location_disabled) 106 | floatingActionButton.setOnClickListener { 107 | checkPermission() 108 | } 109 | } 110 | } 111 | 112 | private fun checkPermission(): Boolean { 113 | if (ContextCompat.checkSelfPermission( 114 | this, 115 | android.Manifest.permission.ACCESS_COARSE_LOCATION 116 | ) != PackageManager.PERMISSION_GRANTED 117 | ) { 118 | ActivityCompat.requestPermissions( 119 | this, arrayOf( 120 | android.Manifest.permission.ACCESS_COARSE_LOCATION, 121 | android.Manifest.permission.ACCESS_FINE_LOCATION 122 | ), REQ_CODE 123 | ) 124 | return false 125 | } 126 | return true 127 | } 128 | 129 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 130 | if (requestCode == REQ_CODE && 131 | grantResults.isNotEmpty() && 132 | grantResults[0] == PackageManager.PERMISSION_GRANTED 133 | ) { 134 | applyFloatActionButton(false) 135 | } 136 | } 137 | 138 | companion object { 139 | const val REQ_CODE = 1453 140 | const val float_max = 360f 141 | const val animation_duration: Long = 1250 142 | const val animation_pivotXValue = 0.5f 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/ui/MainActivityVM.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.ui 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | import com.google.gson.Gson 6 | import com.mbakgun.mobile.data.NearbyMessage 7 | import com.mbakgun.mobile.util.NearbyCommunication 8 | import javax.inject.Inject 9 | 10 | /** 11 | * Created by burakakgun on 8.06.2019. 12 | */ 13 | class MainActivityVM @Inject constructor(private val nearbyCommunication: NearbyCommunication) : 14 | ViewModel() { 15 | 16 | fun nearByMessageObserver(): MutableLiveData = nearbyCommunication.remoteMessage 17 | 18 | fun nearByConnectivityObserver(): MutableLiveData = nearbyCommunication.mIsConnected 19 | 20 | fun send(message: NearbyMessage) { 21 | nearbyCommunication.sendMessage(Gson().toJson(message)) 22 | } 23 | 24 | fun connect() = nearbyCommunication.mGoogleApiClient.connect() 25 | } 26 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/util/AlertDialog.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.util 2 | 3 | import android.content.Context 4 | import android.text.TextUtils 5 | import android.widget.EditText 6 | import android.widget.Toast 7 | import androidx.appcompat.app.AlertDialog 8 | import com.google.android.material.textfield.TextInputLayout 9 | import com.google.gson.Gson 10 | import com.mbakgun.mobile.R 11 | import com.mbakgun.mobile.data.IrData 12 | import com.mbakgun.mobile.data.NearbyMessage 13 | import com.mbakgun.mobile.data.NearbyType 14 | import com.mbakgun.mobile.ui.MainActivityVM 15 | 16 | /** 17 | * Created by burakakgun on 9.06.2019. 18 | */ 19 | 20 | fun showAlertWithTextInputLayout(context: Context, vm: MainActivityVM, irData: IrData? = null) { 21 | val textInputLayout = TextInputLayout(context) 22 | textInputLayout.setPadding( 23 | context.resources.getDimensionPixelOffset(R.dimen.dp_19), 24 | 0, 25 | context.resources.getDimensionPixelOffset(R.dimen.dp_19), 26 | 0 27 | ) 28 | val input = EditText(context) 29 | irData?.let { it -> 30 | input.setText(it.name) 31 | } 32 | textInputLayout.hint = "Remote Device Name" 33 | textInputLayout.addView(input) 34 | val alert = AlertDialog.Builder(context) 35 | .setTitle("Capture Infrared") 36 | .setView(textInputLayout) 37 | .setMessage("Please push any IR signal then enter your device name") 38 | .setPositiveButton("Send") { _, _ -> 39 | val text = input.text 40 | if (TextUtils.isEmpty(text).not()) { 41 | irData?.let { 42 | vm.send(NearbyMessage(NearbyType.UPDATE, Gson().toJson(it.copy(name = text.toString())))) 43 | } ?: run { 44 | vm.send(NearbyMessage(NearbyType.MESSAGE, "read:$text")) 45 | } 46 | } else { 47 | Toast.makeText(context, "Device Name is required", Toast.LENGTH_SHORT).show() 48 | } 49 | } 50 | .setNegativeButton("Cancel") { dialog, _ -> 51 | dialog.cancel() 52 | }.create() 53 | alert.show() 54 | } 55 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/util/MarginItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.util 2 | 3 | /** 4 | * Created by burakakgun on 9.06.2019. 5 | */ 6 | import android.graphics.Rect 7 | import android.view.View 8 | import androidx.recyclerview.widget.RecyclerView 9 | 10 | /** 11 | * @author burakakgun 12 | */ 13 | 14 | class MarginItemDecoration(private val spaceHeight: Int) : RecyclerView.ItemDecoration() { 15 | override fun getItemOffsets( 16 | outRect: Rect, 17 | view: View, 18 | parent: RecyclerView, 19 | state: RecyclerView.State 20 | ) { 21 | with(outRect) { 22 | if (parent.getChildAdapterPosition(view) == 0) { 23 | top = spaceHeight 24 | } 25 | left = spaceHeight 26 | right = spaceHeight 27 | bottom = spaceHeight 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mobile/app/src/main/java/com/mbakgun/mobile/util/NearbyCommunication.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.mobile.util 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.widget.Toast 6 | import androidx.annotation.Nullable 7 | import androidx.lifecycle.MutableLiveData 8 | import com.google.android.gms.common.api.GoogleApiClient 9 | import com.google.android.gms.nearby.Nearby 10 | import com.google.android.gms.nearby.connection.ConnectionInfo 11 | import com.google.android.gms.nearby.connection.ConnectionLifecycleCallback 12 | import com.google.android.gms.nearby.connection.ConnectionResolution 13 | import com.google.android.gms.nearby.connection.ConnectionsStatusCodes 14 | import com.google.android.gms.nearby.connection.DiscoveredEndpointInfo 15 | import com.google.android.gms.nearby.connection.DiscoveryOptions 16 | import com.google.android.gms.nearby.connection.EndpointDiscoveryCallback 17 | import com.google.android.gms.nearby.connection.Payload 18 | import com.google.android.gms.nearby.connection.PayloadCallback 19 | import com.google.android.gms.nearby.connection.PayloadTransferUpdate 20 | import com.google.android.gms.nearby.connection.Strategy 21 | import com.mbakgun.mobile.App 22 | import com.mbakgun.mobile.R 23 | import java.nio.charset.Charset 24 | import javax.inject.Inject 25 | import javax.inject.Singleton 26 | 27 | /** 28 | * Created by burakakgun on 8.06.2019. 29 | */ 30 | @Suppress("DEPRECATION") 31 | @Singleton 32 | class NearbyCommunication @Inject constructor(private val app: App) { 33 | 34 | lateinit var mGoogleApiClient: GoogleApiClient 35 | lateinit var mRemoteHostEndpoint: String 36 | val mIsConnected: MutableLiveData by lazy { 37 | MutableLiveData().apply { 38 | value = false 39 | } 40 | } 41 | val remoteMessage: MutableLiveData by lazy { 42 | MutableLiveData() 43 | } 44 | 45 | companion object { 46 | private const val TAG = "NearbyCommunication" 47 | } 48 | 49 | init { 50 | mGoogleApiClient = GoogleApiClient.Builder(app) 51 | .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { 52 | override fun onConnected(@Nullable bundle: Bundle?) { 53 | Log.d( 54 | TAG, 55 | "onConnected: start discovering hosts to send connection requests" 56 | ) 57 | startDiscovery() 58 | } 59 | 60 | override fun onConnectionSuspended(i: Int) { 61 | Log.d(TAG, "onConnectionSuspended: $i") 62 | mGoogleApiClient.reconnect() 63 | } 64 | }) 65 | .addOnConnectionFailedListener { connectionResult -> 66 | Log.d( 67 | TAG, 68 | "onConnectionFailed: " + connectionResult.errorCode 69 | ) 70 | } 71 | .addApi(Nearby.CONNECTIONS_API) 72 | .build() 73 | } 74 | 75 | fun sendMessage(message: String) { 76 | Log.d(TAG, "About to send message: $message") 77 | Nearby.Connections.sendPayload( 78 | mGoogleApiClient, 79 | mRemoteHostEndpoint, 80 | Payload.fromBytes(message.toByteArray(Charset.forName("UTF-8"))) 81 | ) 82 | } 83 | 84 | private fun startDiscovery() = Nearby.Connections.startDiscovery( 85 | mGoogleApiClient, app.getString(R.string.id), object : EndpointDiscoveryCallback() { 86 | override fun onEndpointFound(endpointId: String, info: DiscoveredEndpointInfo) { 87 | Log.d(TAG, "onEndpointFound:" + endpointId + ":" + info.endpointName) 88 | 89 | Nearby.Connections 90 | .requestConnection(mGoogleApiClient, null, endpointId, object : ConnectionLifecycleCallback() { 91 | override fun onConnectionInitiated(endpointId: String, connectionInfo: ConnectionInfo) { 92 | Log.d(TAG, "onConnectionInitiated. Token: " + connectionInfo.authenticationToken) 93 | Nearby.Connections.acceptConnection( 94 | mGoogleApiClient, 95 | endpointId, 96 | object : PayloadCallback() { 97 | override fun onPayloadReceived(endpointId: String, payload: Payload) { 98 | String(payload.asBytes()!!).apply { 99 | Log.d(TAG, "onPayloadReceived: $this") 100 | remoteMessage.postValue(this) 101 | } 102 | } 103 | 104 | override fun onPayloadTransferUpdate( 105 | endpointId: String, 106 | update: PayloadTransferUpdate 107 | ) = Unit 108 | }) 109 | } 110 | 111 | override fun onConnectionResult(endpointId: String, resolution: ConnectionResolution) { 112 | Log.d(TAG, "onConnectionResult:" + endpointId + ":" + resolution.status) 113 | if (resolution.status.isSuccess) { 114 | Log.d(TAG, "Connected successfully") 115 | Nearby.Connections.stopDiscovery(mGoogleApiClient) 116 | mRemoteHostEndpoint = endpointId 117 | mIsConnected.postValue(true) 118 | Toast.makeText(app, "Connected with Android Things", Toast.LENGTH_SHORT).show() 119 | } else { 120 | if (resolution.status.statusCode == ConnectionsStatusCodes.STATUS_CONNECTION_REJECTED) { 121 | Log.d(TAG, "The connection was rejected by one or both sides") 122 | } else { 123 | Log.d( 124 | TAG, 125 | "Connection to " + endpointId + " failed. Code: " + resolution.status.statusCode 126 | ) 127 | } 128 | mIsConnected.postValue(false) 129 | } 130 | } 131 | 132 | override fun onDisconnected(endpointId: String) { 133 | Toast.makeText(app, "Disconnected", Toast.LENGTH_SHORT).show() 134 | mIsConnected.postValue(false) 135 | Log.d(TAG, "onDisconnected: $endpointId") 136 | } 137 | }) 138 | } 139 | 140 | override fun onEndpointLost(endpointId: String) { 141 | Log.d(TAG, "onEndpointLost:$endpointId") 142 | } 143 | }, 144 | DiscoveryOptions(Strategy.P2P_STAR) 145 | ).setResultCallback { status -> 146 | if (status.isSuccess) { 147 | Log.d(TAG, "Discovering...") 148 | } else { 149 | Log.d( 150 | TAG, 151 | "Discovering failed: " + status.statusMessage + "(" + status.statusCode + ")" 152 | ) 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/drawable/ic_location_disabled.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/drawable/ic_sync.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 20 | 21 | 31 | 32 | 43 | 44 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/layout/empty_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/layout/ir_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 23 | 24 | 27 | 28 | 29 | 41 | 42 | 43 | 53 | 54 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19dp 4 | 8dp 5 | 6 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | IrRemote Mobile 3 | irremote 4 | Empty List click button to add IR signal 5 | 6 | -------------------------------------------------------------------------------- /mobile/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /mobile/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.31' 5 | repositories { 6 | google() 7 | jcenter() 8 | 9 | } 10 | dependencies { 11 | classpath Libs.com_android_tools_build_gradle 12 | classpath Libs.kotlin_gradle_plugin 13 | } 14 | } 15 | plugins { 16 | id("de.fayard.buildSrcVersions") version "0.3.2" 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /mobile/buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .gradle/ 3 | build/ 4 | -------------------------------------------------------------------------------- /mobile/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | repositories { 5 | jcenter() 6 | } 7 | -------------------------------------------------------------------------------- /mobile/buildSrc/src/main/kotlin/Libs.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by https://github.com/jmfayard/buildSrcVersions 3 | * 4 | * Update this file with 5 | * `$ ./gradlew buildSrcVersions` */ 6 | object Libs { 7 | /** 8 | * http://developer.android.com/tools/extras/support-library.html */ 9 | const val appcompat: String = "androidx.appcompat:appcompat:" + Versions.appcompat 10 | 11 | /** 12 | * http://tools.android.com */ 13 | const val constraintlayout: String = "androidx.constraintlayout:constraintlayout:" + 14 | Versions.constraintlayout 15 | 16 | /** 17 | * http://developer.android.com/tools/extras/support-library.html */ 18 | const val core_ktx: String = "androidx.core:core-ktx:" + Versions.core_ktx 19 | 20 | const val databinding_adapters: String = "androidx.databinding:databinding-adapters:" + 21 | Versions.androidx_databinding 22 | 23 | /** 24 | * https://developer.android.com/studio */ 25 | const val databinding_common: String = "androidx.databinding:databinding-common:" + 26 | Versions.androidx_databinding 27 | 28 | /** 29 | * https://developer.android.com/studio */ 30 | const val databinding_compiler: String = "androidx.databinding:databinding-compiler:" + 31 | Versions.androidx_databinding 32 | 33 | const val databinding_runtime: String = "androidx.databinding:databinding-runtime:" + 34 | Versions.androidx_databinding 35 | 36 | /** 37 | * https://developer.android.com/topic/libraries/architecture/index.html */ 38 | const val lifecycle_extensions: String = "androidx.lifecycle:lifecycle-extensions:" + 39 | Versions.lifecycle_extensions 40 | 41 | /** 42 | * https://developer.android.com/studio */ 43 | const val com_android_tools_build_gradle: String = "com.android.tools.build:gradle:" + 44 | Versions.com_android_tools_build_gradle 45 | 46 | /** 47 | * https://developer.android.com/studio */ 48 | const val lint_gradle: String = "com.android.tools.lint:lint-gradle:" + Versions.lint_gradle 49 | 50 | const val play_services_nearby: String = "com.google.android.gms:play-services-nearby:" + 51 | Versions.play_services_nearby 52 | 53 | /** 54 | * http://developer.android.com/tools/extras/support-library.html */ 55 | const val material: String = "com.google.android.material:material:" + Versions.material 56 | 57 | /** 58 | * https://github.com/google/gson */ 59 | const val gson: String = "com.google.code.gson:gson:" + Versions.gson 60 | 61 | /** 62 | * https://github.com/google/dagger */ 63 | const val dagger_android_processor: String = "com.google.dagger:dagger-android-processor:" + 64 | Versions.com_google_dagger 65 | 66 | /** 67 | * https://github.com/google/dagger */ 68 | const val dagger_android_support: String = "com.google.dagger:dagger-android-support:" + 69 | Versions.com_google_dagger 70 | 71 | /** 72 | * https://github.com/google/dagger */ 73 | const val dagger_compiler: String = "com.google.dagger:dagger-compiler:" + 74 | Versions.com_google_dagger 75 | 76 | const val de_fayard_buildsrcversions_gradle_plugin: String = 77 | "de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" + 78 | Versions.de_fayard_buildsrcversions_gradle_plugin 79 | 80 | /** 81 | * https://kotlinlang.org/ */ 82 | const val kotlin_android_extensions_runtime: String = 83 | "org.jetbrains.kotlin:kotlin-android-extensions-runtime:" + 84 | Versions.org_jetbrains_kotlin 85 | 86 | /** 87 | * https://kotlinlang.org/ */ 88 | const val kotlin_android_extensions: String = 89 | "org.jetbrains.kotlin:kotlin-android-extensions:" + Versions.org_jetbrains_kotlin 90 | 91 | /** 92 | * https://kotlinlang.org/ */ 93 | const val kotlin_annotation_processing_gradle: String = 94 | "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:" + 95 | Versions.org_jetbrains_kotlin 96 | 97 | /** 98 | * https://kotlinlang.org/ */ 99 | const val kotlin_gradle_plugin: String = "org.jetbrains.kotlin:kotlin-gradle-plugin:" + 100 | Versions.org_jetbrains_kotlin 101 | 102 | /** 103 | * https://kotlinlang.org/ */ 104 | const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" + 105 | Versions.org_jetbrains_kotlin 106 | } 107 | -------------------------------------------------------------------------------- /mobile/buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Find which updates are available by running 3 | * `$ ./gradlew buildSrcVersions` 4 | * This will only update the comments. 5 | * 6 | * YOU are responsible for updating manually the dependency version. */ 7 | object Versions { 8 | const val appcompat: String = "1.0.2" 9 | 10 | const val constraintlayout: String = "1.1.3" 11 | 12 | const val core_ktx: String = "1.0.2" 13 | 14 | const val androidx_databinding: String = "3.4.2" 15 | 16 | const val lifecycle_extensions: String = "2.0.0" 17 | 18 | const val com_android_tools_build_gradle: String = "3.4.2" 19 | 20 | const val lint_gradle: String = "26.4.2" 21 | 22 | const val play_services_nearby: String = "17.0.0" 23 | 24 | const val material: String = "1.0.0" 25 | 26 | const val gson: String = "2.8.5" 27 | 28 | const val com_google_dagger: String = "2.24" 29 | 30 | const val de_fayard_buildsrcversions_gradle_plugin: String = "0.3.2" 31 | 32 | const val org_jetbrains_kotlin: String = "1.3.41" 33 | 34 | /** 35 | * 36 | * To update Gradle, edit the wrapper file at path: 37 | * ./gradle/wrapper/gradle-wrapper.properties 38 | */ 39 | object Gradle { 40 | const val runningVersion: String = "5.1.1" 41 | 42 | const val currentVersion: String = "5.5.1" 43 | 44 | const val nightlyVersion: String = "5.7-20190805220111+0000" 45 | 46 | const val releaseCandidate: String = "5.6-rc-1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mobile/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 10 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | # Project-wide Gradle settings. 16 | # IDE (e.g. Android Studio) users: 17 | # Gradle settings configured through the IDE *will override* 18 | # any settings specified in this file. 19 | # For more details on how to configure your build environment visit 20 | # http://www.gradle.org/docs/current/userguide/build_environment.html 21 | # Specifies the JVM arguments used for the daemon process. 22 | # The setting is particularly useful for tweaking memory settings. 23 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 24 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 25 | # When configured, Gradle will run in incubating parallel mode. 26 | # This option should only be used with decoupled projects. More details, visit 27 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 28 | # org.gradle.parallel=true 29 | org.gradle.jvmargs=-Xmx8192m -Dfile.encoding=UTF-8 30 | android.enableBuildCache=true 31 | org.gradle.caching=true 32 | android.useAndroidX=true 33 | android.enableJetifier=true 34 | kotlin.code.style=official 35 | -Dsonar.host.url=http://localhost:9000/ -------------------------------------------------------------------------------- /mobile/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/mobile/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /mobile/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jun 08 20:01:07 EET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /mobile/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /mobile/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /things/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /things/Readme.md: -------------------------------------------------------------------------------- 1 | ## Android Things Application 2 | 3 | Embedded App is simple as mentioned before. For optional part of speech recognation you can follow below article. Also you could make your own models. 4 | 5 | ### Okey Things 6 | Thanks Nilhcem for showing us how to add speech recognition to Android Things devices.
7 | **Solution #3: The “Open Source” way** used in this project. For more information click [link](http://nilhcem.com/android-things/control-your-devices-through-voice-with-usb-audio-support) 8 | -------------------------------------------------------------------------------- /things/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /things/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'kotlin-kapt' 5 | 6 | android { 7 | compileSdkVersion 28 8 | defaultConfig { 9 | applicationId "com.mbakgun.things" 10 | minSdkVersion 27 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | lintOptions { 22 | disable('AllowBackup', 'GoogleAppIndexingWarning', 'MissingApplicationIcon') 23 | } 24 | android { 25 | compileOptions { 26 | sourceCompatibility 1.8 27 | targetCompatibility 1.8 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | //android 34 | compileOnly Libs.androidthings 35 | implementation Libs.lifecycle_extensions 36 | implementation Libs.kotlin_stdlib_jdk7 37 | implementation Libs.room_runtime 38 | kapt Libs.room_compiler 39 | implementation Libs.lifecycle_viewmodel_ktx 40 | implementation Libs.kotlinx_coroutines_core 41 | implementation Libs.kotlinx_coroutines_android 42 | api Libs.play_services_nearby 43 | 44 | //Serial Connection 45 | implementation Libs.usbserial 46 | 47 | //dagger 48 | kapt Libs.dagger_compiler 49 | kapt Libs.dagger_android_processor 50 | implementation Libs.dagger_android_support 51 | 52 | //Gson 53 | implementation Libs.gson 54 | 55 | // Speech Recognation 56 | implementation(name: 'tts', ext: 'aar') 57 | implementation(name: 'models', ext: 'aar') 58 | } 59 | -------------------------------------------------------------------------------- /things/app/libs/models.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/things/app/libs/models.aar -------------------------------------------------------------------------------- /things/app/libs/tts.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/things/app/libs/tts.aar -------------------------------------------------------------------------------- /things/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /things/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/App.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things 2 | 3 | import android.app.Application 4 | import com.mbakgun.things.di.components.DaggerAppComponent 5 | import dagger.android.AndroidInjector 6 | import dagger.android.DispatchingAndroidInjector 7 | import dagger.android.HasAndroidInjector 8 | import javax.inject.Inject 9 | 10 | /** 11 | * Created by burakakgun on 8.06.2019. 12 | */ 13 | class App : Application(), HasAndroidInjector { 14 | 15 | @Inject 16 | lateinit var androidInjector: DispatchingAndroidInjector 17 | 18 | override fun onCreate() { 19 | super.onCreate() 20 | 21 | DaggerAppComponent 22 | .builder() 23 | .application(this) 24 | .build() 25 | .inject(this) 26 | } 27 | 28 | override fun androidInjector(): AndroidInjector = androidInjector 29 | } 30 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/data/IrDao.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.data 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Delete 5 | import androidx.room.Insert 6 | import androidx.room.Query 7 | import androidx.room.Update 8 | 9 | /** 10 | * Created by burakakgun on 8.06.2019. 11 | */ 12 | 13 | @Dao 14 | interface IrDao { 15 | 16 | @Insert 17 | fun insert(irData: IrData) 18 | 19 | @Delete 20 | fun delete(irData: IrData) 21 | 22 | @Update 23 | fun update(irData: IrData) 24 | 25 | @Query("SELECT * FROM IrRecords where name like :speech LIMIT 1") 26 | fun getByName(speech: String): IrData? 27 | 28 | @get:Query("SELECT * FROM IrRecords") 29 | val getAll: List 30 | } 31 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/data/IrData.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.data 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | /** 7 | * Created by burakakgun on 8.06.2019. 8 | */ 9 | @Entity(tableName = "IrRecords") 10 | data class IrData(@PrimaryKey(autoGenerate = true) var id: Int = 0, var name: String, var hexCode: String) 11 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/data/IrDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.data 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | 6 | /** 7 | * Created by burakakgun on 8.06.2019. 8 | */ 9 | @Database(entities = [IrData::class], version = 1, exportSchema = false) 10 | abstract class IrDatabase : RoomDatabase() { 11 | 12 | abstract fun getIrDao(): IrDao 13 | } 14 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/data/NearbyMessage.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.data 2 | 3 | /** 4 | * Created by burakakgun on 9.06.2019. 5 | */ 6 | 7 | enum class NearbyType { 8 | UPDATE, DELETE, GET_ALL, MESSAGE 9 | } 10 | 11 | data class NearbyMessage(val nearbyType: NearbyType, val value: String) 12 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/di/components/AppComponent.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.di.components 2 | 3 | import com.mbakgun.things.App 4 | import com.mbakgun.things.di.modules.ActivityInjectorsModule 5 | import com.mbakgun.things.di.modules.AppModule 6 | import dagger.BindsInstance 7 | import dagger.Component 8 | import dagger.android.support.AndroidSupportInjectionModule 9 | import javax.inject.Singleton 10 | 11 | /** 12 | * Created by burakakgun on 8.06.2019. 13 | */ 14 | @Singleton 15 | @Component( 16 | modules = [AndroidSupportInjectionModule::class, 17 | ActivityInjectorsModule::class, 18 | AppModule::class] 19 | ) 20 | @SuppressWarnings("unchecked") 21 | interface AppComponent { 22 | 23 | @Component.Builder 24 | interface Builder { 25 | @BindsInstance 26 | fun application(application: App): Builder 27 | 28 | fun build(): AppComponent 29 | } 30 | 31 | fun inject(app: App) 32 | } 33 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/di/modules/ActivityInjectorsModule.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.di.modules 2 | 3 | import com.mbakgun.things.ui.MainActivity 4 | import dagger.Module 5 | import dagger.android.ContributesAndroidInjector 6 | 7 | /** 8 | * Created by burakakgun on 8.06.2019. 9 | */ 10 | @Module 11 | abstract class ActivityInjectorsModule { 12 | 13 | @ContributesAndroidInjector 14 | abstract fun mainActivityInjector(): MainActivity 15 | } 16 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/di/modules/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.di.modules 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.room.Room 6 | import com.mbakgun.things.App 7 | import com.mbakgun.things.data.IrDao 8 | import com.mbakgun.things.data.IrDatabase 9 | import dagger.Module 10 | import dagger.Provides 11 | import javax.inject.Singleton 12 | 13 | /** 14 | * Created by burakakgun on 8.06.2019. 15 | */ 16 | @Module 17 | class AppModule { 18 | 19 | @Provides 20 | @Singleton 21 | fun provideApplication(app: App): Application = app 22 | 23 | @Provides 24 | @Singleton 25 | fun provideApplicationContext(app: App): Context = app.applicationContext 26 | 27 | @Singleton 28 | @Provides 29 | fun provideDb(app: Application): IrDatabase { 30 | return Room 31 | .databaseBuilder(app, IrDatabase::class.java, "irDbThings.db") 32 | .fallbackToDestructiveMigration() 33 | .build() 34 | } 35 | 36 | @Singleton 37 | @Provides 38 | fun provideIrDao(db: IrDatabase): IrDao { 39 | return db.getIrDao() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/ui/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.ui 2 | 3 | import android.os.Bundle 4 | import com.google.gson.Gson 5 | import com.mbakgun.things.data.NearbyMessage 6 | import com.mbakgun.things.data.NearbyType 7 | import com.mbakgun.things.util.NearbyCommunication 8 | import com.mbakgun.things.util.PocketSphinx 9 | import com.mbakgun.things.util.SerialCommunication 10 | import dagger.android.DaggerActivity 11 | import javax.inject.Inject 12 | 13 | class MainActivity : DaggerActivity() { 14 | @Inject 15 | lateinit var serialCommunication: SerialCommunication 16 | 17 | @Inject 18 | lateinit var nearbyCommunication: NearbyCommunication 19 | 20 | @Inject 21 | lateinit var speechRecognizer: PocketSphinx 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | serialCommunication.initSerialConnectionOverUSB() 26 | speechRecognizer.runRecognizerSetup() 27 | } 28 | 29 | fun onSerialDataReceived(data: String) = 30 | nearbyCommunication.sendMessage( 31 | Gson().toJson(NearbyMessage(NearbyType.MESSAGE, data)) 32 | ) 33 | 34 | fun onNearByDeviceRequestedSerialCommand(data: String) = serialCommunication.sendSerialData(data) 35 | 36 | fun onTextRecognized(recognizedText: String) = serialCommunication.sendVoiceData(recognizedText) 37 | 38 | override fun onResume() { 39 | super.onResume() 40 | nearbyCommunication.connect() 41 | serialCommunication.startUsbConnection() 42 | } 43 | 44 | override fun onStop() { 45 | super.onStop() 46 | serialCommunication.stopUsbConnection() 47 | nearbyCommunication.disconnect() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/util/NearbyCommunication.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.util 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import androidx.annotation.Nullable 6 | import com.google.android.gms.common.api.GoogleApiClient 7 | import com.google.android.gms.nearby.Nearby 8 | import com.google.android.gms.nearby.connection.AdvertisingOptions 9 | import com.google.android.gms.nearby.connection.ConnectionInfo 10 | import com.google.android.gms.nearby.connection.ConnectionLifecycleCallback 11 | import com.google.android.gms.nearby.connection.ConnectionResolution 12 | import com.google.android.gms.nearby.connection.Payload 13 | import com.google.android.gms.nearby.connection.PayloadCallback 14 | import com.google.android.gms.nearby.connection.PayloadTransferUpdate 15 | import com.google.android.gms.nearby.connection.Strategy 16 | import com.google.gson.Gson 17 | import com.mbakgun.things.R 18 | import com.mbakgun.things.data.IrDao 19 | import com.mbakgun.things.data.IrData 20 | import com.mbakgun.things.data.NearbyMessage 21 | import com.mbakgun.things.data.NearbyType 22 | import com.mbakgun.things.ui.MainActivity 23 | import kotlinx.coroutines.GlobalScope 24 | import kotlinx.coroutines.launch 25 | import java.nio.charset.Charset 26 | import javax.inject.Inject 27 | 28 | /** 29 | * Created by burakakgun on 8.06.2019. 30 | */ 31 | @Suppress("DEPRECATION") 32 | class NearbyCommunication @Inject constructor(private val activity: MainActivity, private val irDao: IrDao) { 33 | lateinit var mGoogleApiClient: GoogleApiClient 34 | val mRemotePeerEndpoints = mutableListOf() 35 | 36 | companion object { 37 | private const val TAG = "NearbyCommunication" 38 | } 39 | 40 | init { 41 | mGoogleApiClient = GoogleApiClient.Builder(activity) 42 | .addConnectionCallbacks(object : GoogleApiClient.ConnectionCallbacks { 43 | override fun onConnected(@Nullable bundle: Bundle?) { 44 | Log.d(TAG, "onConnected: advertises on the network as the host") 45 | startAdvertising() 46 | } 47 | 48 | override fun onConnectionSuspended(i: Int) { 49 | Log.d(TAG, "onConnectionSuspended: $i") 50 | mGoogleApiClient.reconnect() 51 | } 52 | }) 53 | .addOnConnectionFailedListener { connectionResult -> Log.d(TAG, "onConnectionFailed: $connectionResult") } 54 | .addApi(Nearby.CONNECTIONS_API) 55 | .build() 56 | } 57 | 58 | fun connect() { 59 | if (mGoogleApiClient.isConnected.not()) mGoogleApiClient.connect() 60 | } 61 | 62 | fun disconnect() { 63 | if (mGoogleApiClient.isConnected()) { 64 | Nearby.Connections.stopAdvertising(mGoogleApiClient) 65 | if (mRemotePeerEndpoints.isNotEmpty()) { 66 | Nearby.Connections.sendPayload( 67 | mGoogleApiClient, 68 | mRemotePeerEndpoints, 69 | Payload.fromBytes("Shutting down host".toByteArray()) 70 | ) 71 | Nearby.Connections.stopAllEndpoints(mGoogleApiClient) 72 | mRemotePeerEndpoints.clear() 73 | } 74 | 75 | mGoogleApiClient.disconnect() 76 | } 77 | } 78 | 79 | fun sendMessage(message: String) { 80 | Log.d(TAG, "About to send message: $message") 81 | Nearby.Connections.sendPayload( 82 | mGoogleApiClient, 83 | mRemotePeerEndpoints, 84 | Payload.fromBytes(message.toByteArray(Charset.forName("UTF-8"))) 85 | ) 86 | } 87 | 88 | private fun startAdvertising() = Nearby.Connections 89 | .startAdvertising( 90 | mGoogleApiClient, null, activity.getString(R.string.id), object : ConnectionLifecycleCallback() { 91 | override fun onConnectionInitiated(endpointId: String, connectionInfo: ConnectionInfo) { 92 | Log.d(TAG, "onConnectionInitiated. Token: " + connectionInfo.authenticationToken) 93 | Nearby.Connections.acceptConnection(mGoogleApiClient, endpointId, object : PayloadCallback() { 94 | override fun onPayloadTransferUpdate(p0: String, p1: PayloadTransferUpdate) = Unit 95 | 96 | override fun onPayloadReceived(endpointId: String, payload: Payload) { 97 | String(payload.asBytes()!!).apply { 98 | Log.d(TAG, "onPayloadReceived: $this") 99 | val message = Gson().fromJson(this, NearbyMessage::class.java) 100 | when (message.nearbyType) { 101 | NearbyType.UPDATE -> { 102 | val irData = Gson().fromJson(message.value, IrData::class.java) 103 | GlobalScope.launch { 104 | irDao.update(irData) 105 | sendMessage(Gson().toJson(NearbyMessage(NearbyType.MESSAGE, "Updated"))) 106 | } 107 | } 108 | NearbyType.DELETE -> { 109 | val irData = Gson().fromJson(message.value, IrData::class.java) 110 | GlobalScope.launch { 111 | irDao.delete(irData) 112 | sendMessage(Gson().toJson(NearbyMessage(NearbyType.MESSAGE, "Deleted"))) 113 | } 114 | } 115 | NearbyType.GET_ALL -> { 116 | GlobalScope.launch { 117 | sendMessage( 118 | Gson().toJson( 119 | NearbyMessage( 120 | NearbyType.GET_ALL, 121 | Gson().toJson(irDao.getAll) 122 | ) 123 | ) 124 | ) 125 | } 126 | } 127 | NearbyType.MESSAGE -> activity.onNearByDeviceRequestedSerialCommand(message.value) 128 | } 129 | } 130 | } 131 | }) 132 | } 133 | 134 | override fun onConnectionResult(endpointId: String, resolution: ConnectionResolution) { 135 | Log.d(TAG, "onConnectionResult") 136 | if (resolution.status.isSuccess) { 137 | if (!mRemotePeerEndpoints.contains(endpointId)) { 138 | mRemotePeerEndpoints.add(endpointId) 139 | } 140 | Log.d(TAG, "Connected! (endpointId=$endpointId)") 141 | } else { 142 | Log.w(TAG, "Connection to " + endpointId + " failed. Code: " + resolution.status.statusCode) 143 | } 144 | } 145 | 146 | override fun onDisconnected(endpointId: String) { 147 | Log.i(TAG, "onDisconnected: $endpointId") 148 | } 149 | }, 150 | AdvertisingOptions(Strategy.P2P_STAR) 151 | ) 152 | .setResultCallback { result -> 153 | Log.d(TAG, "startAdvertising:onResult:$result") 154 | if (result.status.isSuccess) { 155 | Log.d(TAG, "Advertising...") 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/util/PocketSphinx.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.util 2 | 3 | import android.util.Log 4 | import com.mbakgun.things.ui.MainActivity 5 | import edu.cmu.pocketsphinx.Assets 6 | import edu.cmu.pocketsphinx.Hypothesis 7 | import edu.cmu.pocketsphinx.RecognitionListener 8 | import edu.cmu.pocketsphinx.SpeechRecognizer 9 | import edu.cmu.pocketsphinx.SpeechRecognizerSetup 10 | import kotlinx.coroutines.Dispatchers 11 | import kotlinx.coroutines.GlobalScope 12 | import kotlinx.coroutines.launch 13 | import java.io.File 14 | import java.io.IOException 15 | import javax.inject.Inject 16 | 17 | class PocketSphinx @Inject constructor( 18 | private val activity: MainActivity 19 | ) : RecognitionListener { 20 | 21 | private var recognizer: SpeechRecognizer? = null 22 | 23 | override fun onBeginningOfSpeech() { 24 | Log.d(TAG, "onBeginningOfSpeech") 25 | } 26 | 27 | override fun onEndOfSpeech() { 28 | Log.d(TAG, "onEndOfSpeech") 29 | 30 | if (recognizer?.searchName != WAKEUP_SEARCH) { 31 | Log.i(TAG, "End of speech. Stop recognizer") 32 | recognizer?.stop() 33 | } 34 | } 35 | 36 | override fun onPartialResult(hypothesis: Hypothesis?) { 37 | if (hypothesis == null) { 38 | return 39 | } 40 | val text = hypothesis.hypstr 41 | if (text == ACTIVATION_KEYPHRASE) { 42 | Log.i(TAG, "Activation keyphrase detected during a partial result") 43 | recognizer?.stop() 44 | } else { 45 | Log.i(TAG, "On partial result: $text") 46 | } 47 | } 48 | 49 | override fun onResult(hypothesis: Hypothesis?) { 50 | if (hypothesis == null) { 51 | return 52 | } 53 | val text = hypothesis.hypstr 54 | Log.i(TAG, "On result: $text") 55 | if (ACTIVATION_KEYPHRASE == text) { 56 | startListeningToAction() 57 | } else { 58 | text?.apply { 59 | activity.onTextRecognized(this) 60 | } 61 | startListeningToActivationPhrase() 62 | } 63 | } 64 | 65 | override fun onError(e: Exception) { 66 | Log.e(TAG, "On error", e) 67 | } 68 | 69 | override fun onTimeout() { 70 | Log.i(TAG, "Timeout!") 71 | recognizer?.stop() 72 | startListeningToActivationPhrase() 73 | } 74 | 75 | fun runRecognizerSetup() { 76 | Log.d(TAG, "Recognizer setup") 77 | GlobalScope.launch(Dispatchers.IO) { 78 | try { 79 | val assets = Assets(activity) 80 | val assetDir = assets.syncAssets() 81 | setupRecognizer(assetDir) 82 | } catch (e: Exception) { 83 | Log.e(TAG, "Failed to initialize recognizer: ${e.localizedMessage}") 84 | } 85 | } 86 | } 87 | 88 | @Throws(IOException::class) 89 | private fun setupRecognizer(assetsDir: File) { 90 | recognizer = SpeechRecognizerSetup.defaultSetup() 91 | .setAcousticModel(File(assetsDir, "en-us-ptm")) 92 | .setDictionary(File(assetsDir, "cmudict-en-us.dict")) 93 | .recognizer 94 | recognizer?.addListener(this) 95 | 96 | // Custom recognizer 97 | recognizer?.addKeyphraseSearch(WAKEUP_SEARCH, ACTIVATION_KEYPHRASE) 98 | recognizer?.addNgramSearch(ACTION_SEARCH, File(assetsDir, "predefined.lm.bin")) 99 | startListeningToAction() 100 | } 101 | 102 | private fun startListeningToActivationPhrase() { 103 | Log.i(TAG, "Start listening for the \"ok things\" keyphrase") 104 | recognizer?.startListening(WAKEUP_SEARCH) 105 | } 106 | 107 | private fun startListeningToAction() { 108 | Log.i(TAG, "Start listening for some actions with a 10secs timeout") 109 | recognizer?.startListening(ACTION_SEARCH, 10000) 110 | } 111 | 112 | companion object { 113 | private val TAG = PocketSphinx::class.java.simpleName 114 | private const val ACTIVATION_KEYPHRASE = "ok things" 115 | private const val WAKEUP_SEARCH = "wakeup" 116 | private const val ACTION_SEARCH = "action" 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /things/app/src/main/java/com/mbakgun/things/util/SerialCommunication.kt: -------------------------------------------------------------------------------- 1 | package com.mbakgun.things.util 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.IntentFilter 7 | import android.hardware.usb.UsbDevice 8 | import android.hardware.usb.UsbDeviceConnection 9 | import android.hardware.usb.UsbManager 10 | import android.util.Log 11 | import android.util.Log.d 12 | import com.felhr.usbserial.UsbSerialDevice 13 | import com.felhr.usbserial.UsbSerialInterface 14 | import com.mbakgun.things.R 15 | import com.mbakgun.things.data.IrDao 16 | import com.mbakgun.things.data.IrData 17 | import com.mbakgun.things.ui.MainActivity 18 | import kotlinx.coroutines.Dispatchers 19 | import kotlinx.coroutines.GlobalScope 20 | import kotlinx.coroutines.launch 21 | import java.io.UnsupportedEncodingException 22 | import java.nio.charset.Charset 23 | import javax.inject.Inject 24 | 25 | /** 26 | * Created by burakakgun on 8.06.2019. 27 | */ 28 | class SerialCommunication @Inject constructor(private val activity: MainActivity, private val irDao: IrDao) { 29 | 30 | private var connection: UsbDeviceConnection? = null 31 | private var serialDevice: UsbSerialDevice? = null 32 | private var buffer = "" 33 | 34 | private val usbManager by lazy { 35 | activity.getSystemService(UsbManager::class.java) 36 | } 37 | 38 | fun initSerialConnectionOverUSB() { 39 | val filter = IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED) 40 | activity.registerReceiver(usbDetachedReceiver, filter) 41 | } 42 | 43 | private val usbDetachedReceiver = object : BroadcastReceiver() { 44 | override fun onReceive(context: Context, intent: Intent) { 45 | val action = intent.action 46 | if (UsbManager.ACTION_USB_DEVICE_DETACHED == action) { 47 | val device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) 48 | if (device != null && device.vendorId == USB_VENDOR_ID && device.productId == USB_PRODUCT_ID) { 49 | Log.i(TAG, "USB device detached") 50 | stopUsbConnection() 51 | } 52 | } 53 | } 54 | } 55 | 56 | fun startUsbConnection() { 57 | val connectedDevices = usbManager.deviceList 58 | if (connectedDevices.isNotEmpty()) { 59 | for (device in connectedDevices.values) { 60 | if (device.vendorId == USB_VENDOR_ID && device.productId == USB_PRODUCT_ID) { 61 | Log.i(TAG, "Device found: " + device.deviceName) 62 | startSerialConnection(device) 63 | return 64 | } 65 | } 66 | } 67 | Log.w(TAG, "Could not start USB connection - No devices found") 68 | } 69 | 70 | private fun startSerialConnection(device: UsbDevice) { 71 | Log.i(TAG, "Ready to open USB device connection") 72 | connection = usbManager.openDevice(device) 73 | serialDevice = UsbSerialDevice.createUsbSerialDevice(device, connection) 74 | if (serialDevice!!.open()) { 75 | with(serialDevice!!) { 76 | setBaudRate(BAUD_RATE) 77 | setDataBits(UsbSerialInterface.DATA_BITS_8) 78 | setStopBits(UsbSerialInterface.STOP_BITS_1) 79 | setParity(UsbSerialInterface.PARITY_NONE) 80 | setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF) 81 | read(callback) 82 | } 83 | Log.i(TAG, "Serial connection opened") 84 | } else { 85 | Log.w(TAG, "Cannot open serial connection") 86 | } 87 | } 88 | 89 | fun sendSerialData(value: String) { 90 | serialDevice?.write(value.toByteArray()) 91 | } 92 | 93 | fun sendVoiceData(speech: String) { 94 | GlobalScope.launch(Dispatchers.IO) { 95 | val value = irDao.getByName(speech) 96 | value?.let { 97 | d("SerialCommunication", "speech sending:$speech") 98 | serialDevice?.write("send:${value.hexCode}".toByteArray()) 99 | } 100 | } 101 | } 102 | 103 | fun stopUsbConnection() { 104 | activity.unregisterReceiver(usbDetachedReceiver) 105 | try { 106 | serialDevice?.close() 107 | connection?.close() 108 | } finally { 109 | serialDevice = null 110 | connection = null 111 | } 112 | } 113 | 114 | private val callback = UsbSerialInterface.UsbReadCallback { data -> 115 | try { 116 | val dataUtf8 = String(data, Charset.forName("UTF-8")) 117 | buffer += dataUtf8 118 | var index = 0 119 | while ({ 120 | index = buffer.indexOf('\n') 121 | index 122 | }() != -1) { 123 | val dataStr = buffer.substring(0, index + 1).trim { it <= ' ' } 124 | buffer = if (buffer.length == index) "" else buffer.substring(index + 1) 125 | 126 | Log.i(TAG, "Serial data received: $data") 127 | if (dataStr.startsWith("saved")) { 128 | val irData = 129 | IrData( 130 | name = dataStr.substring(SUBSTRING_STARTS, dataStr.indexOf("-")), 131 | hexCode = dataStr.substring(dataStr.indexOf("-") + 1) 132 | ) 133 | if (irData.hexCode != "FFFFFFFF") { 134 | // end of button push 135 | GlobalScope.launch { 136 | irDao.insert(irData) 137 | activity.onSerialDataReceived(activity.getString(R.string.saved)) 138 | } 139 | } else { 140 | activity.onSerialDataReceived(activity.getString(R.string.try_again)) 141 | } 142 | } else { 143 | activity.onSerialDataReceived(dataStr) 144 | } 145 | } 146 | } catch (e: UnsupportedEncodingException) { 147 | Log.e(TAG, "Error receiving USB data", e) 148 | } 149 | } 150 | 151 | companion object { 152 | const val TAG = "SerialCommunication" 153 | const val USB_VENDOR_ID = 6790 154 | const val USB_PRODUCT_ID = 29987 155 | const val BAUD_RATE = 9600 156 | const val SUBSTRING_STARTS = 6 // 'read:' and 'send:' 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /things/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | irremote things 3 | irremote 4 | Command saved 5 | Please try again 6 | 7 | -------------------------------------------------------------------------------- /things/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /things/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.31' 5 | repositories { 6 | google() 7 | jcenter() 8 | 9 | } 10 | dependencies { 11 | classpath Libs.com_android_tools_build_gradle 12 | classpath Libs.kotlin_gradle_plugin 13 | } 14 | } 15 | 16 | plugins { 17 | id("de.fayard.buildSrcVersions") version "0.3.2" 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | maven { url "https://jitpack.io" } 25 | flatDir { dirs 'libs' } 26 | } 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /things/buildSrc/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .gradle/ 3 | build/ 4 | -------------------------------------------------------------------------------- /things/buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | repositories { 5 | jcenter() 6 | } 7 | -------------------------------------------------------------------------------- /things/buildSrc/src/main/kotlin/Libs.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Generated by https://github.com/jmfayard/buildSrcVersions 3 | * 4 | * Update this file with 5 | * `$ ./gradlew buildSrcVersions` */ 6 | object Libs { 7 | /** 8 | * https://developer.android.com/topic/libraries/architecture/index.html */ 9 | const val lifecycle_extensions: String = "androidx.lifecycle:lifecycle-extensions:" + 10 | Versions.androidx_lifecycle 11 | 12 | /** 13 | * http://developer.android.com/tools/extras/support-library.html */ 14 | const val lifecycle_viewmodel_ktx: String = "androidx.lifecycle:lifecycle-viewmodel-ktx:" + 15 | Versions.androidx_lifecycle 16 | 17 | /** 18 | * https://developer.android.com/topic/libraries/architecture/index.html */ 19 | const val room_compiler: String = "androidx.room:room-compiler:" + Versions.androidx_room 20 | 21 | /** 22 | * https://developer.android.com/topic/libraries/architecture/index.html */ 23 | const val room_runtime: String = "androidx.room:room-runtime:" + Versions.androidx_room 24 | 25 | /** 26 | * https://developer.android.com/studio */ 27 | const val com_android_tools_build_gradle: String = "com.android.tools.build:gradle:" + 28 | Versions.com_android_tools_build_gradle 29 | 30 | /** 31 | * https://developer.android.com/studio */ 32 | const val lint_gradle: String = "com.android.tools.lint:lint-gradle:" + Versions.lint_gradle 33 | 34 | /** 35 | * https://github.com/felHR85/UsbSerial */ 36 | const val usbserial: String = "com.github.felHR85:UsbSerial:" + Versions.usbserial 37 | 38 | const val play_services_nearby: String = "com.google.android.gms:play-services-nearby:" + 39 | Versions.play_services_nearby 40 | 41 | const val androidthings: String = "com.google.android.things:androidthings:" + 42 | Versions.androidthings 43 | 44 | /** 45 | * https://github.com/google/gson */ 46 | const val gson: String = "com.google.code.gson:gson:" + Versions.gson 47 | 48 | /** 49 | * https://github.com/google/dagger */ 50 | const val dagger_android_processor: String = "com.google.dagger:dagger-android-processor:" + 51 | Versions.com_google_dagger 52 | 53 | /** 54 | * https://github.com/google/dagger */ 55 | const val dagger_android_support: String = "com.google.dagger:dagger-android-support:" + 56 | Versions.com_google_dagger 57 | 58 | /** 59 | * https://github.com/google/dagger */ 60 | const val dagger_compiler: String = "com.google.dagger:dagger-compiler:" + 61 | Versions.com_google_dagger 62 | 63 | const val de_fayard_buildsrcversions_gradle_plugin: String = 64 | "de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" + 65 | Versions.de_fayard_buildsrcversions_gradle_plugin 66 | 67 | /** 68 | * https://kotlinlang.org/ */ 69 | const val kotlin_android_extensions_runtime: String = 70 | "org.jetbrains.kotlin:kotlin-android-extensions-runtime:" + 71 | Versions.org_jetbrains_kotlin 72 | 73 | /** 74 | * https://kotlinlang.org/ */ 75 | const val kotlin_android_extensions: String = 76 | "org.jetbrains.kotlin:kotlin-android-extensions:" + Versions.org_jetbrains_kotlin 77 | 78 | /** 79 | * https://kotlinlang.org/ */ 80 | const val kotlin_annotation_processing_gradle: String = 81 | "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:" + 82 | Versions.org_jetbrains_kotlin 83 | 84 | /** 85 | * https://kotlinlang.org/ */ 86 | const val kotlin_gradle_plugin: String = "org.jetbrains.kotlin:kotlin-gradle-plugin:" + 87 | Versions.org_jetbrains_kotlin 88 | 89 | /** 90 | * https://kotlinlang.org/ */ 91 | const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" + 92 | Versions.org_jetbrains_kotlin 93 | 94 | /** 95 | * https://github.com/Kotlin/kotlinx.coroutines */ 96 | const val kotlinx_coroutines_android: String = 97 | "org.jetbrains.kotlinx:kotlinx-coroutines-android:" + Versions.org_jetbrains_kotlinx 98 | 99 | /** 100 | * https://github.com/Kotlin/kotlinx.coroutines */ 101 | const val kotlinx_coroutines_core: String = "org.jetbrains.kotlinx:kotlinx-coroutines-core:" + 102 | Versions.org_jetbrains_kotlinx 103 | } 104 | -------------------------------------------------------------------------------- /things/buildSrc/src/main/kotlin/Versions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Find which updates are available by running 3 | * `$ ./gradlew buildSrcVersions` 4 | * This will only update the comments. 5 | * 6 | * YOU are responsible for updating manually the dependency version. */ 7 | object Versions { 8 | const val androidx_lifecycle: String = "2.0.0" 9 | 10 | const val androidx_room: String = "2.1.0" 11 | 12 | const val com_android_tools_build_gradle: String = "3.4.2" 13 | 14 | const val lint_gradle: String = "26.4.2" 15 | 16 | const val usbserial: String = "6.1.0" 17 | 18 | const val play_services_nearby: String = "17.0.0" 19 | 20 | const val androidthings: String = "1.0" 21 | 22 | const val gson: String = "2.8.5" 23 | 24 | const val com_google_dagger: String = "2.24" 25 | 26 | const val de_fayard_buildsrcversions_gradle_plugin: String = "0.3.2" 27 | 28 | const val org_jetbrains_kotlin: String = "1.3.41" 29 | 30 | const val org_jetbrains_kotlinx: String = "1.2.1" 31 | 32 | /** 33 | * 34 | * To update Gradle, edit the wrapper file at path: 35 | * ./gradle/wrapper/gradle-wrapper.properties 36 | */ 37 | object Gradle { 38 | const val runningVersion: String = "5.1.1" 39 | 40 | const val currentVersion: String = "5.5.1" 41 | 42 | const val nightlyVersion: String = "5.7-20190805220111+0000" 43 | 44 | const val releaseCandidate: String = "5.6-rc-1" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /things/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | # Project-wide Gradle settings. 20 | 21 | # IDE (e.g. Android Studio) users: 22 | # Gradle settings configured through the IDE *will override* 23 | # any settings specified in this file. 24 | 25 | # For more details on how to configure your build environment visit 26 | # http://www.gradle.org/docs/current/userguide/build_environment.html 27 | 28 | # Specifies the JVM arguments used for the daemon process. 29 | # The setting is particularly useful for tweaking memory settings. 30 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 31 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 32 | 33 | # When configured, Gradle will run in incubating parallel mode. 34 | # This option should only be used with decoupled projects. More details, visit 35 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 36 | # org.gradle.parallel=true 37 | 38 | 39 | org.gradle.jvmargs=-Xmx8192m -Dfile.encoding=UTF-8 40 | android.enableBuildCache=true 41 | org.gradle.caching=true 42 | android.useAndroidX=true 43 | android.enableJetifier=true 44 | kotlin.code.style=official 45 | -Dsonar.host.url=http://localhost:9000/ -------------------------------------------------------------------------------- /things/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbakgun/Android-Things-IR-Remote-Hacker/2a79a6e540618ca3789b5f506a98639c8e0d1e61/things/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /things/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Jun 08 14:37:36 EET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /things/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /things/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------