├── .gitignore ├── .travis.yml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── co │ │ │ └── joebirch │ │ │ └── braillebox │ │ │ ├── BrailleBoxApplication.java │ │ │ ├── data │ │ │ ├── Braille.java │ │ │ ├── DataManager.java │ │ │ ├── SortBy.java │ │ │ ├── Source.java │ │ │ ├── model │ │ │ │ ├── ArticleModel.java │ │ │ │ └── NewsResponse.java │ │ │ └── remote │ │ │ │ ├── BrailleBoxService.java │ │ │ │ └── BrailleBoxServiceFactory.java │ │ │ ├── injection │ │ │ ├── component │ │ │ │ ├── ActivityComponent.java │ │ │ │ └── ApplicationComponent.java │ │ │ ├── module │ │ │ │ ├── ActivityModule.java │ │ │ │ └── ApplicationModule.java │ │ │ └── scope │ │ │ │ ├── ActivityContext.java │ │ │ │ ├── ApplicationContext.java │ │ │ │ └── PerActivity.java │ │ │ ├── ui │ │ │ ├── base │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── MvpView.java │ │ │ │ └── Presenter.java │ │ │ └── main │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MainMvpView.java │ │ │ │ └── MainPresenter.java │ │ │ └── util │ │ │ ├── BoardDefaults.java │ │ │ ├── BrailleMapper.java │ │ │ └── GpioHelper.java │ └── res │ │ ├── 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 │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── co │ └── joebirch │ └── braillebox │ ├── data │ └── DataManagerTest.java │ ├── injection │ ├── component │ │ └── TestComponent.java │ └── module │ │ └── ApplicationTestModule.java │ ├── test │ └── util │ │ └── TestDataFactory.java │ ├── ui │ └── main │ │ └── MainPresenterTest.java │ └── util │ └── BrailleMapperTest.java ├── brailleBox.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── schematics.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea/workspace.xml 4 | .DS_Store 5 | /build 6 | .idea/ 7 | *iml 8 | *.iml 9 | */build -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | android: 4 | components: 5 | - platform-tools 6 | - tools 7 | 8 | # The BuildTools version used by your project 9 | - build-tools-25.0.2 10 | - android-25 11 | - extra-android-m2repository 12 | - extra-google-m2repository 13 | - extra-android-support 14 | - extra-google-google_play_services 15 | 16 | before_script: 17 | - chmod +x gradlew 18 | #Build, and run tests 19 | script: "./gradlew testDebugUnitTest" 20 | sudo: false 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/hitherejoe/BrailleBox.svg?branch=master)](https://travis-ci.org/hitherejoe/BrailleBox) 2 | 3 | # BrailleBox 4 | 5 | BrailleBox is a news reader for the visually impaired that fetches the latest news article from news.api and displays it on a refreshable braille display. 6 | 7 | ![alt text](brailleBox.png) 8 | 9 | # What it's made of 10 | 11 | - A raspberry pi 3 with Android Things 12 | - A breadboard 13 | - A project box (mine is sized 150 x 80 x 50 — make sure your breadboard fits in here if you want it to!) 14 | - 5v Solenoid x 6 15 | - Wooden balls x 6 (or something similar) 16 | - 100Ω resistor x 7 17 | - 1N4004 diode x 6 18 | - Push switch 19 | - A handful of male-to-male connector cables 20 | - Male-to-female connector cable (for connecting the GPIO pins of the raspberry pi to the breadboard) x 7 21 | 22 | The great thing is that this list isn’t too extensive and all of these components can be found for pretty cheap. The most expensive are the solenoids, but 5V ones can be picked up for around £2.50 each 🙂 23 | 24 | And with the electronically components from this put together into a circuit, the schematics for the complete circuit isn’t too complex either: 25 | 26 | ![alt text](schematics.png) 27 | 28 | You can read more about how the box was built here: https://medium.com/exploring-android/braillebox-building-a-braille-news-reader-with-android-things-f848fe6de596 29 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | *iml 3 | *.iml 4 | .idea -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | defaultConfig { 7 | applicationId "co.joebirch.braillebox" 8 | minSdkVersion 24 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | buildConfigField("String", "API_URL", "\"${apiUrl}\"") 14 | buildConfigField("String", "API_KEY", "\"${apiKey}\"") 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | dependencies { 25 | final RETROFIT_VERSION = '2.2.0' 26 | final OKHTTP_VERSION = '3.5.0' 27 | final DAGGER_VERSION = '2.4' 28 | final HAMCREST_VERSION = '1.3' 29 | 30 | def daggerCompiler = "com.google.dagger:dagger-compiler:$DAGGER_VERSION" 31 | def jUnit = "junit:junit:4.12" 32 | def mockito = "org.mockito:mockito-core:2.7.21" 33 | 34 | compile fileTree(dir: 'libs', include: ['*.jar']) 35 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 36 | exclude group: 'com.android.support', module: 'support-annotations' 37 | }) 38 | compile 'com.android.support:appcompat-v7:25.3.1' 39 | 40 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 41 | compile 'io.reactivex.rxjava2:rxjava:2.0.4' 42 | 43 | compile "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION" 44 | compile "com.squareup.retrofit2:converter-gson:$RETROFIT_VERSION" 45 | compile "com.squareup.retrofit2:adapter-rxjava2:$RETROFIT_VERSION" 46 | compile "com.squareup.okhttp3:okhttp:$OKHTTP_VERSION" 47 | compile "com.squareup.okhttp3:mockwebserver:$OKHTTP_VERSION" 48 | compile "com.squareup.okhttp3:logging-interceptor:$OKHTTP_VERSION" 49 | 50 | compile 'com.jakewharton.timber:timber:4.5.1' 51 | 52 | compile "com.google.dagger:dagger:$DAGGER_VERSION" 53 | provided 'org.glassfish:javax.annotation:10.0-b28' //Required by Dagger2 54 | 55 | provided 'com.google.android.things:androidthings:0.2-devpreview' 56 | compile 'com.google.android.things.contrib:driver-button:0.2' 57 | 58 | testCompile jUnit 59 | testCompile mockito 60 | testCompile "org.hamcrest:hamcrest-core:$HAMCREST_VERSION" 61 | testCompile "org.hamcrest:hamcrest-library:$HAMCREST_VERSION" 62 | testCompile "org.hamcrest:hamcrest-integration:$HAMCREST_VERSION" 63 | 64 | annotationProcessor daggerCompiler 65 | } 66 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/joebirch/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/BrailleBoxApplication.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import co.joebirch.braillebox.injection.component.ApplicationComponent; 7 | import co.joebirch.braillebox.injection.component.DaggerApplicationComponent; 8 | import co.joebirch.braillebox.injection.module.ApplicationModule; 9 | import timber.log.Timber; 10 | 11 | public class BrailleBoxApplication extends Application { 12 | 13 | ApplicationComponent applicationComponent; 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | 19 | if (BuildConfig.DEBUG) { 20 | Timber.plant(new Timber.DebugTree()); 21 | } 22 | 23 | applicationComponent = DaggerApplicationComponent.builder() 24 | .applicationModule(new ApplicationModule(this)) 25 | .build(); 26 | } 27 | 28 | public static BrailleBoxApplication get(Context context) { 29 | return (BrailleBoxApplication) context.getApplicationContext(); 30 | } 31 | 32 | public ApplicationComponent getComponent() { 33 | return applicationComponent; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/Braille.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data; 2 | 3 | public enum Braille { 4 | 5 | A('a', "100000"), B('b', "101000"), C('c', "110000"), D('d', "110100"), E('e', "100100"), F('f', "111000"), 6 | G('g', "111100"), H('h', "101100"), I('i', "010100"), J('j', "011100"), K('k', "100010"), L('l', "101010"), 7 | M('m', "110010"), N('n', "110110"), O('o', "100110"), P('p', "111010"), Q('q', "111110"), R('r', "101110"), 8 | S('s', "011010"), T('t', "011110"), U('u', "100011"), V('v', "101011"), W('w', "011101"), X('x', "110011"), 9 | Y('y', "110111"), Z('z', "100111"), ONE('1', "100000"), TWO('2', "101000"), THREE('3', "110000"), 10 | FOUR('4', "110100"), FIVE('5', "100100"), SIX('6', "111000"), SEVEN('7', "111100"), EIGHT('8', "101100"), 11 | NINE('9', "010100"), ZERO('0', "011100"), PERIOD('.', "001101"), COMMA(',', "001000"), 12 | QUESTION_MARK('?', "001011"), EXCLAMATION_MARK('!', "001110"), COLON(':', "001100"), 13 | SEMICOLON(';', "001010"), HASHTAG('#', "010111"), HYPHEN('-', "000011"), SPACE(' ', "000000"); 14 | 15 | private final Character key; 16 | private final String value; 17 | 18 | Braille(Character key, String value) { 19 | this.key = key; 20 | this.value = value; 21 | } 22 | 23 | public Character getKey() { 24 | return key; 25 | } 26 | 27 | public String getValue() { 28 | return value; 29 | } 30 | 31 | public static Braille fromKey(Character character) { 32 | for (Braille braille : Braille.values()) { 33 | if (braille.getKey().equals(character)) { 34 | return braille; 35 | } 36 | } 37 | return QUESTION_MARK; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/DataManager.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import javax.inject.Inject; 7 | 8 | import co.joebirch.braillebox.data.model.ArticleModel; 9 | import co.joebirch.braillebox.data.model.NewsResponse; 10 | import co.joebirch.braillebox.data.remote.BrailleBoxService; 11 | import co.joebirch.braillebox.util.BrailleMapper; 12 | import io.reactivex.Observable; 13 | import io.reactivex.ObservableSource; 14 | import io.reactivex.functions.BiFunction; 15 | import io.reactivex.functions.Function; 16 | import io.reactivex.functions.Predicate; 17 | 18 | public class DataManager { 19 | 20 | private final BrailleBoxService brailleBoxService; 21 | private final BrailleMapper brailleMapper; 22 | 23 | @Inject 24 | DataManager(BrailleBoxService brailleBoxService, 25 | BrailleMapper brailleMapper) { 26 | this.brailleBoxService = brailleBoxService; 27 | this.brailleMapper = brailleMapper; 28 | } 29 | 30 | public Observable getArticle(final int delay) { 31 | return brailleBoxService.getArticles(Source.BBC_NEWS.getId(), SortBy.TOP.getLabel()) 32 | .filter(new Predicate() { 33 | @Override 34 | public boolean test(NewsResponse newsResponse) throws Exception { 35 | return newsResponse.articles != null && 36 | !newsResponse.articles.isEmpty(); 37 | } 38 | }) 39 | .flatMap(new Function>() { 40 | @Override 41 | public ObservableSource apply(NewsResponse newsResponse) 42 | throws Exception { 43 | return Observable.just(newsResponse.articles.get(0)); 44 | } 45 | }) 46 | .flatMap(new Function>>() { 47 | @Override 48 | public ObservableSource> apply(ArticleModel articleModel) 49 | throws Exception { 50 | return Observable.just(brailleMapper.mapFromWords( 51 | articleModel.title, articleModel.description)); 52 | } 53 | }) 54 | .flatMap(new Function, ObservableSource>() { 55 | @Override 56 | public ObservableSource apply(List strings) throws Exception { 57 | return Observable.fromIterable(strings); 58 | } 59 | }) 60 | .zipWith(Observable.interval(delay, TimeUnit.MILLISECONDS), 61 | new BiFunction() { 62 | @Override 63 | public String apply(String sequence, Long aLong) throws Exception { 64 | return sequence; 65 | } 66 | }); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/SortBy.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data; 2 | 3 | enum SortBy { 4 | TOP("top"), 5 | LATEST("latest"), 6 | POPULAR("popular"); 7 | 8 | private String label; 9 | 10 | SortBy(String label) { 11 | this.label = label; 12 | } 13 | 14 | public String getLabel() { 15 | return this.label; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/Source.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data; 2 | 3 | enum Source { 4 | BBC_NEWS("bbc-news"); 5 | 6 | private String id; 7 | 8 | Source(String id) { 9 | this.id = id; 10 | } 11 | 12 | public String getId() { 13 | return this.id; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/model/ArticleModel.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data.model; 2 | 3 | public class ArticleModel { 4 | 5 | public String author; 6 | public String title; 7 | public String description; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/model/NewsResponse.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data.model; 2 | 3 | import java.util.List; 4 | 5 | public class NewsResponse { 6 | 7 | public String source; 8 | public List articles; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/remote/BrailleBoxService.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data.remote; 2 | 3 | import co.joebirch.braillebox.data.model.NewsResponse; 4 | import io.reactivex.Observable; 5 | import retrofit2.http.GET; 6 | import retrofit2.http.Query; 7 | 8 | public interface BrailleBoxService { 9 | 10 | @GET("articles") 11 | Observable getArticles(@Query("source") String source, 12 | @Query("sortBy") String sortBy); 13 | 14 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/data/remote/BrailleBoxServiceFactory.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data.remote; 2 | 3 | import com.google.gson.FieldNamingPolicy; 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import java.io.IOException; 8 | 9 | import co.joebirch.braillebox.BuildConfig; 10 | import okhttp3.HttpUrl; 11 | import okhttp3.Interceptor; 12 | import okhttp3.OkHttpClient; 13 | import okhttp3.Request; 14 | import okhttp3.Response; 15 | import okhttp3.logging.HttpLoggingInterceptor; 16 | import retrofit2.Retrofit; 17 | import retrofit2.converter.gson.GsonConverterFactory; 18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 19 | 20 | /** 21 | * Provide "make" methods to create instances of {@link BrailleBoxService} 22 | * and its related dependencies, such as OkHttpClient, Gson, etc. 23 | */ 24 | public class BrailleBoxServiceFactory { 25 | 26 | public static BrailleBoxService makeBrailleBoxService(HttpUrl url) { 27 | OkHttpClient okHttpClient = makeOkHttpClient( 28 | makeLoggingInterceptor()); 29 | return makeBrailleBoxService(okHttpClient, makeGson(), url); 30 | } 31 | 32 | public static BrailleBoxService makeBrailleBoxService() { 33 | OkHttpClient okHttpClient = makeOkHttpClient( 34 | makeLoggingInterceptor()); 35 | return makeBrailleBoxService(okHttpClient, makeGson()); 36 | } 37 | 38 | private static BrailleBoxService makeBrailleBoxService(OkHttpClient okHttpClient, Gson gson, 39 | HttpUrl url) { 40 | Retrofit retrofit = new Retrofit.Builder() 41 | .baseUrl(url) 42 | .client(okHttpClient) 43 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 44 | .addConverterFactory(GsonConverterFactory.create(gson)) 45 | .build(); 46 | return retrofit.create(BrailleBoxService.class); 47 | } 48 | 49 | private static BrailleBoxService makeBrailleBoxService(OkHttpClient okHttpClient, Gson gson) { 50 | Retrofit retrofit = new Retrofit.Builder() 51 | .baseUrl(BuildConfig.API_URL) 52 | .client(okHttpClient) 53 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 54 | .addConverterFactory(GsonConverterFactory.create(gson)) 55 | .build(); 56 | return retrofit.create(BrailleBoxService.class); 57 | } 58 | 59 | private static OkHttpClient makeOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor) { 60 | return new OkHttpClient.Builder() 61 | .addInterceptor(httpLoggingInterceptor) 62 | .addInterceptor(new Interceptor() { 63 | @Override 64 | public Response intercept(Interceptor.Chain chain) throws IOException { 65 | Request original = chain.request(); 66 | 67 | Request.Builder requestBuilder = original.newBuilder() 68 | .header("Accept", "application/json") 69 | .addHeader("x-api-key", BuildConfig.API_KEY) 70 | .method(original.method(), original.body()); 71 | 72 | Request request = requestBuilder.build(); 73 | return chain.proceed(request); 74 | } 75 | }) 76 | .build(); 77 | } 78 | 79 | private static Gson makeGson() { 80 | return new GsonBuilder() 81 | .setLenient() 82 | .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") 83 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 84 | .create(); 85 | } 86 | 87 | private static HttpLoggingInterceptor makeLoggingInterceptor() { 88 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 89 | logging.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY 90 | : HttpLoggingInterceptor.Level.NONE); 91 | return logging; 92 | } 93 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/component/ActivityComponent.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.component; 2 | 3 | import co.joebirch.braillebox.injection.module.ActivityModule; 4 | import co.joebirch.braillebox.injection.scope.PerActivity; 5 | import co.joebirch.braillebox.ui.base.BaseActivity; 6 | import co.joebirch.braillebox.ui.main.MainActivity; 7 | import dagger.Component; 8 | 9 | /** 10 | * Component used to inject dependencies throughout the application at an Activity-level 11 | */ 12 | @PerActivity 13 | @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) 14 | public interface ActivityComponent { 15 | 16 | void inject(BaseActivity baseActivity); 17 | void inject(MainActivity mainActivity); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/component/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.component; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.google.android.things.pio.PeripheralManagerService; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import co.joebirch.braillebox.data.remote.BrailleBoxService; 11 | import co.joebirch.braillebox.injection.module.ApplicationModule; 12 | import co.joebirch.braillebox.injection.scope.ApplicationContext; 13 | import dagger.Component; 14 | 15 | @Singleton 16 | @Component(modules = ApplicationModule.class) 17 | public interface ApplicationComponent { 18 | 19 | @ApplicationContext 20 | Context context(); 21 | Application application(); 22 | PeripheralManagerService peripheralManagerService(); 23 | BrailleBoxService brailleBoxService(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/module/ActivityModule.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.module; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | 6 | import co.joebirch.braillebox.injection.scope.ActivityContext; 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | @Module 11 | public class ActivityModule { 12 | 13 | private Activity activity; 14 | 15 | public ActivityModule(Activity activity) { 16 | this.activity = activity; 17 | } 18 | 19 | @Provides 20 | Activity provideActivity() { 21 | return activity; 22 | } 23 | 24 | @Provides 25 | @ActivityContext 26 | Context providesContext() { 27 | return activity; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/module/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.module; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.google.android.things.pio.PeripheralManagerService; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import co.joebirch.braillebox.data.remote.BrailleBoxService; 11 | import co.joebirch.braillebox.data.remote.BrailleBoxServiceFactory; 12 | import co.joebirch.braillebox.injection.scope.ApplicationContext; 13 | import dagger.Module; 14 | import dagger.Provides; 15 | 16 | /** 17 | * Provide application-level dependencies. Mainly singleton object that can be injected from 18 | * anywhere in the app. 19 | */ 20 | @Module 21 | public class ApplicationModule { 22 | protected final Application application; 23 | 24 | public ApplicationModule(Application application) { 25 | this.application = application; 26 | } 27 | 28 | @Provides 29 | @Singleton 30 | Application provideApplication() { 31 | return application; 32 | } 33 | 34 | @Provides 35 | @ApplicationContext 36 | Context provideContext() { 37 | return application; 38 | } 39 | 40 | @Provides 41 | @Singleton 42 | BrailleBoxService provideBrailleBoxService() { 43 | return BrailleBoxServiceFactory.makeBrailleBoxService(); 44 | } 45 | 46 | @Provides 47 | PeripheralManagerService providesPeripheralManagerService() { 48 | return new PeripheralManagerService(); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/scope/ActivityContext.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.scope; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Qualifier; 7 | 8 | @Qualifier 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ActivityContext { 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/scope/ApplicationContext.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.scope; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Qualifier; 7 | 8 | @Qualifier 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ApplicationContext { 11 | 12 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/injection/scope/PerActivity.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.scope; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | /** 9 | * A scoping annotation to permit objects whose lifetime should 10 | * conform to the life of the Activity to be memorised in the 11 | * correct component. 12 | */ 13 | @Scope 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface PerActivity { 16 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.base; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import co.joebirch.braillebox.BrailleBoxApplication; 7 | import co.joebirch.braillebox.injection.component.ActivityComponent; 8 | import co.joebirch.braillebox.injection.component.DaggerActivityComponent; 9 | import co.joebirch.braillebox.injection.module.ActivityModule; 10 | 11 | /** 12 | * Base Activity implementation that provides activity with the following functionality: 13 | *

