├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── .travis.yml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pddstudio │ │ └── orlyandroid │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pddstudio │ │ │ └── orlyandroid │ │ │ ├── AboutActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ResultActivity.java │ │ │ ├── adapters │ │ │ └── CoverImageAdapter.java │ │ │ ├── enums │ │ │ ├── Type.java │ │ │ └── UrlType.java │ │ │ ├── fragments │ │ │ ├── ColorPickerFragment.java │ │ │ ├── CoverImagePickerFragment.java │ │ │ └── SingleTextFragment.java │ │ │ └── utils │ │ │ ├── BuilderUtil.java │ │ │ ├── GenerationUtils.java │ │ │ ├── NetworkUtils.java │ │ │ └── StorageManager.java │ └── res │ │ ├── drawable │ │ ├── ic_close_24dp.xml │ │ ├── ic_help_outline_24dp.xml │ │ ├── ic_open_in_new_24dp.xml │ │ ├── ic_settings_24dp.xml │ │ └── ic_settings_backup_restore_24dp.xml │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_main.xml │ │ ├── activity_result.xml │ │ ├── fragment_dialog_picker.xml │ │ ├── fragment_single_text.xml │ │ └── item_image.xml │ │ ├── menu │ │ ├── menu_main.xml │ │ └── menu_result.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-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── library_strings.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── filepaths.xml │ └── test │ └── java │ └── com │ └── pddstudio │ └── orlyandroid │ └── ExampleUnitTest.java ├── build.gradle ├── gfx └── preview.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── o-rly-generator ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pddstudio │ │ └── orly │ │ └── book │ │ └── generator │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── images │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 16.png │ │ │ ├── 17.png │ │ │ ├── 18.png │ │ │ ├── 19.png │ │ │ ├── 2.png │ │ │ ├── 20.png │ │ │ ├── 21.png │ │ │ ├── 22.png │ │ │ ├── 23.png │ │ │ ├── 24.png │ │ │ ├── 25.png │ │ │ ├── 26.png │ │ │ ├── 27.png │ │ │ ├── 28.png │ │ │ ├── 29.png │ │ │ ├── 3.png │ │ │ ├── 30.png │ │ │ ├── 31.png │ │ │ ├── 32.png │ │ │ ├── 33.png │ │ │ ├── 34.png │ │ │ ├── 35.png │ │ │ ├── 36.png │ │ │ ├── 37.png │ │ │ ├── 38.png │ │ │ ├── 39.png │ │ │ ├── 4.png │ │ │ ├── 40.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ ├── java │ │ └── com │ │ │ └── pddstudio │ │ │ └── orly │ │ │ └── book │ │ │ └── generator │ │ │ ├── Book.java │ │ │ ├── BookGenerator.java │ │ │ ├── enums │ │ │ ├── CoverColor.java │ │ │ ├── CoverImage.java │ │ │ └── GuideTextPosition.java │ │ │ └── utils │ │ │ └── BookUtils.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── pddstudio │ └── orly │ └── book │ └── generator │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | O-Rly-Android -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: false 4 | android: 5 | components: 6 | - tools 7 | - platform-tools 8 | - build-tools-24.0.1 9 | - android-24 10 | - extra-android-support 11 | - extra-android-m2repository 12 | - extra-google-m2repository 13 | - sys-img-armeabi-v7a-android-24 14 | 15 | licenses: 16 | - '.+' 17 | env: 18 | global: 19 | # install timeout in minutes (2 minutes by default) 20 | - ADB_INSTALL_TIMEOUT=8 21 | 22 | # Emulator Management: Create, Start and Wait 23 | before_script: 24 | - echo no | android create avd --force -n test -t android-21 --abi armeabi-v7a 25 | - emulator -avd test -no-skin -no-audio -no-window & 26 | - android-wait-for-emulator 27 | - adb shell input keyevent 82 & 28 | 29 | script: 30 | - android list target 31 | - ./gradlew build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## O RLY Book Cover Generator 2 | 3 | [![Build Status](https://travis-ci.org/PDDStudio/O-Rly-Android.svg)](https://travis-ci.org/PDDStudio/O-Rly-Android) 4 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square)](https://www.apache.org/licenses/LICENSE-2.0.html) 5 | 6 | 7 | *Now on Android brought to you by [PDDStudio](https://github.com/PDDStudio)* 8 | 9 | ***Insult your co-workers with snarky O RLY parody book covers!*** 10 | 11 | ![Showcase](https://github.com/PDDStudio/O-Rly-Android/raw/master/gfx/preview.png) 12 | 13 | O-Rly-Android is a simple app which allows you to create O RLY parody covers on your Android phone. 14 | 15 | 16 | ##Disclaimer: 17 | 18 | I only created the Android-App for the [O RLY Cover Generator](https://dev.to/rly) and do not hold any special permission, except the agreement to bring this awesome site to Android. 19 | 20 | 21 | The site is hosted by [@ThePracticalDev](https://twitter.com/ThePracticalDev) and the original tool was created by [@AModelEngineer](https://twitter.com/AModelEngineer). 22 | 23 | ##Functions: 24 | 25 | - Create O Rly Book Covers with the same functions as on the Website 26 | - Share created Books immediately without saving them on your device 27 | - Save created Books to your device's storage to view and/or use them again 28 | - Android 6.0 Ready 29 | 30 | ##Notice 31 | There are a few known edge cases that do not generate. 32 | 33 | Some characters and long words won't work, etc. If you want to report an issue, feel free to do this in the [Issue Section](https://github.com/PDDStudio/O-Rly-Android/issues) 34 | 35 | ##Google Play 36 | 37 | ###Note: 38 | The App is currently *not* listed in the PlayStore, this will change in the next few days after collecting some Feedback. 39 | 40 | In meanwhile please download the APK from the [Release Section](https://github.com/PDDStudio/O-Rly-Android/releases) 41 | 42 |
43 | Get it on Google Play 44 | 45 | ##Contact 46 | 47 | Feel free to contact or follow me. 48 | 49 | - [Google+ Profile](https://plus.google.com/+PatrickJung42) 50 | - [GitHub Account](https://github.com/PDDStudio) 51 | - [Send me an E-Mail](mailto:patrick.pddstudio@gmail.com) 52 | 53 | ##License 54 | Copyright 2016 Patrick J 55 | 56 | Licensed under the Apache License, Version 2.0 (the "License"); 57 | you may not use this file except in compliance with the License. 58 | You may obtain a copy of the License at 59 | 60 | http://www.apache.org/licenses/LICENSE-2.0 61 | 62 | Unless required by applicable law or agreed to in writing, software 63 | distributed under the License is distributed on an "AS IS" BASIS, 64 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 65 | See the License for the specific language governing permissions and 66 | limitations under the License. -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-apt' 3 | apply plugin: 'me.tatarka.retrolambda' 4 | 5 | android { 6 | compileSdkVersion 24 7 | buildToolsVersion "24.0.1" 8 | 9 | defaultConfig { 10 | applicationId "com.pddstudio.orlyandroid" 11 | minSdkVersion 16 12 | targetSdkVersion 24 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | lintOptions { 27 | abortOnError false 28 | } 29 | } 30 | 31 | apt { 32 | arguments { 33 | androidManifestFile variant.outputs[0]?.processResources?.manifestFile 34 | // if you have multiple outputs (when using splits), you may want to have other index than 0 35 | 36 | // you should set your package name here if you are using different application IDs 37 | // resourcePackageName "your.package.name" 38 | 39 | // You can set optional annotation processing options here, like these commented options: 40 | // logLevel 'INFO' 41 | // logFile '/var/log/aa.log' 42 | } 43 | } 44 | 45 | dependencies { 46 | compile fileTree(include: ['*.jar'], dir: 'libs') 47 | testCompile 'junit:junit:4.12' 48 | 49 | compile 'com.android.support:appcompat-v7:24.1.1' 50 | compile 'com.android.support:cardview-v7:24.1.1' 51 | compile 'com.android.support:recyclerview-v7:24.1.1' 52 | compile 'com.android.support:design:24.1.1' 53 | 54 | apt "org.androidannotations:androidannotations:4.0.0" 55 | compile 'org.androidannotations:androidannotations-api:4.0.0' 56 | 57 | compile 'com.squareup.okhttp3:okhttp:3.4.1' 58 | compile 'com.squareup.picasso:picasso:2.5.2' 59 | 60 | compile 'com.afollestad.material-dialogs:core:0.9.0.1' 61 | compile 'com.afollestad.material-dialogs:commons:0.9.0.1' 62 | 63 | compile 'com.github.drozdzynski:Steppers:5a7b08a7fc' 64 | //compile 'com.github.PDDStudio:Steppers:0.0.2' 65 | 66 | compile('com.mikepenz:aboutlibraries:5.7.2@aar') { 67 | transitive = true 68 | } 69 | 70 | compile project(':o-rly-generator') 71 | } 72 | -------------------------------------------------------------------------------- /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/pddstudio/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 | -keep class .R 19 | -keep class **.R$* { 20 | ; 21 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pddstudio/orlyandroid/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 31 | 32 | 37 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid; 2 | 3 | import android.content.Context; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.Toolbar; 7 | 8 | import com.mikepenz.aboutlibraries.Libs; 9 | import com.mikepenz.aboutlibraries.LibsBuilder; 10 | 11 | import org.androidannotations.annotations.AfterViews; 12 | import org.androidannotations.annotations.EActivity; 13 | import org.androidannotations.annotations.OptionsItem; 14 | import org.androidannotations.annotations.ViewById; 15 | 16 | @EActivity(R.layout.activity_about) 17 | public class AboutActivity extends AppCompatActivity { 18 | 19 | public static void open(Context context) { 20 | AboutActivity_.intent(context).start(); 21 | } 22 | 23 | @ViewById(R.id.toolbar) 24 | Toolbar toolbar; 25 | 26 | @AfterViews 27 | void prepareLayout() { 28 | toolbar.setTitle(R.string.about_activity_title); 29 | setSupportActionBar(toolbar); 30 | if (getSupportActionBar() != null) { 31 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 32 | } 33 | Fragment fragment = new LibsBuilder().withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) 34 | .withFields(R.string.class.getFields()) 35 | .withExcludedLibraries("AndroidIconics", "fastadapter") 36 | .supportFragment(); 37 | getSupportFragmentManager().beginTransaction().replace(R.id.placeholder, fragment).commit(); 38 | } 39 | 40 | @OptionsItem(android.R.id.home) 41 | void onHomePressed() { 42 | onBackPressed(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid; 2 | 3 | import android.content.Context; 4 | import android.graphics.Rect; 5 | import android.support.annotation.ColorInt; 6 | import android.support.annotation.NonNull; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.inputmethod.InputMethodManager; 12 | import android.widget.EditText; 13 | 14 | import com.afollestad.materialdialogs.MaterialDialog; 15 | import com.afollestad.materialdialogs.color.ColorChooserDialog; 16 | import com.pddstudio.orly.book.generator.enums.CoverColor; 17 | import com.pddstudio.orlyandroid.enums.Type; 18 | import com.pddstudio.orlyandroid.enums.UrlType; 19 | import com.pddstudio.orlyandroid.fragments.ColorPickerFragment; 20 | import com.pddstudio.orlyandroid.fragments.CoverImagePickerFragment; 21 | import com.pddstudio.orlyandroid.fragments.SingleTextFragment; 22 | import com.pddstudio.orlyandroid.utils.BuilderUtil; 23 | import com.pddstudio.orlyandroid.utils.GenerationUtils; 24 | import com.pddstudio.orlyandroid.utils.NetworkUtils; 25 | import com.pddstudio.orlyandroid.utils.StorageManager; 26 | 27 | import org.androidannotations.annotations.AfterInject; 28 | import org.androidannotations.annotations.AfterViews; 29 | import org.androidannotations.annotations.Background; 30 | import org.androidannotations.annotations.Bean; 31 | import org.androidannotations.annotations.EActivity; 32 | import org.androidannotations.annotations.OptionsItem; 33 | import org.androidannotations.annotations.OptionsMenu; 34 | import org.androidannotations.annotations.UiThread; 35 | import org.androidannotations.annotations.ViewById; 36 | 37 | import java.io.File; 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | 41 | import me.drozdzynski.library.steppers.OnCancelAction; 42 | import me.drozdzynski.library.steppers.OnFinishAction; 43 | import me.drozdzynski.library.steppers.SteppersItem; 44 | import me.drozdzynski.library.steppers.SteppersView; 45 | 46 | @EActivity(R.layout.activity_main) 47 | @OptionsMenu(R.menu.menu_main) 48 | public class MainActivity extends AppCompatActivity implements OnFinishAction, OnCancelAction, ColorChooserDialog.ColorCallback, ColorPickerFragment.Callback { 49 | 50 | @Bean 51 | GenerationUtils generationUtils; 52 | 53 | @Bean 54 | BuilderUtil builderUtil; 55 | 56 | @Bean 57 | StorageManager storageManager; 58 | 59 | @Bean 60 | NetworkUtils networkUtils; 61 | 62 | @ViewById(R.id.toolbar) 63 | Toolbar toolbar; 64 | 65 | @ViewById(R.id.steppers_view) 66 | SteppersView steppersView; 67 | 68 | SteppersView.Config steppersViewConfig; 69 | MaterialDialog loadingDialog; 70 | 71 | @AfterInject 72 | void prepareApplication() { 73 | storageManager.cleanCacheDir(); 74 | } 75 | 76 | @AfterViews 77 | void prepareLayout() { 78 | setSupportActionBar(toolbar); 79 | steppersViewConfig = new SteppersView.Config(); 80 | steppersViewConfig.setOnFinishAction(this); 81 | steppersViewConfig.setOnCancelAction(this); 82 | steppersViewConfig.setFragmentManager(getSupportFragmentManager()); 83 | steppersView.setConfig(steppersViewConfig); 84 | steppersView.setItems(prepareSteps()); 85 | steppersView.build(); 86 | } 87 | 88 | private List prepareSteps() { 89 | List items = new ArrayList<>(); 90 | //Title item 91 | SteppersItem titleItem = new SteppersItem(); 92 | titleItem.setLabel(getString(R.string.step_title_title)); 93 | titleItem.setSubLabel(getString(R.string.step_title_summary)); 94 | titleItem.setFragment(SingleTextFragment.create(this, R.string.fragment_title_text, Type.TITLE)); 95 | items.add(titleItem); 96 | //SubTitle item 97 | SteppersItem subTitleItem = new SteppersItem(); 98 | subTitleItem.setLabel(getString(R.string.step_guide_text_title)); 99 | subTitleItem.setSubLabel(getString(R.string.step_guide_text_summary)); 100 | subTitleItem.setFragment(SingleTextFragment.create(this, R.string.fragment_sub_title_text, Type.GUIDE)); 101 | items.add(subTitleItem); 102 | //Top Title item 103 | SteppersItem topTitleItem = new SteppersItem(); 104 | topTitleItem.setLabel(getString(R.string.step_top_title_title)); 105 | topTitleItem.setSubLabel(getString(R.string.step_top_title_summary)); 106 | topTitleItem.setFragment(SingleTextFragment.create(this, R.string.fragment_top_text, Type.TOP_TITLE)); 107 | items.add(topTitleItem); 108 | //Author item 109 | SteppersItem authorItem = new SteppersItem(); 110 | authorItem.setLabel(getString(R.string.step_author_title)); 111 | authorItem.setSubLabel(getString(R.string.step_author_summary)); 112 | authorItem.setFragment(SingleTextFragment.create(this, R.string.fragment_author_text, Type.AUTHOR)); 113 | items.add(authorItem); 114 | //Color item 115 | SteppersItem colorItem = new SteppersItem(); 116 | colorItem.setLabel(getString(R.string.step_color_title)); 117 | colorItem.setSubLabel(getString(R.string.step_color_summary)); 118 | colorItem.setFragment(ColorPickerFragment.create()); 119 | items.add(colorItem); 120 | //Image item 121 | SteppersItem imageItem = new SteppersItem(); 122 | imageItem.setLabel(getString(R.string.step_image_title)); 123 | imageItem.setSubLabel(getString(R.string.step_image_summary)); 124 | imageItem.setFragment(CoverImagePickerFragment.create()); 125 | items.add(imageItem); 126 | 127 | return items; 128 | } 129 | 130 | private void restartActivity() { 131 | MainActivity_.intent(this).start(); 132 | this.finish(); 133 | } 134 | 135 | private void showNotConnectedDialog() { 136 | new MaterialDialog.Builder(this).title(R.string.dialog_no_connection_title) 137 | .content(R.string.dialog_no_connection_content) 138 | .positiveText(android.R.string.ok) 139 | .show(); 140 | } 141 | 142 | @Override 143 | public void onFinish() { 144 | if (networkUtils.isConnectionAvailable()) { 145 | loadingDialog = new MaterialDialog.Builder(this).title(R.string.dialog_generate_image_title) 146 | .content(R.string.dialog_generate_image_content) 147 | .progress(true, -1) 148 | .cancelable(false) 149 | .canceledOnTouchOutside(false) 150 | .autoDismiss(false) 151 | .show(); 152 | downloadImage(); 153 | } else { 154 | showNotConnectedDialog(); 155 | } 156 | 157 | } 158 | 159 | @UiThread 160 | void onImageDownloaded(File downloadedFile) { 161 | 162 | if (loadingDialog != null && loadingDialog.isShowing()) { 163 | loadingDialog.dismiss(); 164 | } 165 | 166 | if (downloadedFile != null && downloadedFile.exists()) { 167 | ResultActivity.open(this, downloadedFile); 168 | builderUtil.clean(); 169 | } else { 170 | //TODO: handle case if saving failed! 171 | } 172 | } 173 | 174 | @Background 175 | void downloadImage() { 176 | File downloadedFile = storageManager.saveBook(builderUtil.build()); 177 | onImageDownloaded(downloadedFile); 178 | } 179 | 180 | @Override 181 | @OptionsItem(R.id.menu_reset_content) 182 | public void onCancel() { 183 | new MaterialDialog.Builder(this).title(R.string.dialog_cancel_title) 184 | .content(R.string.dialog_cancel_content) 185 | .positiveText(android.R.string.yes) 186 | .negativeText(android.R.string.no) 187 | .onPositive((dialog, which) -> restartActivity()) 188 | .show(); 189 | } 190 | 191 | @OptionsItem(R.id.menu_github) 192 | void openGithubPage() { 193 | networkUtils.openUrl(this, UrlType.GITHUB); 194 | } 195 | 196 | @OptionsItem(R.id.menu_about) 197 | void openAboutPage() { 198 | AboutActivity.open(this); 199 | } 200 | 201 | @OptionsItem(R.id.menu_settings) 202 | void openSettingsPage() { 203 | //TODO: Add settings 204 | } 205 | 206 | @Override 207 | public void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor) { 208 | String strColor = String.format("#%06X", 0xFFFFFF & selectedColor); 209 | builderUtil.setCoverColor(CoverColor.getForHex(strColor)); 210 | } 211 | 212 | @Override 213 | public void onColorDialogClicked(int[] colors) { 214 | new ColorChooserDialog.Builder(this, R.string.dialog_pick_color_title).customColors(colors, null) 215 | .allowUserColorInput(false) 216 | .allowUserColorInputAlpha(false) 217 | .show(); 218 | } 219 | 220 | @Override 221 | public boolean dispatchTouchEvent(MotionEvent event) { 222 | if (event.getAction() == MotionEvent.ACTION_DOWN) { 223 | View view = getCurrentFocus(); 224 | if (view instanceof EditText) { 225 | Rect outRect = new Rect(); 226 | view.getGlobalVisibleRect(outRect); 227 | if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) { 228 | view.clearFocus(); 229 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 230 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 231 | } 232 | } 233 | } 234 | return super.dispatchTouchEvent(event); 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/ResultActivity.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | import android.support.annotation.NonNull; 6 | import android.support.design.widget.Snackbar; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.Toolbar; 9 | import android.view.View; 10 | import android.widget.ImageView; 11 | 12 | import com.pddstudio.orlyandroid.utils.StorageManager; 13 | import com.squareup.picasso.Picasso; 14 | 15 | import org.androidannotations.annotations.AfterViews; 16 | import org.androidannotations.annotations.Bean; 17 | import org.androidannotations.annotations.EActivity; 18 | import org.androidannotations.annotations.Extra; 19 | import org.androidannotations.annotations.InstanceState; 20 | import org.androidannotations.annotations.OptionsItem; 21 | import org.androidannotations.annotations.OptionsMenu; 22 | import org.androidannotations.annotations.ViewById; 23 | 24 | import java.io.File; 25 | 26 | @EActivity(R.layout.activity_result) 27 | @OptionsMenu(R.menu.menu_result) 28 | public class ResultActivity extends AppCompatActivity implements View.OnClickListener { 29 | 30 | public static void open(Context context, File image) { 31 | ResultActivity_.intent(context).imageFile(image).start(); 32 | } 33 | 34 | @ViewById(R.id.toolbar) 35 | Toolbar toolbar; 36 | 37 | @ViewById(R.id.image) 38 | ImageView imageView; 39 | 40 | @InstanceState 41 | @Extra 42 | File imageFile; 43 | 44 | @Bean 45 | StorageManager storageManager; 46 | 47 | @AfterViews 48 | void prepareActivity() { 49 | toolbar.setTitle(R.string.result_activity_title); 50 | toolbar.setNavigationIcon(R.drawable.ic_close_24dp); 51 | toolbar.setNavigationOnClickListener(this); 52 | setSupportActionBar(toolbar); 53 | Picasso.with(this).load(imageFile).into(imageView); 54 | } 55 | 56 | @Override 57 | @OptionsItem(android.R.id.home) 58 | public void onBackPressed() { 59 | //delete temporary stuff 60 | storageManager.recycle(); 61 | super.onBackPressed(); 62 | } 63 | 64 | @Override 65 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 66 | if (requestCode == StorageManager.STORAGE_REQUEST_CODE) { 67 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 68 | onSaveImage(); 69 | } else { 70 | showPermissionDeniedSnackBar(); 71 | } 72 | } 73 | } 74 | 75 | @OptionsItem(R.id.menu_save_image) 76 | void onSaveImage() { 77 | if (storageManager.canAccessStorage()) { 78 | boolean result = storageManager.saveImageToStorage(); 79 | showSnackBarForResult(result); 80 | } else { 81 | storageManager.requestStoragePermission(this); 82 | } 83 | } 84 | 85 | @OptionsItem(R.id.menu_share_image) 86 | void onShareImage() { 87 | storageManager.shareCachedImage(this); 88 | } 89 | 90 | @OptionsItem(R.id.menu_delete_image) 91 | void onDeleteImage() { 92 | //TODO: Add a dialog for confirming deletion 93 | onBackPressed(); 94 | } 95 | 96 | private void showSnackBarForResult(boolean result) { 97 | if (result) { 98 | Snackbar.make(imageView, 99 | String.format(getString(R.string.snack_bar_save_success), storageManager.getDestFile().getAbsolutePath()), 100 | Snackbar.LENGTH_LONG).setAction(android.R.string.ok, new View.OnClickListener() { 101 | @Override 102 | public void onClick(View view) { 103 | 104 | } 105 | }).show(); 106 | } else { 107 | Snackbar.make(imageView, R.string.snack_bar_save_fail, Snackbar.LENGTH_LONG).setAction(android.R.string.ok, new View.OnClickListener() { 108 | @Override 109 | public void onClick(View view) { 110 | 111 | } 112 | }).show(); 113 | } 114 | 115 | } 116 | 117 | private void showPermissionDeniedSnackBar() { 118 | Snackbar.make(imageView, R.string.snack_bar_permission_required, Snackbar.LENGTH_LONG).setAction(android.R.string.ok, new View.OnClickListener() { 119 | @Override 120 | public void onClick(View view) { 121 | 122 | } 123 | }).show(); 124 | } 125 | 126 | @Override 127 | public void onClick(View view) { 128 | onBackPressed(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/adapters/CoverImageAdapter.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ImageView; 10 | 11 | import com.pddstudio.orly.book.generator.utils.BookUtils; 12 | import com.pddstudio.orlyandroid.R; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * Created by pddstudio on 15/08/16. 18 | */ 19 | public class CoverImageAdapter extends RecyclerView.Adapter { 20 | 21 | private final List itemData; 22 | private final OnClickListener onClickListener; 23 | 24 | public interface OnClickListener { 25 | void onItemSelected(int itemPosition); 26 | } 27 | 28 | public CoverImageAdapter(Context context, OnClickListener onClickListener) { 29 | this.itemData = BookUtils.getImageResources(context); 30 | this.onClickListener = onClickListener; 31 | } 32 | 33 | @Override 34 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 35 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_image, parent, false); 36 | return new ViewHolder(view); 37 | } 38 | 39 | @Override 40 | public void onBindViewHolder(ViewHolder holder, int position) { 41 | Bitmap img = itemData.get(position); 42 | holder.imageView.setImageBitmap(img); 43 | } 44 | 45 | @Override 46 | public int getItemCount() { 47 | return itemData.size(); 48 | } 49 | 50 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 51 | 52 | private ImageView imageView; 53 | 54 | public ViewHolder(View itemView) { 55 | super(itemView); 56 | imageView = (ImageView) itemView.findViewById(R.id.item_image); 57 | itemView.setOnClickListener(this); 58 | } 59 | 60 | @Override 61 | public void onClick(View view) { 62 | onClickListener.onItemSelected(getAdapterPosition()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/enums/Type.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.enums; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by pddstudio on 15/08/16. 7 | */ 8 | public enum Type implements Serializable { 9 | TOP_TITLE, 10 | AUTHOR, 11 | TITLE, 12 | GUIDE 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/enums/UrlType.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.enums; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | /** 9 | * Created by pddstudio on 16/08/16. 10 | */ 11 | @Retention(RetentionPolicy.SOURCE) 12 | @IntDef({UrlType.GITHUB, UrlType.GOOGLE_PLUS}) 13 | public @interface UrlType { 14 | public static final int GITHUB = 1; 15 | public static final int GOOGLE_PLUS = 2; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/fragments/ColorPickerFragment.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.fragments; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.support.v4.app.Fragment; 6 | import android.widget.TextView; 7 | 8 | import com.pddstudio.orly.book.generator.enums.CoverColor; 9 | import com.pddstudio.orlyandroid.R; 10 | 11 | import org.androidannotations.annotations.AfterViews; 12 | import org.androidannotations.annotations.Click; 13 | import org.androidannotations.annotations.EFragment; 14 | import org.androidannotations.annotations.InstanceState; 15 | import org.androidannotations.annotations.ViewById; 16 | 17 | /** 18 | * Created by pddstudio on 15/08/16. 19 | */ 20 | @EFragment(R.layout.fragment_dialog_picker) 21 | public class ColorPickerFragment extends Fragment { 22 | 23 | public interface Callback { 24 | void onColorDialogClicked(int[] colors); 25 | } 26 | 27 | public static ColorPickerFragment create() { 28 | return ColorPickerFragment_.builder().build(); 29 | } 30 | 31 | @InstanceState 32 | int[] colors; 33 | 34 | @ViewById(R.id.text_title) 35 | TextView titleTextView; 36 | 37 | private Callback callback; 38 | 39 | @Override 40 | public void onAttach(Context context) { 41 | super.onAttach(context); 42 | try { 43 | callback = (Callback) context; 44 | } catch (ClassCastException c) { 45 | throw new RuntimeException("Calling Activity must implement " + Callback.class.getCanonicalName()); 46 | } 47 | } 48 | 49 | @AfterViews 50 | void prepareFragment() { 51 | titleTextView.setText(R.string.color_picker_title_text); 52 | colors = new int[CoverColor.values().length]; 53 | for (int i = 0; i < CoverColor.values().length; i++) { 54 | colors[i] = Color.parseColor(CoverColor.values()[i].getColorHex()); 55 | } 56 | } 57 | 58 | @Click(R.id.open_button) 59 | void openDialog() { 60 | callback.onColorDialogClicked(colors); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/fragments/CoverImagePickerFragment.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.fragments; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v7.widget.GridLayoutManager; 5 | import android.util.Log; 6 | import android.widget.TextView; 7 | 8 | import com.afollestad.materialdialogs.MaterialDialog; 9 | import com.pddstudio.orly.book.generator.enums.CoverImage; 10 | import com.pddstudio.orlyandroid.R; 11 | import com.pddstudio.orlyandroid.adapters.CoverImageAdapter; 12 | import com.pddstudio.orlyandroid.utils.BuilderUtil; 13 | 14 | import org.androidannotations.annotations.AfterViews; 15 | import org.androidannotations.annotations.Bean; 16 | import org.androidannotations.annotations.Click; 17 | import org.androidannotations.annotations.EFragment; 18 | import org.androidannotations.annotations.ViewById; 19 | 20 | /** 21 | * Created by pddstudio on 15/08/16. 22 | */ 23 | @EFragment(R.layout.fragment_dialog_picker) 24 | public class CoverImagePickerFragment extends Fragment implements CoverImageAdapter.OnClickListener { 25 | 26 | public static CoverImagePickerFragment create() { 27 | return CoverImagePickerFragment_.builder().build(); 28 | } 29 | 30 | @Bean 31 | BuilderUtil builderUtil; 32 | 33 | CoverImageAdapter coverImageAdapter; 34 | MaterialDialog materialDialog; 35 | 36 | @ViewById(R.id.text_title) 37 | TextView titleTextView; 38 | 39 | @AfterViews 40 | void prepareFragment() { 41 | coverImageAdapter = new CoverImageAdapter(getContext(), this); 42 | titleTextView.setText(R.string.cover_image_picker_title); 43 | } 44 | 45 | @Click(R.id.open_button) 46 | void openDialog() { 47 | materialDialog = new MaterialDialog.Builder(getContext()).title(R.string.dialog_cover_picker_title) 48 | .adapter(coverImageAdapter, new GridLayoutManager(getContext(), 2)) 49 | .show(); 50 | } 51 | 52 | @Override 53 | public void onItemSelected(int itemPosition) { 54 | Log.d("CoverImagePicker", "Selected position: " + itemPosition); 55 | int animalId = ++itemPosition; 56 | CoverImage coverImage = CoverImage.getImageForCode(animalId); 57 | builderUtil.setCoverImage(coverImage); 58 | materialDialog.dismiss(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/fragments/SingleTextFragment.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.fragments; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.StringRes; 5 | import android.support.v4.app.Fragment; 6 | import android.util.Log; 7 | import android.widget.EditText; 8 | import android.widget.TextView; 9 | 10 | import com.pddstudio.orlyandroid.R; 11 | import com.pddstudio.orlyandroid.enums.Type; 12 | import com.pddstudio.orlyandroid.utils.BuilderUtil; 13 | 14 | import org.androidannotations.annotations.AfterTextChange; 15 | import org.androidannotations.annotations.AfterViews; 16 | import org.androidannotations.annotations.Bean; 17 | import org.androidannotations.annotations.EFragment; 18 | import org.androidannotations.annotations.FragmentArg; 19 | import org.androidannotations.annotations.InstanceState; 20 | import org.androidannotations.annotations.ViewById; 21 | 22 | /** 23 | * Created by pddstudio on 15/08/16. 24 | */ 25 | @EFragment(R.layout.fragment_single_text) 26 | public class SingleTextFragment extends Fragment { 27 | 28 | public static SingleTextFragment create(Context context, @StringRes int titleText, Type type) { 29 | return SingleTextFragment_.builder().textTitle(context.getString(titleText)).type(type).build(); 30 | } 31 | 32 | @InstanceState 33 | @FragmentArg 34 | String textTitle; 35 | 36 | @InstanceState 37 | @FragmentArg 38 | Type type; 39 | 40 | @Bean 41 | BuilderUtil builderUtil; 42 | 43 | @ViewById(R.id.text_title) 44 | TextView titleTextView; 45 | 46 | @ViewById(R.id.text_content) 47 | EditText contentEditText; 48 | 49 | @AfterViews 50 | void prepareFragment() { 51 | titleTextView.setText(textTitle); 52 | } 53 | 54 | @AfterTextChange(R.id.text_content) 55 | void afterTextChanged() { 56 | String text = contentEditText.getText().toString(); 57 | Log.d("SingleTextFragment", "Text Changed: " + text); 58 | updateTextForType(text); 59 | } 60 | 61 | private void updateTextForType(String text) { 62 | switch (type) { 63 | case TITLE: 64 | builderUtil.setTitle(text); 65 | break; 66 | case TOP_TITLE: 67 | builderUtil.setTopText(text); 68 | break; 69 | case AUTHOR: 70 | builderUtil.setAuthor(text); 71 | break; 72 | case GUIDE: 73 | builderUtil.setGuideText(text); 74 | break; 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/utils/BuilderUtil.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.utils; 2 | 3 | import com.pddstudio.orly.book.generator.Book; 4 | import com.pddstudio.orly.book.generator.BookGenerator; 5 | import com.pddstudio.orly.book.generator.enums.CoverColor; 6 | import com.pddstudio.orly.book.generator.enums.CoverImage; 7 | import com.pddstudio.orly.book.generator.enums.GuideTextPosition; 8 | 9 | import org.androidannotations.annotations.EBean; 10 | 11 | /** 12 | * Created by pddstudio on 15/08/16. 13 | */ 14 | @EBean(scope = EBean.Scope.Singleton) 15 | public class BuilderUtil { 16 | 17 | private String title; 18 | private String topText; 19 | private String author; 20 | private String guideText; 21 | private CoverColor coverColor; 22 | private CoverImage coverImage; 23 | private GuideTextPosition guideTextPosition; 24 | 25 | public void setTitle(String title) { 26 | this.title = title; 27 | } 28 | 29 | public void setTopText(String topText) { 30 | this.topText = topText; 31 | } 32 | 33 | public void setAuthor(String author) { 34 | this.author = author; 35 | } 36 | 37 | public void setGuideText(String guideText) { 38 | this.guideText = guideText; 39 | } 40 | 41 | public void setCoverColor(CoverColor coverColor) { 42 | this.coverColor = coverColor; 43 | } 44 | 45 | public void setCoverImage(CoverImage coverImage) { 46 | this.coverImage = coverImage; 47 | } 48 | 49 | public void setGuideTextPosition(GuideTextPosition guideTextPosition) { 50 | this.guideTextPosition = guideTextPosition; 51 | } 52 | 53 | public void clean() { 54 | title = null; 55 | topText = null; 56 | author = null; 57 | guideText = null; 58 | coverColor = null; 59 | coverImage = null; 60 | guideTextPosition = null; 61 | } 62 | 63 | public Book build() { 64 | return BookGenerator.createBook() 65 | .withTitle(title) 66 | .withTopText(topText) 67 | .withAuthor(author) 68 | .withGuideText(guideText) 69 | .withCoverColor(coverColor) 70 | .withCoverImage(coverImage) 71 | .withGuideTextPosition(guideTextPosition) 72 | .generate(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/utils/GenerationUtils.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.pddstudio.orly.book.generator.Book; 7 | 8 | import org.androidannotations.annotations.Background; 9 | import org.androidannotations.annotations.EBean; 10 | import org.androidannotations.annotations.RootContext; 11 | 12 | import java.io.IOException; 13 | 14 | import okhttp3.OkHttpClient; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | 18 | /** 19 | * Created by pddstudio on 15/08/16. 20 | */ 21 | @EBean(scope = EBean.Scope.Singleton) 22 | public class GenerationUtils { 23 | 24 | private static final String TAG = GenerationUtils.class.getSimpleName(); 25 | 26 | @RootContext 27 | Context context; 28 | 29 | private OkHttpClient okHttpClient = new OkHttpClient(); 30 | 31 | @Background 32 | public void requestBook(Book book) { 33 | try { 34 | Request request = new Request.Builder().url(book.getGeneratedUrl()).build(); 35 | Response response = okHttpClient.newCall(request).execute(); 36 | Log.d(TAG, "Response code: " + response.code()); 37 | Log.d(TAG, "Response body:\n" + response.body().string()); 38 | } catch (IOException io) { 39 | io.printStackTrace(); 40 | } 41 | } 42 | 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/utils/NetworkUtils.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.ConnectivityManager; 6 | import android.net.NetworkInfo; 7 | import android.net.Uri; 8 | 9 | import com.pddstudio.orlyandroid.enums.UrlType; 10 | 11 | import org.androidannotations.annotations.EBean; 12 | import org.androidannotations.annotations.RootContext; 13 | import org.androidannotations.annotations.SystemService; 14 | 15 | /** 16 | * Created by pddstudio on 15/08/16. 17 | */ 18 | @EBean(scope = EBean.Scope.Singleton) 19 | public class NetworkUtils { 20 | 21 | private static final String GITHUB_URL = "https://github.com/PDDStudio"; 22 | private static final String GOOGLE_PLUS_URL = "https://plus.google.com/+PatrickJung42"; 23 | 24 | @RootContext 25 | Context context; 26 | 27 | @SystemService 28 | ConnectivityManager connectivityManager; 29 | 30 | public boolean isConnectionAvailable() { 31 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 32 | return networkInfo != null && networkInfo.isConnectedOrConnecting(); 33 | } 34 | 35 | public void openUrl(Context context, @UrlType int urlType) { 36 | String url = ""; 37 | switch (urlType) { 38 | case UrlType.GITHUB: 39 | url = GITHUB_URL; 40 | break; 41 | case UrlType.GOOGLE_PLUS: 42 | url = GOOGLE_PLUS_URL; 43 | break; 44 | } 45 | Intent webPage = new Intent(Intent.ACTION_VIEW); 46 | webPage.setData(Uri.parse(url)); 47 | context.startActivity(webPage); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/pddstudio/orlyandroid/utils/StorageManager.java: -------------------------------------------------------------------------------- 1 | package com.pddstudio.orlyandroid.utils; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Environment; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.support.v4.content.ContextCompat; 13 | import android.support.v4.content.FileProvider; 14 | 15 | import com.pddstudio.orly.book.generator.Book; 16 | import com.pddstudio.orlyandroid.R; 17 | 18 | import org.androidannotations.annotations.EBean; 19 | import org.androidannotations.annotations.RootContext; 20 | 21 | import java.io.File; 22 | import java.io.FileInputStream; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.io.InputStream; 26 | import java.io.OutputStream; 27 | 28 | import java8.util.J8Arrays; 29 | import okhttp3.OkHttpClient; 30 | import okhttp3.Request; 31 | import okhttp3.Response; 32 | import okio.BufferedSink; 33 | import okio.Okio; 34 | 35 | /** 36 | * Created by pddstudio on 15/08/16. 37 | */ 38 | @EBean(scope = EBean.Scope.Singleton) 39 | public class StorageManager { 40 | 41 | public static final int STORAGE_REQUEST_CODE = 42; 42 | private static final String STORAGE_FOLDER_NAME = "ORly-Android-Covers"; 43 | 44 | @RootContext 45 | Context context; 46 | 47 | private OkHttpClient okHttpClient = new OkHttpClient(); 48 | 49 | private Book tmpBook; 50 | private File tmpFile; 51 | private File destFile; 52 | 53 | private String generateName(Book book) { 54 | return book.getTitle() + "_" + book.getAuthor() + "_" + System.currentTimeMillis() + ".png"; 55 | } 56 | 57 | public File saveBook(Book book) { 58 | try { 59 | 60 | Request request = new Request.Builder().url(book.getGeneratedUrl()).build(); 61 | Response response = okHttpClient.newCall(request).execute(); 62 | 63 | if (!response.isSuccessful()) { 64 | throw new IOException("Something went wrong!\n" + response); 65 | } 66 | 67 | String bookName = generateName(book); 68 | tmpBook = book; 69 | tmpFile = new File(context.getCacheDir(), bookName); 70 | BufferedSink bufferedSink = Okio.buffer(Okio.sink(tmpFile)); 71 | bufferedSink.writeAll(response.body().source()); 72 | bufferedSink.close(); 73 | 74 | if (tmpFile.exists()) { 75 | return tmpFile; 76 | } else { 77 | throw new IOException("Unable to find file: " + tmpFile.getAbsolutePath()); 78 | } 79 | 80 | } catch (IOException io) { 81 | io.printStackTrace(); 82 | recycle(); 83 | return null; 84 | } 85 | } 86 | 87 | public Book getTmpBook() { 88 | return tmpBook; 89 | } 90 | 91 | public File getTmpFile() { 92 | return tmpFile; 93 | } 94 | 95 | public File getDestFile() { 96 | return destFile; 97 | } 98 | 99 | public void recycle() { 100 | if (tmpFile.exists()) { 101 | tmpFile.delete(); 102 | } 103 | destFile = null; 104 | tmpBook = null; 105 | tmpFile = null; 106 | } 107 | 108 | public void shareCachedImage(Context context) { 109 | if (tmpFile != null && tmpFile.exists()) { 110 | Uri imageUri = FileProvider.getUriForFile(context, "com.pddstudio.orlyandroid.fileprovider", tmpFile); 111 | if (imageUri != null) { 112 | Intent shareIntent = new Intent(); 113 | shareIntent.setAction(Intent.ACTION_SEND); 114 | shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 115 | shareIntent.setDataAndType(imageUri, context.getContentResolver().getType(imageUri)); 116 | shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri); 117 | context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image_text))); 118 | } 119 | } 120 | } 121 | 122 | public boolean canAccessStorage() { 123 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 124 | return true; 125 | } 126 | return ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; 127 | } 128 | 129 | public void requestStoragePermission(Activity activity) { 130 | ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_REQUEST_CODE); 131 | } 132 | 133 | private boolean checkIfDirectoryExistsAndCreateIfNot() { 134 | if (canAccessStorage()) { 135 | String directory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + STORAGE_FOLDER_NAME; 136 | File storageDir = new File(directory); 137 | if (!storageDir.exists()) { 138 | storageDir.mkdirs(); 139 | } 140 | return storageDir.exists() && storageDir.isDirectory(); 141 | } else { 142 | return false; 143 | } 144 | } 145 | 146 | private File getStorageDir() { 147 | String directory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + STORAGE_FOLDER_NAME; 148 | return new File(directory); 149 | } 150 | 151 | public boolean saveImageToStorage() { 152 | if (!checkIfDirectoryExistsAndCreateIfNot()) { 153 | return false; 154 | } else { 155 | destFile = new File(getStorageDir(), tmpFile.getName()); 156 | copyFile(tmpFile, destFile); 157 | return destFile.exists(); 158 | } 159 | } 160 | 161 | private void copyFile(File source, File dest) { 162 | try { 163 | InputStream inputStream = new FileInputStream(source); 164 | OutputStream outputStream = new FileOutputStream(dest); 165 | byte[] buffer = new byte[1024]; 166 | int reader; 167 | while ((reader = inputStream.read(buffer)) > 0) { 168 | outputStream.write(buffer, 0, reader); 169 | } 170 | inputStream.close(); 171 | outputStream.close(); 172 | } catch (IOException io) { 173 | io.printStackTrace(); 174 | } 175 | } 176 | 177 | public void cleanCacheDir() { 178 | File cache = context.getCacheDir(); 179 | J8Arrays.stream(cache.listFiles()).filter(File::isFile).forEach(file -> { 180 | if (file.getName().endsWith(".png")) { 181 | file.delete(); 182 | } 183 | }); 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help_outline_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_open_in_new_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_backup_restore_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_result.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_dialog_picker.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 |