14 | * - Creation of Dagger components 15 | */ 16 | public abstract class BaseActivity extends Activity { 17 | 18 | private ActivityComponent mActivityComponent; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | } 24 | 25 | public ActivityComponent activityComponent() { 26 | if (mActivityComponent == null) { 27 | mActivityComponent = DaggerActivityComponent.builder() 28 | .activityModule(new ActivityModule(this)) 29 | .applicationComponent(BrailleBoxApplication.get(this).getComponent()) 30 | .build(); 31 | } 32 | return mActivityComponent; 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.base; 2 | 3 | /** 4 | * Base Presenter class that implementing the Presenter interface. It provides a base implementation 5 | * for both the attachView() and detachView() lifecycle methods. Child classes can retrieve a 6 | * reference to the mvpView by calling the defined getMvpView() method. 7 | */ 8 | public class BasePresenter implements Presenter { 9 | 10 | private T mvpView; 11 | 12 | @Override 13 | public void attachView(T mvpView) { 14 | this.mvpView = mvpView; 15 | } 16 | 17 | @Override 18 | public void detachView() { 19 | mvpView = null; 20 | } 21 | 22 | public boolean isViewAttached() { 23 | return mvpView != null; 24 | } 25 | 26 | public T getMvpView() { 27 | return mvpView; 28 | } 29 | 30 | public void checkViewAttached() { 31 | if (!isViewAttached()) throw new MvpViewNotAttachedException(); 32 | } 33 | 34 | public static class MvpViewNotAttachedException extends RuntimeException { 35 | public MvpViewNotAttachedException() { 36 | super("Please call Presenter.attachView(MvpView) before" + 37 | " requesting data to the Presenter"); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/base/MvpView.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.base; 2 | 3 | /** 4 | * Interface class to act as a base for any class that is to take the role of the View in the Model- 5 | * View-Presenter pattern. 6 | */ 7 | public interface MvpView { 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/base/Presenter.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.base; 2 | 3 | /** 4 | * Interface class to act as a base for any class that is to take the role of the Presenter in the 5 | * Model-View-Presenter pattern. 6 | */ 7 | public interface Presenter { 8 | 9 | void attachView(V mvpView); 10 | 11 | void detachView(); 12 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.main; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.google.android.things.contrib.driver.button.Button; 6 | 7 | import javax.inject.Inject; 8 | 9 | import co.joebirch.braillebox.ui.base.BaseActivity; 10 | import co.joebirch.braillebox.util.GpioHelper; 11 | 12 | public class MainActivity extends BaseActivity implements MainMvpView { 13 | 14 | private static final int DEFAULT_DELAY = 500; 15 | private static final String DEFAULT_SOLENOID_STATE = "111111"; 16 | 17 | @Inject GpioHelper gpioHelper; 18 | @Inject MainPresenter mainPresenter; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | 24 | activityComponent().inject(this); 25 | mainPresenter.attachView(this); 26 | 27 | gpioHelper.openPushButtonGpioPin(buttonCallback); 28 | gpioHelper.openSolenoidGpioPins(); 29 | } 30 | 31 | @Override 32 | public void showSequence(String sequence) { 33 | gpioHelper.sendGpioValues(sequence); 34 | } 35 | 36 | @Override 37 | public void resetSolenoids() { 38 | gpioHelper.sendGpioValues(DEFAULT_SOLENOID_STATE); 39 | } 40 | 41 | private Button.OnButtonEventListener buttonCallback = 42 | new Button.OnButtonEventListener() { 43 | @Override 44 | public void onButtonEvent(Button button, boolean pressed) { 45 | if (pressed) { 46 | mainPresenter.fetchArticle(DEFAULT_DELAY); 47 | } 48 | } 49 | }; 50 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/main/MainMvpView.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.main; 2 | 3 | import co.joebirch.braillebox.ui.base.MvpView; 4 | 5 | interface MainMvpView extends MvpView { 6 | 7 | void showSequence(String sequence); 8 | 9 | void resetSolenoids(); 10 | 11 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/ui/main/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.main; 2 | 3 | import javax.inject.Inject; 4 | 5 | import co.joebirch.braillebox.data.DataManager; 6 | import co.joebirch.braillebox.ui.base.BasePresenter; 7 | import io.reactivex.Observer; 8 | import io.reactivex.android.schedulers.AndroidSchedulers; 9 | import io.reactivex.disposables.Disposable; 10 | import io.reactivex.schedulers.Schedulers; 11 | import timber.log.Timber; 12 | 13 | public class MainPresenter extends BasePresenter { 14 | 15 | @Inject DataManager dataManager; 16 | 17 | private Disposable articleDisposable; 18 | 19 | @Inject 20 | MainPresenter(DataManager dataManager) { 21 | this.dataManager = dataManager; 22 | } 23 | 24 | @Override 25 | public void detachView() { 26 | super.detachView(); 27 | cancelCurrentSubscription(); 28 | } 29 | 30 | void fetchArticle(int delay) { 31 | cancelCurrentSubscription(); 32 | getMvpView().resetSolenoids(); 33 | dataManager.getArticle(delay) 34 | .observeOn(AndroidSchedulers.mainThread()) 35 | .subscribeOn(Schedulers.io()) 36 | .subscribe(new Observer() { 37 | @Override 38 | public void onSubscribe(Disposable disposable) { 39 | articleDisposable = disposable; 40 | } 41 | 42 | @Override 43 | public void onNext(String sequence) { 44 | getMvpView().showSequence(sequence); 45 | } 46 | 47 | @Override 48 | public void onError(Throwable error) { 49 | Timber.e(error, "There was a problem fetching the news article..."); 50 | } 51 | 52 | @Override 53 | public void onComplete() { 54 | 55 | } 56 | }); 57 | } 58 | 59 | private void cancelCurrentSubscription() { 60 | if (articleDisposable != null) articleDisposable.dispose(); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/util/BoardDefaults.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.util; 2 | 3 | import android.os.Build; 4 | 5 | import com.google.android.things.pio.PeripheralManagerService; 6 | 7 | import java.util.List; 8 | 9 | class BoardDefaults { 10 | 11 | private static final String DEVICE_EDISON_ARDUINO = "edison_arduino"; 12 | private static final String DEVICE_EDISON = "edison"; 13 | private static final String DEVICE_RPI3 = "rpi3"; 14 | private static final String DEVICE_NXP = "imx6ul"; 15 | private static String sBoardVariant = ""; 16 | 17 | static String getFirstSolenoidGpioPin() { 18 | switch (getBoardVariant()) { 19 | case DEVICE_EDISON_ARDUINO: 20 | return ""; 21 | case DEVICE_EDISON: 22 | return ""; 23 | case DEVICE_RPI3: 24 | return "BCM17"; 25 | case DEVICE_NXP: 26 | return ""; 27 | default: 28 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 29 | } 30 | } 31 | 32 | static String getSecondSolenoidGpioPin() { 33 | switch (getBoardVariant()) { 34 | case DEVICE_EDISON_ARDUINO: 35 | return ""; 36 | case DEVICE_EDISON: 37 | return ""; 38 | case DEVICE_RPI3: 39 | return "BCM27"; 40 | case DEVICE_NXP: 41 | return ""; 42 | default: 43 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 44 | } 45 | } 46 | 47 | static String getThirdSolenoidGpioPin() { 48 | switch (getBoardVariant()) { 49 | case DEVICE_EDISON_ARDUINO: 50 | return ""; 51 | case DEVICE_EDISON: 52 | return ""; 53 | case DEVICE_RPI3: 54 | return "BCM22"; 55 | case DEVICE_NXP: 56 | return ""; 57 | default: 58 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 59 | } 60 | } 61 | 62 | static String getFourthSolenoidGpioPin() { 63 | switch (getBoardVariant()) { 64 | case DEVICE_EDISON_ARDUINO: 65 | return ""; 66 | case DEVICE_EDISON: 67 | return ""; 68 | case DEVICE_RPI3: 69 | return "BCM23"; 70 | case DEVICE_NXP: 71 | return ""; 72 | default: 73 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 74 | } 75 | } 76 | 77 | static String getFifthSolenoidGpioPin() { 78 | switch (getBoardVariant()) { 79 | case DEVICE_EDISON_ARDUINO: 80 | return ""; 81 | case DEVICE_EDISON: 82 | return ""; 83 | case DEVICE_RPI3: 84 | return "BCM16"; 85 | case DEVICE_NXP: 86 | return ""; 87 | default: 88 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 89 | } 90 | } 91 | 92 | static String getSixthSolenoidGpioPin() { 93 | switch (getBoardVariant()) { 94 | case DEVICE_EDISON_ARDUINO: 95 | return ""; 96 | case DEVICE_EDISON: 97 | return ""; 98 | case DEVICE_RPI3: 99 | return "BCM25"; 100 | case DEVICE_NXP: 101 | return ""; 102 | default: 103 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 104 | } 105 | } 106 | 107 | static String getPushButtonGpioPin() { 108 | switch (getBoardVariant()) { 109 | case DEVICE_EDISON_ARDUINO: 110 | return ""; 111 | case DEVICE_EDISON: 112 | return ""; 113 | case DEVICE_RPI3: 114 | return "BCM12"; 115 | case DEVICE_NXP: 116 | return ""; 117 | default: 118 | throw new IllegalArgumentException("Unknown device: " + Build.DEVICE); 119 | } 120 | } 121 | 122 | private static String getBoardVariant() { 123 | if (!sBoardVariant.isEmpty()) return sBoardVariant; 124 | 125 | sBoardVariant = Build.DEVICE; 126 | if (sBoardVariant.equals(DEVICE_EDISON)) { 127 | PeripheralManagerService pioService = new PeripheralManagerService(); 128 | List gpioList = pioService.getGpioList(); 129 | if (gpioList.size() != 0) { 130 | String pin = gpioList.get(0); 131 | if (pin.startsWith("IO")) { 132 | sBoardVariant = DEVICE_EDISON_ARDUINO; 133 | } 134 | } 135 | } 136 | return sBoardVariant; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/util/BrailleMapper.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import javax.inject.Inject; 7 | 8 | import co.joebirch.braillebox.data.Braille; 9 | 10 | public class BrailleMapper { 11 | 12 | @Inject 13 | BrailleMapper() { 14 | 15 | } 16 | 17 | public List mapFromWords(String... words) { 18 | List braille = new ArrayList<>(); 19 | for (int i = 0; i < words.length; i++) { 20 | braille.addAll(mapFromString(words[i])); 21 | if (i < words.length - 1) braille.add(Braille.SPACE.getValue()); 22 | } 23 | return braille; 24 | } 25 | 26 | public List mapFromString(String text) { 27 | List braille = new ArrayList<>(); 28 | for (int i = 0; i < text.length(); i++) { 29 | braille.add(Braille.fromKey( 30 | Character.toLowerCase(text.charAt(i))).getValue()); 31 | } 32 | return braille; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/co/joebirch/braillebox/util/GpioHelper.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.util; 2 | 3 | import com.google.android.things.contrib.driver.button.Button; 4 | import com.google.android.things.pio.Gpio; 5 | import com.google.android.things.pio.PeripheralManagerService; 6 | 7 | import java.io.IOException; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import javax.inject.Inject; 12 | 13 | import timber.log.Timber; 14 | 15 | import static com.google.android.things.pio.Gpio.ACTIVE_HIGH; 16 | import static com.google.android.things.pio.Gpio.DIRECTION_OUT_INITIALLY_LOW; 17 | 18 | public class GpioHelper { 19 | 20 | private List solenoids; 21 | private PeripheralManagerService peripheralManagerService; 22 | 23 | @Inject 24 | GpioHelper(PeripheralManagerService peripheralManagerService) { 25 | this.peripheralManagerService = peripheralManagerService; 26 | solenoids = new ArrayList<>(); 27 | } 28 | 29 | public void openPushButtonGpioPin(Button.OnButtonEventListener buttonCallback) { 30 | try { 31 | Button button = new Button(BoardDefaults.getPushButtonGpioPin(), 32 | Button.LogicState.PRESSED_WHEN_LOW); 33 | 34 | button.setOnButtonEventListener(buttonCallback); 35 | } catch (IOException error) { 36 | Timber.e(error, "There was an error configuring the push button GPIO pin"); 37 | } 38 | } 39 | 40 | public void openSolenoidGpioPins() { 41 | solenoids.add(configureGpioPin(BoardDefaults.getFirstSolenoidGpioPin())); 42 | solenoids.add(configureGpioPin(BoardDefaults.getSecondSolenoidGpioPin())); 43 | solenoids.add(configureGpioPin(BoardDefaults.getThirdSolenoidGpioPin())); 44 | solenoids.add(configureGpioPin(BoardDefaults.getFourthSolenoidGpioPin())); 45 | solenoids.add(configureGpioPin(BoardDefaults.getFifthSolenoidGpioPin())); 46 | solenoids.add(configureGpioPin(BoardDefaults.getSixthSolenoidGpioPin())); 47 | } 48 | 49 | private Gpio configureGpioPin(String pin) { 50 | Gpio gpioPin = null; 51 | try { 52 | gpioPin = peripheralManagerService.openGpio(pin); 53 | gpioPin.setDirection(DIRECTION_OUT_INITIALLY_LOW); 54 | gpioPin.setActiveType(ACTIVE_HIGH); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | return gpioPin; 59 | } 60 | 61 | public void sendGpioValues(String sequence) { 62 | for (int i = 0; i < solenoids.size(); i++) { 63 | try { 64 | solenoids.get(i).setValue((int) sequence.charAt(i) == '1'); 65 | } catch (IOException error) { 66 | Timber.e(error, "There was an error configuring GPIO pins"); 67 | } 68 | } 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | private static final int SEQUENCE_A = 100000; 9 | private static final int SEQUENCE_B = 101000; 10 | private static final int SEQUENCE_C = 110000; 11 | private static final int SEQUENCE_D = 110100; 12 | private static final int SEQUENCE_E = 100100; 13 | private static final int SEQUENCE_F = 111000; 14 | private static final int SEQUENCE_G = 111100; 15 | private static final int SEQUENCE_H = 101100; 16 | private static final int SEQUENCE_I = 010100; 17 | private static final int SEQUENCE_J = 011100; 18 | private static final int SEQUENCE_K = 100010; 19 | private static final int SEQUENCE_L = 101010; 20 | private static final int SEQUENCE_M = 110010; 21 | private static final int SEQUENCE_N = 110110; 22 | private static final int SEQUENCE_O = 100110; 23 | private static final int SEQUENCE_P = 111010; 24 | private static final int SEQUENCE_Q = 111110; 25 | private static final int SEQUENCE_R = 101110; 26 | private static final int SEQUENCE_S = 011010; 27 | private static final int SEQUENCE_T = 011110; 28 | private static final int SEQUENCE_U = 100011; 29 | private static final int SEQUENCE_V = 101011; 30 | private static final int SEQUENCE_W = 011101; 31 | private static final int SEQUENCE_X = 110011; 32 | private static final int SEQUENCE_Y = 110111; 33 | private static final int SEQUENCE_Z = 100111; 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | BrailleBox 3 | co.joebirch.braillebox 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/data/DataManagerTest.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.data; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.junit.MockitoJUnitRunner; 9 | 10 | import java.util.List; 11 | import java.util.concurrent.Callable; 12 | 13 | import co.joebirch.braillebox.data.model.NewsResponse; 14 | import co.joebirch.braillebox.data.remote.BrailleBoxService; 15 | import co.joebirch.braillebox.util.BrailleMapper; 16 | import co.joebirch.braillebox.test.util.TestDataFactory; 17 | import io.reactivex.Observable; 18 | import io.reactivex.Scheduler; 19 | import io.reactivex.android.plugins.RxAndroidPlugins; 20 | import io.reactivex.functions.Function; 21 | import io.reactivex.observers.TestObserver; 22 | import io.reactivex.schedulers.Schedulers; 23 | 24 | import static org.mockito.Matchers.anyString; 25 | import static org.mockito.Mockito.never; 26 | import static org.mockito.Mockito.verify; 27 | import static org.mockito.Mockito.when; 28 | 29 | /** 30 | * Tests for DataManager methods related to retrieving/updating content (articles, videos, etc.) 31 | */ 32 | @RunWith(MockitoJUnitRunner.class) 33 | public class DataManagerTest { 34 | 35 | @Mock BrailleBoxService mockBrailleBoxService; 36 | @Mock BrailleMapper mockBrailleMapper; 37 | private DataManager dataManager; 38 | 39 | @Before 40 | public void setUp() { 41 | dataManager = new DataManager(mockBrailleBoxService, mockBrailleMapper); 42 | } 43 | 44 | @BeforeClass 45 | public static void setupClass() { 46 | RxAndroidPlugins.setInitMainThreadSchedulerHandler( 47 | new Function, Scheduler>() { 48 | @Override 49 | public Scheduler apply(Callable schedulerCallable) throws Exception { 50 | return Schedulers.trampoline(); 51 | } 52 | }); 53 | } 54 | 55 | @Test 56 | public void getArticlesRetrievesArticlesFromSource() { 57 | NewsResponse newsResponse = TestDataFactory.makeNewsResponse(); 58 | stubBrailleBoxServiceGetArticle(Observable.just(newsResponse)); 59 | 60 | dataManager.getArticle(TestDataFactory.randomInt()); 61 | 62 | verify(mockBrailleBoxService).getArticles(Source.BBC_NEWS.getId(), 63 | SortBy.TOP.getLabel()); 64 | } 65 | 66 | @Test 67 | public void getArticlesReceivesNullArticlesAndDoesntMapResult() { 68 | NewsResponse newsResponse = TestDataFactory.makeNewsResponse(); 69 | newsResponse.articles = null; 70 | stubBrailleBoxServiceGetArticle(Observable.just(newsResponse)); 71 | 72 | dataManager.getArticle(TestDataFactory.randomInt()); 73 | 74 | verify(mockBrailleMapper, never()).mapFromString(anyString()); 75 | } 76 | 77 | @Test 78 | public void getArticlesReceivesEmptyArticlesAndDoesntMapResult() { 79 | NewsResponse newsResponse = TestDataFactory.makeNewsResponse(); 80 | newsResponse.articles.clear(); 81 | stubBrailleBoxServiceGetArticle(Observable.just(newsResponse)); 82 | 83 | dataManager.getArticle(TestDataFactory.randomInt()); 84 | 85 | verify(mockBrailleMapper, never()).mapFromString(anyString()); 86 | } 87 | 88 | @Test 89 | public void getArticlesDoesntEmitResultWhenResponseHasError() { 90 | NewsResponse newsResponse = TestDataFactory.makeNewsResponse(); 91 | stubBrailleBoxServiceGetArticle(Observable.just(newsResponse)); 92 | 93 | dataManager.getArticle(TestDataFactory.randomInt()); 94 | 95 | verify(mockBrailleMapper, never()).mapFromString(anyString()); 96 | } 97 | 98 | @Test 99 | public void getArticlesCompletesAndEmitsArticle() { 100 | NewsResponse newsResponse = TestDataFactory.makeNewsResponse(); 101 | stubBrailleBoxServiceGetArticle(Observable.just(newsResponse)); 102 | List braille = TestDataFactory.makeBrailleList(5); 103 | stubBrailleMapperMapFromSequences(braille); 104 | 105 | TestObserver testSubscriber = dataManager 106 | .getArticle(TestDataFactory.randomInt()).test(); 107 | testSubscriber.awaitTerminalEvent(); 108 | testSubscriber.assertComplete().assertValues( 109 | braille.toArray(new String[braille.size()])); 110 | } 111 | 112 | /****** Helper methods *******/ 113 | 114 | private void stubBrailleBoxServiceGetArticle(Observable observable) { 115 | when(mockBrailleBoxService.getArticles(anyString(), anyString())) 116 | .thenReturn(observable); 117 | } 118 | 119 | private void stubBrailleMapperMapFromSequences(List braille) { 120 | when(mockBrailleMapper.mapFromWords(anyString(), anyString())) 121 | .thenReturn(braille); 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/injection/component/TestComponent.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.component; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import co.joebirch.braillebox.injection.module.ApplicationTestModule; 6 | import dagger.Component; 7 | 8 | @Singleton 9 | @Component(modules = ApplicationTestModule.class) 10 | public interface TestComponent extends ApplicationComponent { 11 | 12 | } -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/injection/module/ApplicationTestModule.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.injection.module; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import co.joebirch.braillebox.data.DataManager; 9 | import co.joebirch.braillebox.injection.scope.ApplicationContext; 10 | import dagger.Module; 11 | import dagger.Provides; 12 | 13 | import static org.mockito.Mockito.mock; 14 | 15 | /** 16 | * Provides application-level dependencies for an app running on a testing environment 17 | * This allows injecting mocks if necessary. 18 | */ 19 | @Module 20 | public class ApplicationTestModule { 21 | private final Application mApplication; 22 | 23 | public ApplicationTestModule(Application application) { 24 | mApplication = application; 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | Application provideApplication() { 30 | return mApplication; 31 | } 32 | 33 | @Provides 34 | @ApplicationContext 35 | Context provideContext() { 36 | return mApplication; 37 | } 38 | 39 | /************* MOCKS *************/ 40 | 41 | @Provides 42 | @Singleton 43 | DataManager providesDataManager() { 44 | return mock(DataManager.class); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/test/util/TestDataFactory.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.test.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Random; 6 | import java.util.UUID; 7 | 8 | import co.joebirch.braillebox.data.model.ArticleModel; 9 | import co.joebirch.braillebox.data.model.NewsResponse; 10 | 11 | public class TestDataFactory { 12 | 13 | private static final Random sRandom = new Random(); 14 | 15 | public static String randomUuid() { 16 | return UUID.randomUUID().toString(); 17 | } 18 | 19 | public static int randomInt() { 20 | return sRandom.nextInt(200) + Integer.SIZE - 1; 21 | } 22 | 23 | public static NewsResponse makeNewsResponse() { 24 | NewsResponse newsResponse = new NewsResponse(); 25 | newsResponse.source = randomUuid(); 26 | newsResponse.articles = makeArticleModelsList(4); 27 | return newsResponse; 28 | } 29 | 30 | public static List makeBrailleList(int count) { 31 | List braille = new ArrayList<>(); 32 | for (int i = 0; i < count; i++) { 33 | braille.add(randomUuid()); 34 | } 35 | return braille; 36 | } 37 | 38 | private static ArticleModel makeArticleModel() { 39 | ArticleModel articleModel = new ArticleModel(); 40 | articleModel.author = randomUuid(); 41 | articleModel.description = randomUuid(); 42 | articleModel.title = randomUuid(); 43 | return articleModel; 44 | } 45 | 46 | private static List makeArticleModelsList(int count) { 47 | List articleModels = new ArrayList<>(); 48 | for (int i = 0; i < count; i++) { 49 | articleModels.add(makeArticleModel()); 50 | } 51 | return articleModels; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/ui/main/MainPresenterTest.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.ui.main; 2 | 3 | import org.junit.Before; 4 | import org.junit.BeforeClass; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.InOrder; 8 | import org.mockito.Mock; 9 | import org.mockito.Mockito; 10 | import org.mockito.junit.MockitoJUnitRunner; 11 | 12 | import java.util.concurrent.Callable; 13 | 14 | import co.joebirch.braillebox.data.DataManager; 15 | import co.joebirch.braillebox.test.util.TestDataFactory; 16 | import io.reactivex.Observable; 17 | import io.reactivex.Scheduler; 18 | import io.reactivex.android.plugins.RxAndroidPlugins; 19 | import io.reactivex.functions.Function; 20 | import io.reactivex.schedulers.Schedulers; 21 | 22 | import static org.mockito.Matchers.anyInt; 23 | import static org.mockito.Matchers.anyString; 24 | import static org.mockito.Mockito.never; 25 | import static org.mockito.Mockito.verify; 26 | import static org.mockito.Mockito.when; 27 | 28 | @RunWith(MockitoJUnitRunner.class) 29 | public class MainPresenterTest { 30 | 31 | private MainPresenter mainPresenter; 32 | @Mock DataManager dataManager; 33 | @Mock MainMvpView mockMainMvpView; 34 | 35 | @Before 36 | public void setup() { 37 | mainPresenter = new MainPresenter(dataManager); 38 | mainPresenter.attachView(mockMainMvpView); 39 | } 40 | 41 | @BeforeClass 42 | public static void setupClass() { 43 | RxAndroidPlugins.setInitMainThreadSchedulerHandler( 44 | new Function, Scheduler>() { 45 | @Override 46 | public Scheduler apply(Callable schedulerCallable) throws Exception { 47 | return Schedulers.trampoline(); 48 | } 49 | }); 50 | } 51 | 52 | @Test 53 | public void getArticleShowsSequenceAfterResettingSolenoids() { 54 | String sequence = TestDataFactory.randomUuid(); 55 | stubDataManagerGetArticle(Observable.just(sequence)); 56 | 57 | mainPresenter.fetchArticle(TestDataFactory.randomInt()); 58 | 59 | InOrder inOrder = Mockito.inOrder(mockMainMvpView); 60 | inOrder.verify(mockMainMvpView).resetSolenoids(); 61 | inOrder.verify(mockMainMvpView).showSequence(sequence); 62 | } 63 | 64 | @Test 65 | public void getArticleFails() { 66 | stubDataManagerGetArticle(Observable.error(new RuntimeException())); 67 | 68 | mainPresenter.fetchArticle(TestDataFactory.randomInt()); 69 | 70 | verify(mockMainMvpView, never()).showSequence(anyString()); 71 | } 72 | 73 | private void stubDataManagerGetArticle(Observable sequence) { 74 | when(dataManager.getArticle(anyInt())) 75 | .thenReturn(sequence); 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /app/src/test/java/co/joebirch/braillebox/util/BrailleMapperTest.java: -------------------------------------------------------------------------------- 1 | package co.joebirch.braillebox.util; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import co.joebirch.braillebox.data.Braille; 10 | 11 | import static junit.framework.Assert.assertEquals; 12 | 13 | public class BrailleMapperTest { 14 | 15 | private BrailleMapper brailleMapper; 16 | 17 | @Before 18 | public void setup() { 19 | brailleMapper = new BrailleMapper(); 20 | } 21 | 22 | @Test 23 | public void mapsSingleWordToBrailleRepresentatino() throws Exception { 24 | String word = "hello"; 25 | List brailleSequence = new ArrayList<>(); 26 | brailleSequence.add(Braille.H.getValue()); 27 | brailleSequence.add(Braille.E.getValue()); 28 | brailleSequence.add(Braille.L.getValue()); 29 | brailleSequence.add(Braille.L.getValue()); 30 | brailleSequence.add(Braille.O.getValue()); 31 | 32 | List braille = brailleMapper.mapFromString(word); 33 | 34 | assertEquals(brailleSequence, braille); 35 | } 36 | 37 | @Test 38 | public void mapsAlphabetToBrailleRepresentatino() throws Exception { 39 | String word = "wxyzabcdstuvmnopijklqrefgh"; 40 | List brailleSequence = new ArrayList<>(); 41 | brailleSequence.add(Braille.W.getValue()); 42 | brailleSequence.add(Braille.X.getValue()); 43 | brailleSequence.add(Braille.Y.getValue()); 44 | brailleSequence.add(Braille.Z.getValue()); 45 | brailleSequence.add(Braille.A.getValue()); 46 | brailleSequence.add(Braille.B.getValue()); 47 | brailleSequence.add(Braille.C.getValue()); 48 | brailleSequence.add(Braille.D.getValue()); 49 | brailleSequence.add(Braille.S.getValue()); 50 | brailleSequence.add(Braille.T.getValue()); 51 | brailleSequence.add(Braille.U.getValue()); 52 | brailleSequence.add(Braille.V.getValue()); 53 | brailleSequence.add(Braille.M.getValue()); 54 | brailleSequence.add(Braille.N.getValue()); 55 | brailleSequence.add(Braille.O.getValue()); 56 | brailleSequence.add(Braille.P.getValue()); 57 | brailleSequence.add(Braille.I.getValue()); 58 | brailleSequence.add(Braille.J.getValue()); 59 | brailleSequence.add(Braille.K.getValue()); 60 | brailleSequence.add(Braille.L.getValue()); 61 | brailleSequence.add(Braille.Q.getValue()); 62 | brailleSequence.add(Braille.R.getValue()); 63 | brailleSequence.add(Braille.E.getValue()); 64 | brailleSequence.add(Braille.F.getValue()); 65 | brailleSequence.add(Braille.G.getValue()); 66 | brailleSequence.add(Braille.H.getValue()); 67 | 68 | List braille = brailleMapper.mapFromString(word); 69 | 70 | assertEquals(brailleSequence, braille); 71 | } 72 | 73 | @Test 74 | public void mapsNumericalStringToBrailleRepresentatino() throws Exception { 75 | String word = "1574"; 76 | List brailleSequence = new ArrayList<>(); 77 | brailleSequence.add(Braille.ONE.getValue()); 78 | brailleSequence.add(Braille.FIVE.getValue()); 79 | brailleSequence.add(Braille.SEVEN.getValue()); 80 | brailleSequence.add(Braille.FOUR.getValue()); 81 | 82 | List braille = brailleMapper.mapFromString(word); 83 | 84 | assertEquals(brailleSequence, braille); 85 | } 86 | 87 | @Test 88 | public void mapsAllNumbersStringToBrailleRepresentatino() throws Exception { 89 | String word = "6701458923"; 90 | List brailleSequence = new ArrayList<>(); 91 | brailleSequence.add(Braille.SIX.getValue()); 92 | brailleSequence.add(Braille.SEVEN.getValue()); 93 | brailleSequence.add(Braille.ZERO.getValue()); 94 | brailleSequence.add(Braille.ONE.getValue()); 95 | brailleSequence.add(Braille.FOUR.getValue()); 96 | brailleSequence.add(Braille.FIVE.getValue()); 97 | brailleSequence.add(Braille.EIGHT.getValue()); 98 | brailleSequence.add(Braille.NINE.getValue()); 99 | brailleSequence.add(Braille.TWO.getValue()); 100 | brailleSequence.add(Braille.THREE.getValue()); 101 | 102 | List braille = brailleMapper.mapFromString(word); 103 | 104 | assertEquals(brailleSequence, braille); 105 | } 106 | 107 | @Test 108 | public void mapsSpecialCharactersStringToBrailleRepresentatino() throws Exception { 109 | String word = ".,?!"; 110 | List brailleSequence = new ArrayList<>(); 111 | brailleSequence.add(Braille.PERIOD.getValue()); 112 | brailleSequence.add(Braille.COMMA.getValue()); 113 | brailleSequence.add(Braille.QUESTION_MARK.getValue()); 114 | brailleSequence.add(Braille.EXCLAMATION_MARK.getValue()); 115 | 116 | List braille = brailleMapper.mapFromString(word); 117 | 118 | assertEquals(brailleSequence, braille); 119 | } 120 | 121 | @Test 122 | public void mapsAllSpecialCharactersStringToBrailleRepresentatino() throws Exception { 123 | String word = ".#,?:!;"; 124 | List brailleSequence = new ArrayList<>(); 125 | brailleSequence.add(Braille.PERIOD.getValue()); 126 | brailleSequence.add(Braille.HASHTAG.getValue()); 127 | brailleSequence.add(Braille.COMMA.getValue()); 128 | brailleSequence.add(Braille.QUESTION_MARK.getValue()); 129 | brailleSequence.add(Braille.COLON.getValue()); 130 | brailleSequence.add(Braille.EXCLAMATION_MARK.getValue()); 131 | brailleSequence.add(Braille.SEMICOLON.getValue()); 132 | 133 | List braille = brailleMapper.mapFromString(word); 134 | 135 | assertEquals(brailleSequence, braille); 136 | } 137 | 138 | @Test 139 | public void mapsMixedCharacterStringToBrailleRepresentatino() throws Exception { 140 | String word = "hi?76!there,joe."; 141 | List brailleSequence = new ArrayList<>(); 142 | brailleSequence.add(Braille.H.getValue()); 143 | brailleSequence.add(Braille.I.getValue()); 144 | brailleSequence.add(Braille.QUESTION_MARK.getValue()); 145 | brailleSequence.add(Braille.SEVEN.getValue()); 146 | brailleSequence.add(Braille.SIX.getValue()); 147 | brailleSequence.add(Braille.EXCLAMATION_MARK.getValue()); 148 | brailleSequence.add(Braille.T.getValue()); 149 | brailleSequence.add(Braille.H.getValue()); 150 | brailleSequence.add(Braille.E.getValue()); 151 | brailleSequence.add(Braille.R.getValue()); 152 | brailleSequence.add(Braille.E.getValue()); 153 | brailleSequence.add(Braille.COMMA.getValue()); 154 | brailleSequence.add(Braille.J.getValue()); 155 | brailleSequence.add(Braille.O.getValue()); 156 | brailleSequence.add(Braille.E.getValue()); 157 | brailleSequence.add(Braille.PERIOD.getValue()); 158 | 159 | List braille = brailleMapper.mapFromString(word); 160 | 161 | assertEquals(brailleSequence, braille); 162 | } 163 | 164 | } -------------------------------------------------------------------------------- /brailleBox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/brailleBox.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.3.0' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | apiUrl=https://newsapi.org/v1/ 19 | apiKey=your-api-key -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 19 18:30:19 CET 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /schematics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hitherejoe/BrailleBox/604e1981f860fd44d07e583f05165f179b112f4f/schematics.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------