├── settings.gradle ├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ ├── colors.xml │ │ │ └── strings.xml │ │ ├── drawable-hdpi │ │ │ ├── ic_info.png │ │ │ ├── ic_mail.png │ │ │ ├── ic_myo.png │ │ │ ├── ic_undo.png │ │ │ ├── ic_coffee.png │ │ │ ├── ic_github.png │ │ │ ├── ic_gplus.png │ │ │ ├── ic_reload.png │ │ │ ├── ic_thalmic.png │ │ │ ├── ic_twitter.png │ │ │ ├── ic_arrow_top.png │ │ │ ├── ic_document.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_pose_fist.png │ │ │ ├── ic_arrow_bottom.png │ │ │ ├── ic_arrow_left.png │ │ │ ├── ic_arrow_right.png │ │ │ ├── ic_circle_empty.png │ │ │ ├── ic_google_play.png │ │ │ ├── ic_pose_fist_lh.png │ │ │ ├── ic_pose_spread.png │ │ │ ├── ic_pose_spread_lh.png │ │ │ ├── ic_pose_waveleft.png │ │ │ ├── ic_pose_waveright.png │ │ │ ├── ic_pose_waveleft_lh.png │ │ │ └── ic_pose_waveright_lh.png │ │ ├── drawable-mdpi │ │ │ ├── ic_info.png │ │ │ ├── ic_mail.png │ │ │ ├── ic_myo.png │ │ │ ├── ic_undo.png │ │ │ ├── ic_coffee.png │ │ │ ├── ic_github.png │ │ │ ├── ic_gplus.png │ │ │ ├── ic_reload.png │ │ │ ├── ic_thalmic.png │ │ │ ├── ic_twitter.png │ │ │ ├── ic_arrow_top.png │ │ │ ├── ic_document.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_pose_fist.png │ │ │ ├── ic_arrow_bottom.png │ │ │ ├── ic_arrow_left.png │ │ │ ├── ic_arrow_right.png │ │ │ ├── ic_circle_empty.png │ │ │ ├── ic_google_play.png │ │ │ ├── ic_pose_fist_lh.png │ │ │ ├── ic_pose_spread.png │ │ │ ├── ic_pose_spread_lh.png │ │ │ ├── ic_pose_waveleft.png │ │ │ ├── ic_action_settings.png │ │ │ ├── ic_pose_waveleft_lh.png │ │ │ └── scoreview_background_rectangle_inner.xml │ │ ├── drawable-xhdpi │ │ │ ├── ic_myo.png │ │ │ ├── ic_coffee.png │ │ │ ├── ic_github.png │ │ │ ├── ic_gplus.png │ │ │ ├── ic_info.png │ │ │ ├── ic_mail.png │ │ │ ├── ic_reload.png │ │ │ ├── ic_undo.png │ │ │ ├── ic_document.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_thalmic.png │ │ │ ├── ic_twitter.png │ │ │ ├── ic_arrow_left.png │ │ │ ├── ic_arrow_right.png │ │ │ ├── ic_arrow_top.png │ │ │ ├── ic_google_play.png │ │ │ ├── ic_pose_fist.png │ │ │ ├── ic_pose_spread.png │ │ │ ├── ic_arrow_bottom.png │ │ │ ├── ic_circle_empty.png │ │ │ ├── ic_pose_fist_lh.png │ │ │ ├── ic_pose_waveleft.png │ │ │ ├── ic_pose_spread_lh.png │ │ │ ├── ic_pose_waveleft_lh.png │ │ │ ├── ic_pose_waveright.png │ │ │ └── ic_pose_waveright_lh.png │ │ ├── drawable-xxhdpi │ │ │ ├── ic_gplus.png │ │ │ ├── ic_info.png │ │ │ ├── ic_mail.png │ │ │ ├── ic_myo.png │ │ │ ├── ic_undo.png │ │ │ ├── ic_coffee.png │ │ │ ├── ic_github.png │ │ │ ├── ic_reload.png │ │ │ ├── ic_thalmic.png │ │ │ ├── ic_twitter.png │ │ │ ├── ic_arrow_left.png │ │ │ ├── ic_arrow_top.png │ │ │ ├── ic_document.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_pose_fist.png │ │ │ ├── ic_arrow_bottom.png │ │ │ ├── ic_arrow_right.png │ │ │ ├── ic_circle_empty.png │ │ │ ├── ic_google_play.png │ │ │ ├── ic_pose_fist_lh.png │ │ │ ├── ic_pose_spread.png │ │ │ ├── ic_pose_spread_lh.png │ │ │ ├── ic_pose_waveleft.png │ │ │ ├── ic_pose_waveright.png │ │ │ ├── ic_pose_waveleft_lh.png │ │ │ └── ic_pose_waveright_lh.png │ │ ├── drawable-xxxhdpi │ │ │ ├── ic_pose_fist.png │ │ │ ├── ic_pose_spread.png │ │ │ ├── ic_pose_fist_lh.png │ │ │ ├── ic_pose_spread_lh.png │ │ │ ├── ic_pose_waveleft.png │ │ │ ├── ic_pose_waveright.png │ │ │ ├── ic_pose_waveleft_lh.png │ │ │ └── ic_pose_waveright_lh.png │ │ ├── anim │ │ │ ├── fragment_anim_normal_enter.xml │ │ │ ├── fragment_anim_normal_exit.xml │ │ │ └── wiggle.xml │ │ ├── drawable │ │ │ ├── cell_rectangle_128.xml │ │ │ ├── cell_rectangle_16.xml │ │ │ ├── cell_rectangle_2.xml │ │ │ ├── cell_rectangle_32.xml │ │ │ ├── cell_rectangle_4.xml │ │ │ ├── cell_rectangle_64.xml │ │ │ ├── cell_rectangle_8.xml │ │ │ ├── cell_rectangle_256.xml │ │ │ ├── cell_rectangle_512.xml │ │ │ ├── background_rectangle.xml │ │ │ ├── cell_rectangle.xml │ │ │ ├── cell_rectangle_1024.xml │ │ │ ├── cell_rectangle_131072.xml │ │ │ ├── cell_rectangle_16384.xml │ │ │ ├── cell_rectangle_2048.xml │ │ │ ├── cell_rectangle_262144.xml │ │ │ ├── cell_rectangle_32768.xml │ │ │ ├── cell_rectangle_4096.xml │ │ │ ├── cell_rectangle_524288.xml │ │ │ ├── cell_rectangle_65536.xml │ │ │ ├── cell_rectangle_8192.xml │ │ │ ├── light_up_rectangle.xml │ │ │ ├── fade_rectangle.xml │ │ │ ├── endstate_overlay_rectangle.xml │ │ │ ├── button_rectangle.xml │ │ │ ├── button_rectangle_pressed.xml │ │ │ └── button_selector.xml │ │ ├── layout-land │ │ │ ├── activity_main.xml │ │ │ └── fragment_game_layout.xml │ │ ├── layout-large-land │ │ │ ├── activity_main.xml │ │ │ └── fragment_game_layout.xml │ │ ├── layout │ │ │ ├── activity_main.xml │ │ │ ├── fragment_welcome_layout.xml │ │ │ └── fragment_game_layout.xml │ │ └── xml │ │ │ └── prefs_info.xml │ │ ├── java │ │ └── eu │ │ │ └── thedarken │ │ │ └── myo │ │ │ └── twothousandfortyeight │ │ │ ├── tools │ │ │ ├── KeyListener.java │ │ │ ├── iap │ │ │ │ ├── Base64DecoderException.java │ │ │ │ ├── IabException.java │ │ │ │ ├── IabResult.java │ │ │ │ ├── SkuDetails.java │ │ │ │ ├── Purchase.java │ │ │ │ ├── Inventory.java │ │ │ │ └── Security.java │ │ │ ├── Logy.java │ │ │ ├── ScoreKeeper.java │ │ │ └── InputListener.java │ │ │ ├── game │ │ │ ├── Tile.java │ │ │ ├── Position.java │ │ │ ├── AnimationTile.java │ │ │ ├── AnimationGrid.java │ │ │ ├── GameGrid.java │ │ │ └── Game.java │ │ │ ├── InfoFragment.java │ │ │ ├── WelcomeFragment.java │ │ │ └── MainActivity.java │ │ ├── AndroidManifest.xml │ │ └── aidl │ │ └── com │ │ └── android │ │ └── vending │ │ └── billing │ │ └── IInAppBillingService.aidl ├── proguard-rules.txt └── build.gradle ├── artwork ├── app_icon.ai ├── app_icon.png ├── thalmic.png ├── myo_icon_white.ai ├── thalmic_icon.psd └── screenshot_port.png ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── privacy_policy_for_gplay.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /src/main/assets/crashlytics-build.properties -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /artwork/app_icon.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/app_icon.ai -------------------------------------------------------------------------------- /artwork/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/app_icon.png -------------------------------------------------------------------------------- /artwork/thalmic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/thalmic.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | *.iml 3 | .idea 4 | /build 5 | local.properties -------------------------------------------------------------------------------- /artwork/myo_icon_white.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/myo_icon_white.ai -------------------------------------------------------------------------------- /artwork/thalmic_icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/thalmic_icon.psd -------------------------------------------------------------------------------- /artwork/screenshot_port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/artwork/screenshot_port.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_myo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_myo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_undo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_myo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_myo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_undo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_myo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_myo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_coffee.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_gplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_gplus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_reload.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_thalmic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_thalmic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_twitter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_coffee.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_gplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_gplus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_reload.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_thalmic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_thalmic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_twitter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_coffee.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_gplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_gplus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_reload.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_undo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_gplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_gplus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_myo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_myo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_undo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_arrow_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_document.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_fist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_arrow_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_document.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_fist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_document.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_thalmic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_thalmic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_twitter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_coffee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_coffee.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_reload.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_thalmic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_thalmic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_twitter.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_arrow_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_arrow_left.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_circle_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_circle_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_google_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_fist_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_fist_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_spread.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_arrow_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_arrow_left.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_circle_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_circle_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_google_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_fist_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_fist_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_spread.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_arrow_left.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_arrow_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_google_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_fist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_spread.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_arrow_left.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_arrow_top.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_document.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_fist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_fist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_fist.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_spread_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_spread_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_waveleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_waveleft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_waveright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_waveright.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_spread_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_spread_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_waveleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_waveleft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_arrow_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_circle_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_circle_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_fist_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_fist_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_waveleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_waveleft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_arrow_bottom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_arrow_right.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_circle_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_circle_empty.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_google_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_google_play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_fist_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_fist_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_spread.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_spread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_spread.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_waveleft_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_waveleft_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_pose_waveright_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-hdpi/ic_pose_waveright_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_action_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_pose_waveleft_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-mdpi/ic_pose_waveleft_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_spread_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_spread_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_waveleft_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_waveleft_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_waveright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_waveright.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_spread_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_spread_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_waveleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_waveleft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_waveright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_waveright.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_fist_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_fist_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_spread_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_spread_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_waveleft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_waveleft.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_waveright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_waveright.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_pose_waveright_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xhdpi/ic_pose_waveright_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_waveleft_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_waveleft_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_pose_waveright_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxhdpi/ic_pose_waveright_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_waveleft_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_waveleft_lh.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_pose_waveright_lh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d4rken-org/myo-android-2048/HEAD/app/src/main/res/drawable-xxxhdpi/ic_pose_waveright_lh.png -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/KeyListener.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.tools; 2 | 3 | import android.view.KeyEvent; 4 | 5 | 6 | public interface KeyListener { 7 | public boolean onKeyDown(int keyCode, KeyEvent event); 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Dec 07 16:22:45 CET 2014 2 | w= 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_anim_normal_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_anim_normal_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_128.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_16.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_32.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_64.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_256.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_512.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_1024.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_131072.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_16384.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_2048.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_262144.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_32768.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_4096.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_524288.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_65536.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cell_rectangle_8192.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/light_up_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/fade_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/endstate_overlay_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/scoreview_background_rectangle_inner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout-large-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #f9f6f2 4 | #212121 5 | #d6cdc4 6 | #000000 7 | #1e242b 8 | #00bcdd 9 | #232528 10 | #111111 11 | #222222 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_rectangle_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2048 Myo Edition 2 | 3 | The game 2048 with Myo controls. 4 | Available on [Google Play](https://play.google.com/store/apps/details?id=eu.thedarken.myo.twothousandfortyeight). 5 | 6 | ![](https://lh3.ggpht.com/31XjA_f46SZNaCPiIHP36AHIQLHDvrAFhyLO-PEmZD_pxF-uF9uz1G_F-gL71Wc_8jOu=h450) 7 | 8 | ### LICENSE 9 | My work is under Apache 2.0 10 | 11 | It is based on the android port: https://github.com/tpcstld/2048 12 | Licensed under MIT. 13 | 14 | The original game 2048 is by Gabriele Cirulli: https://github.com/gabrielecirulli/2048 15 | Licensed under MIT. 16 | 17 | Myo and Thalmic artwork are property of Thalmic Labs 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /privacy_policy_for_gplay.md: -------------------------------------------------------------------------------- 1 | Privacy policy for the app "2048 Myo Edition" 2 | 3 | If you are using a Myo, Thalmic's privacy policy can be found here: 4 | http://thalmic.com/privacy 5 | 6 | I make use of "Crashlytics" to track app crashes: 7 | https://www.crashlytics.com/ 8 | 9 | Crashlytics privacy policy can be found here: 10 | http://try.crashlytics.com/terms/ 11 | 12 | This app can contains ads, they are displayed via "AdMob": 13 | https://www.google.com/admob/ 14 | 15 | AdMob is owned by Google, the privacy policy can be found here: 16 | https://www.google.com/intl/en/policies/privacy/ 17 | 18 | If you have any questions, just send a quick mail to support@darken.eu. 19 | -------------------------------------------------------------------------------- /app/src/main/res/anim/wiggle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/Tile.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | public class Tile extends Position { 4 | private final int value; 5 | private Tile[] mergedFrom = null; 6 | 7 | public Tile(int x, int y, int value) { 8 | super(x, y); 9 | this.value = value; 10 | } 11 | 12 | public Tile(Position cell, int value) { 13 | super(cell.getX(), cell.getY()); 14 | this.value = value; 15 | } 16 | 17 | public void updatePosition(Position cell) { 18 | this.setX(cell.getX()); 19 | this.setY(cell.getY()); 20 | } 21 | 22 | public int getValue() { 23 | return this.value; 24 | } 25 | 26 | public Tile[] getMergedFrom() { 27 | return mergedFrom; 28 | } 29 | 30 | public void setMergedFrom(Tile[] tile) { 31 | mergedFrom = tile; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/Position.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | public class Position { 4 | private int x; 5 | private int y; 6 | 7 | public Position(int x, int y) { 8 | this.x = x; 9 | this.y = y; 10 | } 11 | 12 | public int getX() { 13 | return this.x; 14 | } 15 | 16 | public int getY() { 17 | return this.y; 18 | } 19 | 20 | void setX(int x) { 21 | this.x = x; 22 | } 23 | 24 | void setY(int y) { 25 | this.y = y; 26 | } 27 | 28 | public static boolean equal(Position first, Position second) { 29 | return first.getX() == second.getX() && first.getY() == second.getY(); 30 | } 31 | 32 | public static Position getVector(int direction) { 33 | Position[] map = { 34 | new Position(0, -1), // up 35 | new Position(1, 0), // right 36 | new Position(0, 1), // down 37 | new Position(-1, 0) // left 38 | }; 39 | return map[direction]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/Base64DecoderException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2002, Google, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 16 | 17 | /** 18 | * Exception thrown when encountering an invalid Base64 input character. 19 | * 20 | * @author nelson 21 | */ 22 | public class Base64DecoderException extends Exception { 23 | public Base64DecoderException() { 24 | super(); 25 | } 26 | 27 | public Base64DecoderException(String s) { 28 | super(s); 29 | } 30 | 31 | private static final long serialVersionUID = 1L; 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/AnimationTile.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | class AnimationTile extends Position { 4 | private final int mAnimationType; 5 | private long mTimeElapsed; 6 | private final long mAnimationTime; 7 | private final long mDelayTime; 8 | private final int[] mExtras; 9 | 10 | public AnimationTile(int x, int y, int animationType, long length, long delay, int[] extras) { 11 | super(x, y); 12 | mAnimationType = animationType; 13 | mAnimationTime = length; 14 | mDelayTime = delay; 15 | mExtras = extras; 16 | } 17 | 18 | public int getAnimationType() { 19 | return mAnimationType; 20 | } 21 | 22 | public void tick(long timeElapsed) { 23 | this.mTimeElapsed = this.mTimeElapsed + timeElapsed; 24 | } 25 | 26 | public boolean animationDone() { 27 | return mAnimationTime + mDelayTime < mTimeElapsed; 28 | } 29 | 30 | public double getPercentageDone() { 31 | return Math.max(0, 1.0 * (mTimeElapsed - mDelayTime) / mAnimationTime); 32 | } 33 | 34 | public boolean isActive() { 35 | return (mTimeElapsed >= mDelayTime); 36 | } 37 | 38 | public int[] getExtras() { 39 | return mExtras; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:/Android/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -keepattributes SourceFile,LineNumberTable 20 | 21 | -keep class * extends java.util.ListResourceBundle { 22 | protected Object[][] getContents(); 23 | } 24 | 25 | -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { 26 | public static final *** NULL; 27 | } 28 | 29 | -keepnames @com.google.android.gms.common.annotation.KeepName class * 30 | -keepclassmembernames class * { 31 | @com.google.android.gms.common.annotation.KeepName *; 32 | } 33 | 34 | -keepnames class * implements android.os.Parcelable { 35 | public static final ** CREATOR; 36 | } -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/Logy.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.tools; 2 | 3 | import android.util.Log; 4 | import eu.thedarken.myo.twothousandfortyeight.BuildConfig; 5 | 6 | /** 7 | * It's Logy! 8 | */ 9 | public class Logy { 10 | private static final int VERBOSE = 1; 11 | private static final int DEBUG = 2; 12 | private static final int NORMAL = 3; 13 | private static final int QUIET = 4; 14 | public static final int SILENT = 5; 15 | 16 | private static int loglevel = BuildConfig.DEBUG ? VERBOSE : NORMAL; 17 | 18 | public static void v(String c, String s) { 19 | if (loglevel <= VERBOSE) { 20 | if (s == null) 21 | s = "\"NULL\""; 22 | Log.v(c, s); 23 | } 24 | } 25 | 26 | public static void d(String c, String s) { 27 | if (loglevel <= DEBUG) { 28 | if (s == null) 29 | s = "\"NULL\""; 30 | Log.d(c, s); 31 | } 32 | } 33 | 34 | public static void i(String c, String s) { 35 | if (loglevel <= NORMAL) { 36 | if (s == null) 37 | s = "\"NULL\""; 38 | Log.i(c, s); 39 | } 40 | } 41 | 42 | public static void w(String c, String s) { 43 | if (loglevel <= QUIET) { 44 | if (s == null) 45 | s = "\"NULL\""; 46 | Log.w(c, s); 47 | } 48 | } 49 | 50 | public static void e(String c, String s) { 51 | if (loglevel <= QUIET) { 52 | if (s == null) 53 | s = "\"NULL\""; 54 | Log.e(c, s); 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/IabException.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | /** 19 | * Exception thrown when something went wrong with in-app billing. 20 | * An IabException has an associated IabResult (an error). 21 | * To get the IAB result that caused this exception to be thrown, 22 | * call {@link #getResult()}. 23 | */ 24 | public class IabException extends Exception { 25 | IabResult mResult; 26 | 27 | public IabException(IabResult r) { 28 | this(r, null); 29 | } 30 | public IabException(int response, String message) { 31 | this(new IabResult(response, message)); 32 | } 33 | public IabException(IabResult r, Exception cause) { 34 | super(r.getMessage(), cause); 35 | mResult = r; 36 | } 37 | public IabException(int response, String message, Exception cause) { 38 | this(new IabResult(response, message), cause); 39 | } 40 | 41 | /** Returns the IAB result (error) that this exception signals. */ 42 | public IabResult getResult() { return mResult; } 43 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | maven { url 'https://maven.fabric.io/public' } 5 | } 6 | dependencies { 7 | classpath 'io.fabric.tools:gradle:1.+' 8 | } 9 | } 10 | apply plugin: 'com.android.application' 11 | apply plugin: 'io.fabric' 12 | 13 | repositories { 14 | maven { url 'https://maven.fabric.io/public' } 15 | } 16 | 17 | 18 | def gitSha() { 19 | return 'git rev-parse --short HEAD'.execute().text.trim() 20 | } 21 | 22 | android { 23 | compileSdkVersion 22 24 | buildToolsVersion "22.0.1" 25 | 26 | defaultConfig { 27 | minSdkVersion 18 28 | targetSdkVersion 22 29 | versionCode 12 30 | versionName "1.1.1" 31 | } 32 | buildTypes { 33 | release { 34 | minifyEnabled true 35 | shrinkResources true 36 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 37 | } 38 | applicationVariants.all { variant -> 39 | if (variant.buildType.name == "release") { 40 | variant.outputs.each { output -> 41 | def file = output.outputFile 42 | output.outputFile = new File(file.parent, "2048_Myo_Edition-v" + defaultConfig.versionName + "-" + defaultConfig.versionCode + "-" + gitSha() + ".apk") 43 | } 44 | } 45 | } 46 | } 47 | } 48 | dependencies { 49 | compile fileTree(dir: 'libs', include: ['*.jar']) 50 | repositories { 51 | maven { 52 | url '../../myoSDK/myorepository' 53 | } 54 | maven { url 'http://download.crashlytics.com/maven' } 55 | } 56 | compile 'com.thalmic:myosdk:0.10.0@aar' 57 | compile 'com.google.android.gms:play-services-ads:7.0.0' 58 | compile('com.crashlytics.sdk.android:crashlytics:2.5.3@aar') { 59 | transitive = true; 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/IabResult.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | /** 19 | * Represents the result of an in-app billing operation. 20 | * A result is composed of a response code (an integer) and possibly a 21 | * message (String). You can get those by calling 22 | * {@link #getResponse} and {@link #getMessage()}, respectively. You 23 | * can also inquire whether a result is a success or a failure by 24 | * calling {@link #isSuccess()} and {@link #isFailure()}. 25 | */ 26 | public class IabResult { 27 | int mResponse; 28 | String mMessage; 29 | 30 | public IabResult(int response, String message) { 31 | mResponse = response; 32 | if (message == null || message.trim().length() == 0) { 33 | mMessage = IabHelper.getResponseDesc(response); 34 | } 35 | else { 36 | mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")"; 37 | } 38 | } 39 | public int getResponse() { return mResponse; } 40 | public String getMessage() { return mMessage; } 41 | public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } 42 | public boolean isFailure() { return !isSuccess(); } 43 | public String toString() { return "IabResult: " + getMessage(); } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | // Required for communicating with the Myo device 6 | 7 | 8 | 9 | // Required for sending usage data to Thalmic Labs 10 | 11 | 12 | // Required for saving Myo settings and other data 13 | 14 | 15 | 16 | // IAP to remove ads 17 | 18 | 19 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | // AdMob 35 | 38 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/SkuDetails.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | /** 22 | * Represents an in-app product's listing details. 23 | */ 24 | public class SkuDetails { 25 | String mItemType; 26 | String mSku; 27 | String mType; 28 | String mPrice; 29 | String mTitle; 30 | String mDescription; 31 | String mJson; 32 | 33 | public SkuDetails(String jsonSkuDetails) throws JSONException { 34 | this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails); 35 | } 36 | 37 | public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException { 38 | mItemType = itemType; 39 | mJson = jsonSkuDetails; 40 | JSONObject o = new JSONObject(mJson); 41 | mSku = o.optString("productId"); 42 | mType = o.optString("type"); 43 | mPrice = o.optString("price"); 44 | mTitle = o.optString("title"); 45 | mDescription = o.optString("description"); 46 | } 47 | 48 | public String getSku() { return mSku; } 49 | public String getType() { return mType; } 50 | public String getPrice() { return mPrice; } 51 | public String getTitle() { return mTitle; } 52 | public String getDescription() { return mDescription; } 53 | 54 | @Override 55 | public String toString() { 56 | return "SkuDetails:" + mJson; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/ScoreKeeper.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.tools; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.widget.TextView; 6 | import eu.thedarken.myo.twothousandfortyeight.game.Game; 7 | 8 | public class ScoreKeeper implements Game.ScoreListener { 9 | 10 | private TextView mScoreDisplay; 11 | private TextView mHighScoreDisplay; 12 | private static final String HIGH_SCORE = "score.highscore"; 13 | private static final String PREFERENCES = "score"; 14 | private final SharedPreferences mPreferences; 15 | private long mScore; 16 | private long mHighScore; 17 | 18 | public ScoreKeeper(Context context) { 19 | mPreferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE); 20 | } 21 | 22 | public void setViews(TextView score, TextView highscore) { 23 | mScoreDisplay = score; 24 | mHighScoreDisplay = highscore; 25 | reset(); 26 | } 27 | 28 | private void reset() { 29 | mHighScore = loadHighScore(); 30 | if (mHighScoreDisplay != null) 31 | mHighScoreDisplay.setText("" + mHighScore); 32 | mScore = 0; 33 | if (mScoreDisplay != null) 34 | mScoreDisplay.setText("" + mScore); 35 | } 36 | 37 | private long loadHighScore() { 38 | if (mPreferences == null) 39 | return -1; 40 | return mPreferences.getLong(HIGH_SCORE, 0); 41 | } 42 | 43 | private void saveHighScore(long highScore) { 44 | SharedPreferences.Editor editor = mPreferences.edit(); 45 | editor.putLong(HIGH_SCORE, highScore); 46 | editor.commit(); 47 | } 48 | 49 | void setScore(long score) { 50 | mScore = score; 51 | if (mScoreDisplay != null) 52 | mScoreDisplay.setText("" + mScore); 53 | if (mScore > mHighScore) { 54 | mHighScore = mScore; 55 | if (mHighScoreDisplay != null) 56 | mHighScoreDisplay.setText("" + mHighScore); 57 | saveHighScore(mHighScore); 58 | } 59 | } 60 | 61 | @Override 62 | public void onNewScore(long score) { 63 | setScore(score); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/Purchase.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | /** 22 | * Represents an in-app billing purchase. 23 | */ 24 | public class Purchase { 25 | String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS 26 | String mOrderId; 27 | String mPackageName; 28 | String mSku; 29 | long mPurchaseTime; 30 | int mPurchaseState; 31 | String mDeveloperPayload; 32 | String mToken; 33 | String mOriginalJson; 34 | String mSignature; 35 | 36 | public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException { 37 | mItemType = itemType; 38 | mOriginalJson = jsonPurchaseInfo; 39 | JSONObject o = new JSONObject(mOriginalJson); 40 | mOrderId = o.optString("orderId"); 41 | mPackageName = o.optString("packageName"); 42 | mSku = o.optString("productId"); 43 | mPurchaseTime = o.optLong("purchaseTime"); 44 | mPurchaseState = o.optInt("purchaseState"); 45 | mDeveloperPayload = o.optString("developerPayload"); 46 | mToken = o.optString("token", o.optString("purchaseToken")); 47 | mSignature = signature; 48 | } 49 | 50 | public String getItemType() { return mItemType; } 51 | public String getOrderId() { return mOrderId; } 52 | public String getPackageName() { return mPackageName; } 53 | public String getSku() { return mSku; } 54 | public long getPurchaseTime() { return mPurchaseTime; } 55 | public int getPurchaseState() { return mPurchaseState; } 56 | public String getDeveloperPayload() { return mDeveloperPayload; } 57 | public String getToken() { return mToken; } 58 | public String getOriginalJson() { return mOriginalJson; } 59 | public String getSignature() { return mSignature; } 60 | 61 | @Override 62 | public String toString() { return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson; } 63 | } 64 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2048 Myo Edition 5 | HIGH SCORE 6 | SCORE 7 | You Win! 8 | Game Over! 9 | App version 10 | This app is open source. 11 | Source code 12 | Hear about new stuff. 13 | Come by and say hi :) 14 | I speak English and German. 15 | Thank you. 16 | Remove ads 17 | Ads are removed 18 | Communication 19 | Source 20 | Need help? 21 | Advertisements 22 | Get a Myo 23 | You can get a Myo from ThalmicLabs. 24 | What is a Myo? 25 | The Myo is an input device you wear on your arm that can track movement and hand gestures. 26 | Myo 27 | Thanks to tpcstld 28 | Thanks to Gabriele Cirulli 29 | This app is based on tpcstld\'s 2048 Android app. Under MIT License. 30 | tpcstld based his Android version on it. 31 | Myo edition 32 | Connect your Myo 33 | Setup Myo 34 | Wave in 35 | Wave out 36 | Spread 37 | Fist 38 | Reverse Left/Right 39 | PLAY 40 | The operation failed. 41 | Purchase unsuccessful 42 | Start new game 43 | Undo last move 44 | Myo options 45 | About this app 46 | Awesome!\nNow do the sync gesture. 47 | Arm recognized 48 | Arm lost 49 | Perfect!\nWhen you\'re ready, press play. 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/xml/prefs_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 11 | 16 | 17 | 18 | 19 | 22 | 27 | 28 | 29 | 30 | 33 | 38 | 43 | 48 | 49 | 50 | 51 | 54 | 59 | 64 | 69 | 70 | 73 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/AnimationGrid.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | import java.util.ArrayList; 4 | 5 | 6 | class AnimationGrid { 7 | private final ArrayList[][] mAnimationGameGrid; 8 | private int mActiveAnimations = 0; 9 | private boolean mOneMoreFrame = false; 10 | private final ArrayList mGlobalAnimation = new ArrayList(); 11 | 12 | public AnimationGrid(int x, int y) { 13 | mAnimationGameGrid = new ArrayList[x][y]; 14 | for (int xx = 0; xx < x; xx++) { 15 | for (int yy = 0; yy < y; yy++) { 16 | mAnimationGameGrid[xx][yy] = new ArrayList(); 17 | } 18 | } 19 | } 20 | 21 | public void startAnimation(int x, int y, int animationType, long length, long delay, int[] extras) { 22 | AnimationTile animationToAdd = new AnimationTile(x, y, animationType, length, delay, extras); 23 | if (x == -1 && y == -1) { 24 | mGlobalAnimation.add(animationToAdd); 25 | } else { 26 | mAnimationGameGrid[x][y].add(animationToAdd); 27 | } 28 | mActiveAnimations = mActiveAnimations + 1; 29 | } 30 | 31 | public void tickAll(long timeElapsed) { 32 | ArrayList cancelledAnimations = new ArrayList(); 33 | for (AnimationTile animation : mGlobalAnimation) { 34 | animation.tick(timeElapsed); 35 | if (animation.animationDone()) { 36 | cancelledAnimations.add(animation); 37 | mActiveAnimations = mActiveAnimations - 1; 38 | } 39 | } 40 | 41 | for (ArrayList[] array : mAnimationGameGrid) { 42 | for (ArrayList list : array) { 43 | for (AnimationTile animation : list) { 44 | animation.tick(timeElapsed); 45 | if (animation.animationDone()) { 46 | cancelledAnimations.add(animation); 47 | mActiveAnimations = mActiveAnimations - 1; 48 | } 49 | } 50 | } 51 | } 52 | 53 | for (AnimationTile animation : cancelledAnimations) { 54 | cancelAnimation(animation); 55 | } 56 | } 57 | 58 | public boolean isAnimationActive() { 59 | if (mActiveAnimations != 0) { 60 | mOneMoreFrame = true; 61 | return true; 62 | } else if (mOneMoreFrame) { 63 | mOneMoreFrame = false; 64 | return true; 65 | } else { 66 | return false; 67 | } 68 | } 69 | 70 | public ArrayList getAnimationCell(int x, int y) { 71 | return mAnimationGameGrid[x][y]; 72 | } 73 | 74 | public void cancelAnimations() { 75 | for (ArrayList[] array : mAnimationGameGrid) { 76 | for (ArrayList list : array) { 77 | list.clear(); 78 | } 79 | } 80 | mGlobalAnimation.clear(); 81 | mActiveAnimations = 0; 82 | } 83 | 84 | void cancelAnimation(AnimationTile animation) { 85 | if (animation.getX() == -1 && animation.getY() == -1) { 86 | mGlobalAnimation.remove(animation); 87 | } else { 88 | mAnimationGameGrid[animation.getX()][animation.getY()].remove(animation); 89 | } 90 | } 91 | 92 | public ArrayList getGlobalAnimationList() { 93 | return mGlobalAnimation; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/Inventory.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | * Represents a block of information about in-app items. 25 | * An Inventory is returned by such methods as {@link IabHelper#queryInventory}. 26 | */ 27 | public class Inventory { 28 | Map mSkuMap = new HashMap(); 29 | Map mPurchaseMap = new HashMap(); 30 | 31 | Inventory() { } 32 | 33 | /** Returns the listing details for an in-app product. */ 34 | public SkuDetails getSkuDetails(String sku) { 35 | return mSkuMap.get(sku); 36 | } 37 | 38 | /** Returns purchase information for a given product, or null if there is no purchase. */ 39 | public Purchase getPurchase(String sku) { 40 | return mPurchaseMap.get(sku); 41 | } 42 | 43 | /** Returns whether or not there exists a purchase of the given product. */ 44 | public boolean hasPurchase(String sku) { 45 | return mPurchaseMap.containsKey(sku); 46 | } 47 | 48 | /** Return whether or not details about the given product are available. */ 49 | public boolean hasDetails(String sku) { 50 | return mSkuMap.containsKey(sku); 51 | } 52 | 53 | /** 54 | * Erase a purchase (locally) from the inventory, given its product ID. This just 55 | * modifies the Inventory object locally and has no effect on the server! This is 56 | * useful when you have an existing Inventory object which you know to be up to date, 57 | * and you have just consumed an item successfully, which means that erasing its 58 | * purchase data from the Inventory you already have is quicker than querying for 59 | * a new Inventory. 60 | */ 61 | public void erasePurchase(String sku) { 62 | if (mPurchaseMap.containsKey(sku)) mPurchaseMap.remove(sku); 63 | } 64 | 65 | /** Returns a list of all owned product IDs. */ 66 | List getAllOwnedSkus() { 67 | return new ArrayList(mPurchaseMap.keySet()); 68 | } 69 | 70 | /** Returns a list of all owned product IDs of a given type */ 71 | List getAllOwnedSkus(String itemType) { 72 | List result = new ArrayList(); 73 | for (Purchase p : mPurchaseMap.values()) { 74 | if (p.getItemType().equals(itemType)) result.add(p.getSku()); 75 | } 76 | return result; 77 | } 78 | 79 | /** Returns a list of all purchases. */ 80 | List getAllPurchases() { 81 | return new ArrayList(mPurchaseMap.values()); 82 | } 83 | 84 | void addSkuDetails(SkuDetails d) { 85 | mSkuMap.put(d.getSku(), d); 86 | } 87 | 88 | void addPurchase(Purchase p) { 89 | mPurchaseMap.put(p.getSku(), p); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/GameGrid.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class GameGrid { 6 | 7 | private final Tile[][] mGrid; 8 | private final Tile[][] mUndoGrid; 9 | private final Tile[][] mBufferGrid; 10 | 11 | public GameGrid(int sizeX, int sizeY) { 12 | mGrid = new Tile[sizeX][sizeY]; 13 | mUndoGrid = new Tile[sizeX][sizeY]; 14 | mBufferGrid = new Tile[sizeX][sizeY]; 15 | clearGrid(); 16 | clearUndoGrid(); 17 | } 18 | 19 | public Position randomAvailableCell() { 20 | ArrayList availableCells = getAvailableCells(); 21 | if (availableCells.size() >= 1) { 22 | return availableCells.get((int) Math.floor(Math.random() * availableCells.size())); 23 | } 24 | return null; 25 | } 26 | 27 | ArrayList getAvailableCells() { 28 | ArrayList availableCells = new ArrayList(); 29 | for (int xx = 0; xx < mGrid.length; xx++) { 30 | for (int yy = 0; yy < mGrid[0].length; yy++) { 31 | if (mGrid[xx][yy] == null) { 32 | availableCells.add(new Position(xx, yy)); 33 | } 34 | } 35 | } 36 | return availableCells; 37 | } 38 | 39 | public boolean isCellsAvailable() { 40 | return (getAvailableCells().size() >= 1); 41 | } 42 | 43 | public boolean isCellAvailable(Position cell) { 44 | return !isCellOccupied(cell); 45 | } 46 | 47 | public boolean isCellOccupied(Position cell) { 48 | return (getTile(cell) != null); 49 | } 50 | 51 | public Tile getTile(Position cell) { 52 | if (cell != null && isCellWithinBounds(cell)) { 53 | return mGrid[cell.getX()][cell.getY()]; 54 | } else { 55 | return null; 56 | } 57 | } 58 | 59 | public Tile getCellContent(int x, int y) { 60 | if (isCellWithinBounds(x, y)) { 61 | return mGrid[x][y]; 62 | } else { 63 | return null; 64 | } 65 | } 66 | 67 | public boolean isCellWithinBounds(Position cell) { 68 | return 0 <= cell.getX() && cell.getX() < mGrid.length 69 | && 0 <= cell.getY() && cell.getY() < mGrid[0].length; 70 | } 71 | 72 | boolean isCellWithinBounds(int x, int y) { 73 | return 0 <= x && x < mGrid.length 74 | && 0 <= y && y < mGrid[0].length; 75 | } 76 | 77 | public void insertTile(Tile tile) { 78 | mGrid[tile.getX()][tile.getY()] = tile; 79 | } 80 | 81 | public void removeTile(Tile tile) { 82 | mGrid[tile.getX()][tile.getY()] = null; 83 | } 84 | 85 | public void saveTiles() { 86 | for (int xx = 0; xx < mBufferGrid.length; xx++) { 87 | for (int yy = 0; yy < mBufferGrid[0].length; yy++) { 88 | if (mBufferGrid[xx][yy] == null) { 89 | mUndoGrid[xx][yy] = null; 90 | } else { 91 | mUndoGrid[xx][yy] = new Tile(xx, yy, mBufferGrid[xx][yy].getValue()); 92 | } 93 | } 94 | } 95 | } 96 | 97 | public void prepareSaveTiles() { 98 | for (int xx = 0; xx < mGrid.length; xx++) { 99 | for (int yy = 0; yy < mGrid[0].length; yy++) { 100 | if (mGrid[xx][yy] == null) { 101 | mBufferGrid[xx][yy] = null; 102 | } else { 103 | mBufferGrid[xx][yy] = new Tile(xx, yy, mGrid[xx][yy].getValue()); 104 | } 105 | } 106 | } 107 | } 108 | 109 | public void revertTiles() { 110 | for (int xx = 0; xx < mUndoGrid.length; xx++) { 111 | for (int yy = 0; yy < mUndoGrid[0].length; yy++) { 112 | if (mUndoGrid[xx][yy] == null) { 113 | mGrid[xx][yy] = null; 114 | } else { 115 | mGrid[xx][yy] = new Tile(xx, yy, mUndoGrid[xx][yy].getValue()); 116 | } 117 | } 118 | } 119 | } 120 | 121 | public void clearGrid() { 122 | for (int xx = 0; xx < mGrid.length; xx++) { 123 | for (int yy = 0; yy < mGrid[0].length; yy++) { 124 | mGrid[xx][yy] = null; 125 | } 126 | } 127 | } 128 | 129 | void clearUndoGrid() { 130 | for (int xx = 0; xx < mGrid.length; xx++) { 131 | for (int yy = 0; yy < mGrid[0].length; yy++) { 132 | mUndoGrid[xx][yy] = null; 133 | } 134 | } 135 | } 136 | 137 | public Tile[][] getGrid() { 138 | return mGrid; 139 | } 140 | 141 | public Tile[][] getUndoGrid() { 142 | return mUndoGrid; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/iap/Security.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package eu.thedarken.myo.twothousandfortyeight.tools.iap; 17 | 18 | import android.text.TextUtils; 19 | import android.util.Log; 20 | import eu.thedarken.myo.twothousandfortyeight.BuildConfig; 21 | 22 | import java.security.*; 23 | import java.security.spec.InvalidKeySpecException; 24 | import java.security.spec.X509EncodedKeySpec; 25 | 26 | /** 27 | * Security-related methods. For a secure implementation, all of this code 28 | * should be implemented on a server that communicates with the 29 | * application on the device. For the sake of simplicity and clarity of this 30 | * example, this code is included here and is executed on the device. If you 31 | * must verify the purchases on the phone, you should obfuscate this code to 32 | * make it harder for an attacker to replace the code with stubs that treat all 33 | * purchases as verified. 34 | */ 35 | public class Security { 36 | private static final String TAG = "IABUtil/Security"; 37 | 38 | private static final String KEY_FACTORY_ALGORITHM = "RSA"; 39 | private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; 40 | 41 | /** 42 | * Verifies that the data was signed with the given signature, and returns 43 | * the verified purchase. The data is in JSON format and signed 44 | * with a private key. The data also contains the {@link PurchaseState} 45 | * and product ID of the purchase. 46 | * @param base64PublicKey the base64-encoded public key to use for verifying. 47 | * @param signedData the signed JSON string (signed, not encrypted) 48 | * @param signature the signature for the data, signed with the private key 49 | */ 50 | public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) { 51 | if (BuildConfig.DEBUG) { 52 | return true; 53 | } 54 | if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) || 55 | TextUtils.isEmpty(signature)) { 56 | Log.e(TAG, "Purchase verification failed: missing data."); 57 | return false; 58 | } 59 | 60 | PublicKey key = Security.generatePublicKey(base64PublicKey); 61 | return Security.verify(key, signedData, signature); 62 | } 63 | 64 | /** 65 | * Generates a PublicKey instance from a string containing the 66 | * Base64-encoded public key. 67 | * 68 | * @param encodedPublicKey Base64-encoded public key 69 | * @throws IllegalArgumentException if encodedPublicKey is invalid 70 | */ 71 | public static PublicKey generatePublicKey(String encodedPublicKey) { 72 | try { 73 | byte[] decodedKey = Base64.decode(encodedPublicKey); 74 | KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM); 75 | return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey)); 76 | } catch (NoSuchAlgorithmException e) { 77 | throw new RuntimeException(e); 78 | } catch (InvalidKeySpecException e) { 79 | Log.e(TAG, "Invalid key specification."); 80 | throw new IllegalArgumentException(e); 81 | } catch (Base64DecoderException e) { 82 | Log.e(TAG, "Base64 decoding failed."); 83 | throw new IllegalArgumentException(e); 84 | } 85 | } 86 | 87 | /** 88 | * Verifies that the signature from the server matches the computed 89 | * signature on the data. Returns true if the data is correctly signed. 90 | * 91 | * @param publicKey public key associated with the developer account 92 | * @param signedData signed data from server 93 | * @param signature server signature 94 | * @return true if the data and signature match 95 | */ 96 | public static boolean verify(PublicKey publicKey, String signedData, String signature) { 97 | Signature sig; 98 | try { 99 | sig = Signature.getInstance(SIGNATURE_ALGORITHM); 100 | sig.initVerify(publicKey); 101 | sig.update(signedData.getBytes()); 102 | if (!sig.verify(Base64.decode(signature))) { 103 | Log.e(TAG, "Signature verification failed."); 104 | return false; 105 | } 106 | return true; 107 | } catch (NoSuchAlgorithmException e) { 108 | Log.e(TAG, "NoSuchAlgorithmException."); 109 | } catch (InvalidKeyException e) { 110 | Log.e(TAG, "Invalid key specification."); 111 | } catch (SignatureException e) { 112 | Log.e(TAG, "Signature exception."); 113 | } catch (Base64DecoderException e) { 114 | Log.e(TAG, "Base64 decoding failed."); 115 | } 116 | return false; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/tools/InputListener.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.tools; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.View; 5 | import eu.thedarken.myo.twothousandfortyeight.game.Game; 6 | 7 | public class InputListener implements View.OnTouchListener { 8 | 9 | private static final int SWIPE_MIN_DISTANCE = 0; 10 | private static final int SWIPE_THRESHOLD_VELOCITY = 25; 11 | private static final int MOVE_THRESHOLD = 250; 12 | private static final int RESET_STARTING = 10; 13 | 14 | private float x; 15 | private float y; 16 | private float lastdx; 17 | private float lastdy; 18 | private float previousX; 19 | private float previousY; 20 | private float startingX; 21 | private float startingY; 22 | private int previousDirection = 1; 23 | private int veryLastDirection = 1; 24 | private boolean hasMoved = false; 25 | 26 | private View mView; 27 | private Game mGame; 28 | 29 | public void setView(View view) { 30 | mView = view; 31 | mView.setOnTouchListener(this); 32 | } 33 | 34 | public void setGame(Game game) { 35 | mGame = game; 36 | } 37 | 38 | public boolean onTouch(View view, MotionEvent event) { 39 | switch (event.getAction()) { 40 | 41 | case MotionEvent.ACTION_DOWN: 42 | x = event.getX(); 43 | y = event.getY(); 44 | startingX = x; 45 | startingY = y; 46 | previousX = x; 47 | previousY = y; 48 | lastdx = 0; 49 | lastdy = 0; 50 | hasMoved = false; 51 | return true; 52 | case MotionEvent.ACTION_MOVE: 53 | x = event.getX(); 54 | y = event.getY(); 55 | if (mGame.isGameOnGoing()) { 56 | float dx = x - previousX; 57 | if (Math.abs(lastdx + dx) < Math.abs(lastdx) + Math.abs(dx) && Math.abs(dx) > RESET_STARTING 58 | && Math.abs(x - startingX) > SWIPE_MIN_DISTANCE) { 59 | startingX = x; 60 | startingY = y; 61 | lastdx = dx; 62 | previousDirection = veryLastDirection; 63 | } 64 | if (lastdx == 0) { 65 | lastdx = dx; 66 | } 67 | float dy = y - previousY; 68 | if (Math.abs(lastdy + dy) < Math.abs(lastdy) + Math.abs(dy) && Math.abs(dy) > RESET_STARTING 69 | && Math.abs(y - startingY) > SWIPE_MIN_DISTANCE) { 70 | startingX = x; 71 | startingY = y; 72 | lastdy = dy; 73 | previousDirection = veryLastDirection; 74 | } 75 | if (lastdy == 0) { 76 | lastdy = dy; 77 | } 78 | if (pathMoved() > SWIPE_MIN_DISTANCE * SWIPE_MIN_DISTANCE && !hasMoved) { 79 | boolean moved = false; 80 | //Vertical 81 | if (((dy >= SWIPE_THRESHOLD_VELOCITY && Math.abs(dy) >= Math.abs(dx)) || y - startingY >= MOVE_THRESHOLD) && previousDirection % 2 != 0) { 82 | moved = true; 83 | previousDirection = previousDirection * 2; 84 | veryLastDirection = 2; 85 | mGame.move(2); 86 | } else if (((dy <= -SWIPE_THRESHOLD_VELOCITY && Math.abs(dy) >= Math.abs(dx)) || y - startingY <= -MOVE_THRESHOLD) && previousDirection % 3 != 0) { 87 | moved = true; 88 | previousDirection = previousDirection * 3; 89 | veryLastDirection = 3; 90 | mGame.move(0); 91 | } 92 | //Horizontal 93 | if (((dx >= SWIPE_THRESHOLD_VELOCITY && Math.abs(dx) >= Math.abs(dy)) || x - startingX >= MOVE_THRESHOLD) && previousDirection % 5 != 0) { 94 | moved = true; 95 | previousDirection = previousDirection * 5; 96 | veryLastDirection = 5; 97 | mGame.move(1); 98 | } else if (((dx <= -SWIPE_THRESHOLD_VELOCITY && Math.abs(dx) >= Math.abs(dy)) || x - startingX <= -MOVE_THRESHOLD) && previousDirection % 7 != 0) { 99 | moved = true; 100 | previousDirection = previousDirection * 7; 101 | veryLastDirection = 7; 102 | mGame.move(3); 103 | } 104 | if (moved) { 105 | hasMoved = true; 106 | startingX = x; 107 | startingY = y; 108 | } 109 | } 110 | } 111 | previousX = x; 112 | previousY = y; 113 | return true; 114 | case MotionEvent.ACTION_UP: 115 | x = event.getX(); 116 | y = event.getY(); 117 | previousDirection = 1; 118 | veryLastDirection = 1; 119 | } 120 | return true; 121 | } 122 | 123 | private float pathMoved() { 124 | return (x - startingX) * (x - startingX) + (y - startingY) * (y - startingY); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /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 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/InfoFragment.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.preference.Preference; 10 | import android.preference.PreferenceFragment; 11 | import android.preference.PreferenceScreen; 12 | import android.view.View; 13 | 14 | 15 | public class InfoFragment extends PreferenceFragment implements MainActivity.HideThemAdsCallback { 16 | private String mAppVersion = "unknown"; 17 | private MainActivity mMainActivity; 18 | private Boolean mHideAds; 19 | private Preference mAddRemoval; 20 | 21 | public static Fragment newInstance() { 22 | return new InfoFragment(); 23 | } 24 | 25 | @Override 26 | public void onAttach(Activity activity) { 27 | mMainActivity = (MainActivity) activity; 28 | super.onAttach(activity); 29 | } 30 | 31 | @Override 32 | public void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | // Load the preferences from an XML resource 35 | addPreferencesFromResource(R.xml.prefs_info); 36 | } 37 | 38 | @Override 39 | public void onViewCreated(View view, Bundle savedInstanceState) { 40 | view.setFitsSystemWindows(true); 41 | super.onViewCreated(view, savedInstanceState); 42 | } 43 | 44 | @Override 45 | public void onActivityCreated(Bundle savedInstanceState) { 46 | super.onActivityCreated(savedInstanceState); 47 | try { 48 | mAppVersion = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName; 49 | } catch (PackageManager.NameNotFoundException e1) { 50 | e1.printStackTrace(); 51 | } 52 | Preference appVersion = findPreference("app.version"); 53 | if (appVersion != null) { 54 | appVersion.setSummary(mAppVersion); 55 | } 56 | mAddRemoval = findPreference("ads.remove"); 57 | if (mAddRemoval != null) { 58 | mAddRemoval.setEnabled(false); 59 | mMainActivity.tellMeAboutTheAds(this); 60 | } 61 | } 62 | 63 | @Override 64 | public void onDestroy() { 65 | if (mMainActivity != null) 66 | mMainActivity.dontTellMeAboutTheAdds(this); 67 | super.onDestroy(); 68 | } 69 | 70 | @Override 71 | public void onAdStateDetermined(boolean hideAds) { 72 | mHideAds = hideAds; 73 | if (mAddRemoval != null) { 74 | if (hideAds) { 75 | mAddRemoval.setEnabled(false); 76 | mAddRemoval.setTitle(R.string.ads_removed); 77 | } else { 78 | mAddRemoval.setEnabled(true); 79 | mAddRemoval.setTitle(R.string.remove_ads); 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { 86 | if (preference != null && preference.getKey() != null) { 87 | if (preference.getKey().equals("myo.info")) { 88 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.thalmic.com/en/myo/"))); 89 | } else if (preference.getKey().equals("myo.purchase")) { 90 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.thalmic.com/en/myo/preorder/"))); 91 | } else if (preference.getKey().equals("ads.remove")) { 92 | if (mMainActivity != null) { 93 | if (!mHideAds) { 94 | mMainActivity.purchaseUpgrade(); 95 | } 96 | } 97 | } else if (preference.getKey().equals("help.mail")) { 98 | createSupportEmail(); 99 | } else if (preference.getKey().equals("help.twitter")) { 100 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://twitter.com/d4rken"))); 101 | } else if (preference.getKey().equals("help.gplus")) { 102 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://plus.google.com/116634499773478773276"))); 103 | } else if (preference.getKey().equals("source.github")) { 104 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/d4rken/myo-android-2048"))); 105 | } else if (preference.getKey().equals("source.base")) { 106 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/tpcstld/2048"))); 107 | } else if (preference.getKey().equals("source.orig")) { 108 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/gabrielecirulli/2048"))); 109 | } else if (preference.getKey().equals("app.version")) { 110 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=eu.thedarken.myo.twothousandfortyeight"))); 111 | } 112 | 113 | } 114 | return super.onPreferenceTreeClick(preferenceScreen, preference); 115 | } 116 | 117 | private void createSupportEmail() { 118 | Intent intent = new Intent(Intent.ACTION_SEND); 119 | intent.setType("message/rfc822"); 120 | intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"support@thedarken.eu"}); 121 | 122 | String subject = "[2048 Myo edition] Question/Suggestion/Feedback"; 123 | 124 | 125 | intent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject); 126 | intent.putExtra(android.content.Intent.EXTRA_TEXT, "\n\n" + "(I'm using app version v" + mAppVersion + ")"); 127 | startActivity(Intent.createChooser(intent, "")); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_welcome_layout.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 41 | 42 | 56 | 57 | 73 | 74 | 81 | 82 | 87 | 88 | 98 | 99 | 104 | 105 | 111 | 112 | 113 | 123 | 124 | 130 | 131 | 136 | 137 | 138 | 139 | 140 | 145 | 146 | 156 | 157 | 162 | 163 | 169 | 170 | 171 | 181 | 182 | 189 | 190 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.vending.billing; 18 | 19 | import android.os.Bundle; 20 | 21 | /** 22 | * InAppBillingService is the service that provides in-app billing version 3 and beyond. 23 | * This service provides the following features: 24 | * 1. Provides a new API to get details of in-app items published for the app including 25 | * price, type, title and description. 26 | * 2. The purchase flow is synchronous and purchase information is available immediately 27 | * after it completes. 28 | * 3. Purchase information of in-app purchases is maintained within the Google Play system 29 | * till the purchase is consumed. 30 | * 4. An API to consume a purchase of an inapp item. All purchases of one-time 31 | * in-app items are consumable and thereafter can be purchased again. 32 | * 5. An API to get current purchases of the user immediately. This will not contain any 33 | * consumed purchases. 34 | * 35 | * All calls will give a response code with the following possible values 36 | * RESULT_OK = 0 - success 37 | * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog 38 | * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested 39 | * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase 40 | * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API 41 | * RESULT_ERROR = 6 - Fatal error during the API action 42 | * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned 43 | * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned 44 | */ 45 | interface IInAppBillingService { 46 | /** 47 | * Checks support for the requested billing API version, package and in-app type. 48 | * Minimum API version supported by this interface is 3. 49 | * @param apiVersion the billing version which the app is using 50 | * @param packageName the package name of the calling app 51 | * @param type type of the in-app item being purchased "inapp" for one-time purchases 52 | * and "subs" for subscription. 53 | * @return RESULT_OK(0) on success, corresponding result code on failures 54 | */ 55 | int isBillingSupported(int apiVersion, String packageName, String type); 56 | 57 | /** 58 | * Provides details of a list of SKUs 59 | * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle 60 | * with a list JSON strings containing the productId, price, title and description. 61 | * This API can be called with a maximum of 20 SKUs. 62 | * @param apiVersion billing API version that the Third-party is using 63 | * @param packageName the package name of the calling app 64 | * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" 65 | * @return Bundle containing the following key-value pairs 66 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on 67 | * failure as listed above. 68 | * "DETAILS_LIST" with a StringArrayList containing purchase information 69 | * in JSON format similar to: 70 | * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", 71 | * "title : "Example Title", "description" : "This is an example description" }' 72 | */ 73 | Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); 74 | 75 | /** 76 | * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, 77 | * the type, a unique purchase token and an optional developer payload. 78 | * @param apiVersion billing API version that the app is using 79 | * @param packageName package name of the calling app 80 | * @param sku the SKU of the in-app item as published in the developer console 81 | * @param type the type of the in-app item ("inapp" for one-time purchases 82 | * and "subs" for subscription). 83 | * @param developerPayload optional argument to be sent back with the purchase information 84 | * @return Bundle containing the following key-value pairs 85 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on 86 | * failure as listed above. 87 | * "BUY_INTENT" - PendingIntent to start the purchase flow 88 | * 89 | * The Pending intent should be launched with startIntentSenderForResult. When purchase flow 90 | * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. 91 | * If the purchase is successful, the result data will contain the following key-value pairs 92 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on 93 | * failure as listed above. 94 | * "INAPP_PURCHASE_DATA" - String in JSON format similar to 95 | * '{"orderId":"12999763169054705758.1371079406387615", 96 | * "packageName":"com.example.app", 97 | * "productId":"exampleSku", 98 | * "purchaseTime":1345678900000, 99 | * "purchaseToken" : "122333444455555", 100 | * "developerPayload":"example developer payload" }' 101 | * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that 102 | * was signed with the private key of the developer 103 | * TODO: change this to app-specific keys. 104 | */ 105 | Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, 106 | String developerPayload); 107 | 108 | /** 109 | * Returns the current SKUs owned by the user of the type and package name specified along with 110 | * purchase information and a signature of the data to be validated. 111 | * This will return all SKUs that have been purchased in V3 and managed items purchased using 112 | * V1 and V2 that have not been consumed. 113 | * @param apiVersion billing API version that the app is using 114 | * @param packageName package name of the calling app 115 | * @param type the type of the in-app items being requested 116 | * ("inapp" for one-time purchases and "subs" for subscription). 117 | * @param continuationToken to be set as null for the first call, if the number of owned 118 | * skus are too many, a continuationToken is returned in the response bundle. 119 | * This method can be called again with the continuation token to get the next set of 120 | * owned skus. 121 | * @return Bundle containing the following key-value pairs 122 | * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on 123 | * failure as listed above. 124 | * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs 125 | * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information 126 | * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures 127 | * of the purchase information 128 | * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the 129 | * next set of in-app purchases. Only set if the 130 | * user has more owned skus than the current list. 131 | */ 132 | Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); 133 | 134 | /** 135 | * Consume the last purchase of the given SKU. This will result in this item being removed 136 | * from all subsequent responses to getPurchases() and allow re-purchase of this item. 137 | * @param apiVersion billing API version that the app is using 138 | * @param packageName package name of the calling app 139 | * @param purchaseToken token in the purchase information JSON that identifies the purchase 140 | * to be consumed 141 | * @return 0 if consumption succeeded. Appropriate error values for failures. 142 | */ 143 | int consumePurchase(int apiVersion, String packageName, String purchaseToken); 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/WelcomeFragment.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.view.animation.Animation; 12 | import android.view.animation.AnimationUtils; 13 | import android.widget.ImageView; 14 | import android.widget.LinearLayout; 15 | import android.widget.TextView; 16 | 17 | import com.thalmic.myo.Arm; 18 | import com.thalmic.myo.DeviceListener; 19 | import com.thalmic.myo.Hub; 20 | import com.thalmic.myo.Myo; 21 | import com.thalmic.myo.Pose; 22 | import com.thalmic.myo.Quaternion; 23 | import com.thalmic.myo.Vector3; 24 | import com.thalmic.myo.XDirection; 25 | import com.thalmic.myo.scanner.ScanActivity; 26 | 27 | import eu.thedarken.myo.twothousandfortyeight.tools.Logy; 28 | 29 | 30 | public class WelcomeFragment extends Fragment implements DeviceListener { 31 | private static final String TAG = "2048Myo:WelcomeFragment"; 32 | private TextView mConnectMyo, mSetupMyo, mWhenReady, mPlayNow; 33 | private ImageView mLeftIcon, mRightIcon, mDownIcon, mUpIcon; 34 | private LinearLayout mControlLayout, mUpBox, mDownBox, mLeftBox, mRightBox; 35 | private Hub mMyoHub; 36 | private static final long REFRESHINTERVAL = 250; 37 | private boolean mConnected = false; 38 | private boolean mSynced = false; 39 | private Animation mAnimWiggle; 40 | 41 | public static Fragment newInstance() { 42 | return new WelcomeFragment(); 43 | } 44 | 45 | @Override 46 | public void onAttach(Activity activity) { 47 | super.onAttach(activity); 48 | mMyoHub = ((MainActivity) activity).getMyoHub(); 49 | } 50 | 51 | @Override 52 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 53 | View layout = inflater.inflate(R.layout.fragment_welcome_layout, container, false); 54 | mConnectMyo = (TextView) layout.findViewById(R.id.tv_connect_myo); 55 | mSetupMyo = (TextView) layout.findViewById(R.id.tv_setup_myo); 56 | mWhenReady = (TextView) layout.findViewById(R.id.tv_when_ready); 57 | mPlayNow = (TextView) layout.findViewById(R.id.tv_play_now); 58 | mControlLayout = (LinearLayout) layout.findViewById(R.id.ll_controls); 59 | mLeftIcon = (ImageView) layout.findViewById(R.id.iv_gesture_left); 60 | mRightIcon = (ImageView) layout.findViewById(R.id.iv_gesture_right); 61 | mUpIcon = (ImageView) layout.findViewById(R.id.iv_gesture_up); 62 | mDownIcon = (ImageView) layout.findViewById(R.id.iv_gesture_down); 63 | 64 | mUpBox = (LinearLayout) layout.findViewById(R.id.ll_up_layout); 65 | mDownBox = (LinearLayout) layout.findViewById(R.id.ll_down_layout); 66 | mLeftBox = (LinearLayout) layout.findViewById(R.id.ll_left_layout); 67 | mRightBox = (LinearLayout) layout.findViewById(R.id.ll_right_layout); 68 | return layout; 69 | } 70 | 71 | @Override 72 | public void onActivityCreated(Bundle savedInstanceState) { 73 | mConnectMyo.setOnClickListener(new View.OnClickListener() { 74 | @Override 75 | public void onClick(View v) { 76 | if (mMyoHub != null) { 77 | Intent intent = new Intent(getActivity(), ScanActivity.class); 78 | startActivity(intent); 79 | } 80 | } 81 | }); 82 | mSetupMyo.setOnClickListener(new View.OnClickListener() { 83 | @Override 84 | public void onClick(View v) { 85 | if (!mMyoHub.getConnectedDevices().isEmpty()) { 86 | Intent intent = new Intent(Intent.ACTION_VIEW); 87 | intent.setData(Uri.parse("https://support.getmyo.com/hc/en-us/articles/200755509-How-to-perform-the-sync-gesture")); 88 | startActivity(intent); 89 | } 90 | } 91 | }); 92 | mPlayNow.setOnClickListener(new View.OnClickListener() { 93 | @Override 94 | public void onClick(View v) { 95 | getFragmentManager().popBackStack(); 96 | } 97 | }); 98 | refresh(); 99 | if (getView() != null) 100 | getView().postDelayed(new Runnable() { 101 | @Override 102 | public void run() { 103 | if (getView() != null && isAdded()) { 104 | getView().postDelayed(this, REFRESHINTERVAL); 105 | refresh(); 106 | } 107 | } 108 | }, REFRESHINTERVAL); 109 | 110 | if (mMyoHub != null) { 111 | mMyoHub.addListener(this); 112 | } 113 | 114 | mAnimWiggle = AnimationUtils.loadAnimation(getActivity(), R.anim.wiggle); 115 | super.onActivityCreated(savedInstanceState); 116 | } 117 | 118 | @Override 119 | public void onDestroy() { 120 | if (mMyoHub != null) 121 | mMyoHub.removeListener(this); 122 | super.onDestroy(); 123 | } 124 | 125 | private void setArm(boolean left) { 126 | if (left) { 127 | GameFragment.sReversed = true; 128 | mLeftIcon.setImageResource(R.drawable.ic_pose_waveleft_lh); 129 | mRightIcon.setImageResource(R.drawable.ic_pose_waveright_lh); 130 | mUpIcon.setImageResource(R.drawable.ic_pose_spread_lh); 131 | mDownIcon.setImageResource(R.drawable.ic_pose_fist_lh); 132 | } else { 133 | GameFragment.sReversed = false; 134 | mLeftIcon.setImageResource(R.drawable.ic_pose_waveleft); 135 | mRightIcon.setImageResource(R.drawable.ic_pose_waveright); 136 | mUpIcon.setImageResource(R.drawable.ic_pose_spread); 137 | mDownIcon.setImageResource(R.drawable.ic_pose_fist); 138 | } 139 | } 140 | 141 | private void refresh() { 142 | if (mMyoHub != null) { 143 | setArm(GameFragment.sReversed); 144 | mConnectMyo.setVisibility(mConnected ? View.GONE : View.VISIBLE); 145 | mSetupMyo.setVisibility(mConnected && !mSynced ? View.VISIBLE : View.GONE); 146 | mWhenReady.setVisibility(mConnected && mSynced ? View.VISIBLE : View.GONE); 147 | mPlayNow.setVisibility(mConnected && mSynced ? View.VISIBLE : View.GONE); 148 | mControlLayout.setVisibility(mConnected && mSynced ? View.VISIBLE : View.GONE); 149 | } else { 150 | mConnectMyo.setVisibility(View.GONE); 151 | mSetupMyo.setTextColor(View.GONE); 152 | mSetupMyo.setVisibility(View.GONE); 153 | mWhenReady.setVisibility(View.GONE); 154 | mPlayNow.setVisibility(View.GONE); 155 | mControlLayout.setVisibility(View.GONE); 156 | } 157 | } 158 | 159 | @Override 160 | public void onAttach(Myo myo, long l) { 161 | Logy.d(TAG, "onAttach"); 162 | } 163 | 164 | @Override 165 | public void onDetach(Myo myo, long l) { 166 | Logy.d(TAG, "onDetach"); 167 | } 168 | 169 | @Override 170 | public void onConnect(Myo myo, long l) { 171 | Logy.d(TAG, "onConnect"); 172 | mConnected = true; 173 | if (myo.isUnlocked()) 174 | mSynced = true; 175 | } 176 | 177 | @Override 178 | public void onDisconnect(Myo myo, long l) { 179 | Logy.d(TAG, "onDisconnect"); 180 | mConnected = false; 181 | } 182 | 183 | @Override 184 | public void onArmSync(Myo myo, long l, Arm arm, XDirection xDirection) { 185 | Logy.d(TAG, "onArmSync"); 186 | mSynced = true; 187 | GameFragment.sReversed = arm == Arm.LEFT; 188 | myo.unlock(Myo.UnlockType.HOLD); 189 | } 190 | 191 | @Override 192 | public void onArmUnsync(Myo myo, long l) { 193 | Logy.d(TAG, "onArmUnsync"); 194 | mSynced = false; 195 | } 196 | 197 | @Override 198 | public void onUnlock(Myo myo, long l) { 199 | Logy.d(TAG, "onUnlock"); 200 | } 201 | 202 | @Override 203 | public void onLock(Myo myo, long l) { 204 | Logy.d(TAG, "onLock"); 205 | } 206 | 207 | @Override 208 | public void onPose(Myo myo, long l, Pose pose) { 209 | Logy.i(TAG, "onPose:" + pose.name()); 210 | if(!isResumed()) 211 | return; 212 | 213 | if (pose == Pose.FIST) { 214 | mDownBox.startAnimation(mAnimWiggle); 215 | } else if (pose == Pose.WAVE_OUT) { 216 | if (GameFragment.sReversed) { 217 | mLeftBox.startAnimation(mAnimWiggle); 218 | } else { 219 | mRightBox.startAnimation(mAnimWiggle); 220 | } 221 | } else if (pose == Pose.WAVE_IN) { 222 | if (GameFragment.sReversed) { 223 | mRightBox.startAnimation(mAnimWiggle); 224 | } else { 225 | mLeftBox.startAnimation(mAnimWiggle); 226 | } 227 | } else if (pose == Pose.FINGERS_SPREAD) { 228 | mUpBox.startAnimation(mAnimWiggle); 229 | } 230 | } 231 | 232 | @Override 233 | public void onOrientationData(Myo myo, long l, Quaternion quaternion) { 234 | 235 | } 236 | 237 | @Override 238 | public void onAccelerometerData(Myo myo, long l, Vector3 vector3) { 239 | 240 | } 241 | 242 | @Override 243 | public void onGyroscopeData(Myo myo, long l, Vector3 vector3) { 244 | 245 | } 246 | 247 | @Override 248 | public void onRssi(Myo myo, long l, int i) { 249 | 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_game_layout.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 26 | 27 | 35 | 36 | 48 | 49 | 50 | 51 | 57 | 58 | 69 | 70 | 80 | 81 | 89 | 90 | 91 | 102 | 103 | 113 | 114 | 122 | 123 | 124 | 125 | 134 | 135 | 136 | 146 | 147 | 148 | 158 | 159 | 169 | 170 | 180 | 181 | 182 | 183 | 192 | 193 | 198 | 199 | 213 | 214 | 215 | 216 | 226 | 227 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /app/src/main/res/layout-large-land/fragment_game_layout.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 16 | 17 | 22 | 23 | 30 | 31 | 40 | 41 | 49 | 50 | 62 | 63 | 64 | 65 | 69 | 70 | 81 | 82 | 93 | 94 | 102 | 103 | 104 | 115 | 116 | 127 | 128 | 136 | 137 | 138 | 139 | 145 | 146 | 147 | 157 | 158 | 168 | 169 | 170 | 180 | 181 | 191 | 192 | 193 | 194 | 195 | 196 | 207 | 208 | 215 | 216 | 217 | 218 | 219 | 230 | 231 | 236 | 237 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/fragment_game_layout.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 21 | 22 | 30 | 31 | 40 | 41 | 49 | 50 | 62 | 63 | 64 | 65 | 71 | 72 | 83 | 84 | 94 | 95 | 103 | 104 | 105 | 116 | 117 | 127 | 128 | 136 | 137 | 138 | 139 | 148 | 149 | 150 | 160 | 161 | 171 | 172 | 173 | 183 | 184 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 210 | 211 | 216 | 217 | 231 | 232 | 233 | 234 | 244 | 245 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/MainActivity.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight; 2 | 3 | import android.app.Activity; 4 | import android.app.Fragment; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.view.KeyEvent; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | import android.widget.Toast; 11 | 12 | import com.crashlytics.android.Crashlytics; 13 | import com.thalmic.myo.Hub; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import eu.thedarken.myo.twothousandfortyeight.tools.KeyListener; 19 | import eu.thedarken.myo.twothousandfortyeight.tools.Logy; 20 | import eu.thedarken.myo.twothousandfortyeight.tools.iap.IabHelper; 21 | import eu.thedarken.myo.twothousandfortyeight.tools.iap.IabResult; 22 | import eu.thedarken.myo.twothousandfortyeight.tools.iap.Inventory; 23 | import eu.thedarken.myo.twothousandfortyeight.tools.iap.Purchase; 24 | import io.fabric.sdk.android.Fabric; 25 | 26 | 27 | public class MainActivity extends Activity { 28 | private static final String TAG = "2048Myo:MainActivity"; 29 | private static final int RC_REQUEST = 2048; 30 | private Fragment mGameFragment; 31 | private Hub mMyoHub; 32 | private IabHelper mHelper; 33 | private final Object mSyncObject = new Object(); 34 | private Purchase mNoAdsPurchase; 35 | private Boolean mIsAdFree; 36 | private static final String SKU_NOADS = "purchase.removeads"; 37 | private final List mAdStateCallbacks = new ArrayList(); 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | if (BuildConfig.BUILD_TYPE.equals("release")) 43 | Fabric.with(this, new Crashlytics()); 44 | setContentView(eu.thedarken.myo.twothousandfortyeight.R.layout.activity_main); 45 | Window w = getWindow(); 46 | w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); 47 | w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 48 | w.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 49 | if (savedInstanceState == null) { 50 | getFragmentManager().beginTransaction() 51 | .add(eu.thedarken.myo.twothousandfortyeight.R.id.container, new GameFragment()) 52 | .commit(); 53 | } 54 | if (mIsAdFree == null) { 55 | initIAP(); 56 | } else { 57 | 58 | } 59 | } 60 | 61 | public Hub getMyoHub() { 62 | if (mMyoHub == null) { 63 | mMyoHub = Hub.getInstance(); 64 | if (!mMyoHub.init(this)) { 65 | Logy.e(TAG, "Could not initialize the Hub."); 66 | mMyoHub = null; 67 | } 68 | } 69 | return mMyoHub; 70 | } 71 | 72 | @Override 73 | public boolean onKeyDown(int keyCode, KeyEvent event) { 74 | if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 75 | Fragment current = getFragmentManager().findFragmentById(eu.thedarken.myo.twothousandfortyeight.R.id.container); 76 | if (current != null && current instanceof KeyListener) { 77 | return ((KeyListener) current).onKeyDown(keyCode, event); 78 | } 79 | } 80 | return super.onKeyDown(keyCode, event); 81 | } 82 | 83 | // We're being destroyed. It's important to dispose of the helper here! 84 | @Override 85 | public void onDestroy() { 86 | // very important: 87 | Logy.d(TAG, "Destroying helper."); 88 | if (mHelper != null) { 89 | mHelper.dispose(); 90 | mHelper = null; 91 | } 92 | super.onDestroy(); 93 | } 94 | 95 | public interface HideThemAdsCallback { 96 | /** 97 | * Callback when the IAP state has been determined 98 | * 99 | * @param hideAds true if ads should be hidden 100 | */ 101 | public void onAdStateDetermined(boolean hideAds); 102 | } 103 | 104 | public void tellMeAboutTheAds(HideThemAdsCallback callback) { 105 | synchronized (mSyncObject) { 106 | if (!mAdStateCallbacks.contains(callback)) 107 | mAdStateCallbacks.add(callback); 108 | if (mIsAdFree != null) { 109 | callback.onAdStateDetermined(mIsAdFree); 110 | } 111 | } 112 | } 113 | 114 | public void dontTellMeAboutTheAdds(HideThemAdsCallback callback) { 115 | mAdStateCallbacks.remove(callback); 116 | } 117 | 118 | // User clicked the "Upgrade to Premium" button. 119 | public void purchaseUpgrade() { 120 | if (mHelper != null && mNoAdsPurchase == null) { 121 | // No payload because we don't have to secure an open source app against piracy 122 | String payload = ""; 123 | Logy.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade."); 124 | mHelper.launchPurchaseFlow(this, SKU_NOADS, RC_REQUEST, mPurchaseFinishedListener, payload); 125 | } 126 | } 127 | 128 | // public void consumePurchase() { 129 | // if (mHelper != null && mNoAdsPurchase != null) { 130 | // mHelper.consumeAsync(mNoAdsPurchase, mConsumeFinishedListener); 131 | // } 132 | // } 133 | 134 | private void initIAP() { 135 | final String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2EJHuxh5xWoD/FPe2a4tEbCr7oUmu2RZvAUx/dIM5ER4RXjZxb4LEZRcf8Vh0z0AXjnY0SdtB58cyZ8Bw6GIb8TgGTs00WQMbJ9OGG0u/S9F/7L77Na7/iFpMJ8SAYye3uPNI3AzJ1AhuCkYb7fMdkDo7YkWp9iUJB+/6VV32XXtgz7JIqxuupDdYddONzNAsu+zj/oq3mvd/QGVxK61UdpjnMPDkKAK6+jPMH+c/5NsEuuoRGOZYnerlQg/MxRjNo0GdvFYItKYsxl8xP7FZqL241TpGRGf9DAxc/H4bXAcU1GcA/WLyOMwzUMgslDjtvDDNg1SJDlR1+imJsYOwIDAQAB"; 136 | // Create the helper, passing it our context and the public key to verify signatures with 137 | Logy.d(TAG, "Creating IAB helper."); 138 | mHelper = new IabHelper(this, base64EncodedPublicKey); 139 | 140 | // enable debug logging (for a production application, you should set this to false). 141 | mHelper.enableDebugLogging(BuildConfig.DEBUG); 142 | 143 | // Start setup. This is asynchronous and the specified listener 144 | // will be called once setup completes. 145 | Logy.d(TAG, "Starting setup."); 146 | mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { 147 | public void onIabSetupFinished(IabResult result) { 148 | Logy.d(TAG, "Setup finished."); 149 | 150 | if (!result.isSuccess()) { 151 | // Oh noes, there was a problem. 152 | Logy.e(TAG, "No Google Play? No Problem. We don't care."); 153 | return; 154 | } 155 | 156 | // Have we been disposed of in the meantime? If so, quit. 157 | if (mHelper == null) return; 158 | 159 | // IAB is fully set up. Now, let's get an inventory of stuff we own. 160 | Logy.d(TAG, "Setup successful. Querying inventory."); 161 | mHelper.queryInventoryAsync(mGotInventoryListener); 162 | } 163 | }); 164 | } 165 | 166 | // Listener that's called when we finish querying the items and subscriptions we own 167 | private final IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { 168 | public void onQueryInventoryFinished(IabResult result, Inventory inventory) { 169 | Logy.d(TAG, "Query inventory finished."); 170 | 171 | // Have we been disposed of in the meantime? If so, quit. 172 | if (mHelper == null) return; 173 | 174 | if (result.isFailure()) { 175 | Logy.e(TAG, "Failed to retrieve inventory."); 176 | return; 177 | } 178 | 179 | Logy.d(TAG, "Query inventory was successful."); 180 | /* 181 | * Check for items we own. Notice that for each purchase, we check 182 | * the developer payload to see if it's correct! See 183 | * verifyDeveloperPayload(). 184 | */ 185 | 186 | // Do we have the premium upgrade? 187 | synchronized (mSyncObject) { 188 | mNoAdsPurchase = inventory.getPurchase(SKU_NOADS); 189 | mIsAdFree = (mNoAdsPurchase != null && verifyDeveloperPayload(mNoAdsPurchase)); 190 | for (HideThemAdsCallback callback : mAdStateCallbacks) { 191 | callback.onAdStateDetermined(mIsAdFree); 192 | } 193 | } 194 | 195 | Logy.d(TAG, "AD FREE? " + (mIsAdFree ? "YES" : "NO")); 196 | Logy.d(TAG, "Initial inventory query finished; enabling main UI."); 197 | } 198 | }; 199 | 200 | @Override 201 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 202 | Logy.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); 203 | if (mHelper == null) return; 204 | 205 | // Pass on the activity result to the helper for handling 206 | if (!mHelper.handleActivityResult(requestCode, resultCode, data)) { 207 | // not handled, so handle it ourselves (here's where you'd 208 | // perform any handling of activity results not related to in-app 209 | // billing... 210 | super.onActivityResult(requestCode, resultCode, data); 211 | } else { 212 | Logy.d(TAG, "onActivityResult handled by IABUtil."); 213 | } 214 | } 215 | 216 | private boolean verifyDeveloperPayload(Purchase p) { 217 | /** 218 | * Yeah, well, we could make it SUPER PIRACY PROOF, or not. 219 | * This app will be open source, so put your hands up in the air, 220 | * BECAUSE WE DON'T CARE \o/! 221 | */ 222 | return true; 223 | } 224 | 225 | // Callback for when a purchase is finished 226 | private final IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { 227 | public void onIabPurchaseFinished(IabResult result, Purchase purchase) { 228 | Logy.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase); 229 | 230 | // if we were disposed of in the meantime, quit. 231 | if (mHelper == null) return; 232 | 233 | if (result.isFailure()) { 234 | Logy.e(TAG, "result was FAILURE"); 235 | Toast.makeText(MainActivity.this, getString(R.string.purchase_unsuccessfull), Toast.LENGTH_LONG).show(); 236 | return; 237 | } 238 | if (!verifyDeveloperPayload(purchase)) { 239 | Logy.e(TAG, "verifyDeveloperPayload failed, but we don't care"); 240 | return; 241 | } 242 | 243 | Logy.d(TAG, "Purchase successful."); 244 | if (purchase.getSku().equals(SKU_NOADS)) { 245 | // bought the premium upgrade! 246 | Logy.d(TAG, "Purchase is premium upgrade. Congratulating user."); 247 | Toast.makeText(MainActivity.this, eu.thedarken.myo.twothousandfortyeight.R.string.thank_you, Toast.LENGTH_LONG).show(); 248 | mIsAdFree = true; 249 | synchronized (mSyncObject) { 250 | mNoAdsPurchase = purchase; 251 | mIsAdFree = (purchase != null && verifyDeveloperPayload(purchase)); 252 | for (HideThemAdsCallback callback : mAdStateCallbacks) { 253 | callback.onAdStateDetermined(mIsAdFree); 254 | } 255 | } 256 | } 257 | } 258 | }; 259 | 260 | // Called when consumption is complete 261 | // private final IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() { 262 | // public void onConsumeFinished(Purchase purchase, IabResult result) { 263 | // Logy.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result); 264 | // 265 | // // if we were disposed of in the meantime, quit. 266 | // if (mHelper == null) return; 267 | // 268 | // // We know this is the "gas" sku because it's the only one we consume, 269 | // // so we don't check which sku was consumed. If you have more than one 270 | // // sku, you probably should check... 271 | // if (result.isSuccess()) { 272 | // // successfully consumed, so we apply the effects of the item in our 273 | // // game world's logic, which in our case means filling the gas tank a bit 274 | // Logy.d(TAG, "Consumption successful."); 275 | // synchronized (mSyncObject) { 276 | // mIsAdFree = false; 277 | // mNoAdsPurchase = null; 278 | // for (HideThemAdsCallback callback : mAdStateCallbacks) { 279 | // callback.onAdStateDetermined(mIsAdFree); 280 | // } 281 | // } 282 | // } else { 283 | // // We fail silently 284 | // } 285 | // Logy.d(TAG, "End consumption flow."); 286 | // } 287 | // }; 288 | 289 | 290 | } 291 | -------------------------------------------------------------------------------- /app/src/main/java/eu/thedarken/myo/twothousandfortyeight/game/Game.java: -------------------------------------------------------------------------------- 1 | package eu.thedarken.myo.twothousandfortyeight.game; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import eu.thedarken.myo.twothousandfortyeight.BuildConfig; 10 | 11 | public class Game { 12 | private static final int startingMaxValue = 2048; 13 | private int endingMaxValue; 14 | private GameGrid mGameGrid = null; 15 | static final int DEFAULT_HEIGHT_X = 4; 16 | static final int DEFAULT_WIDTH_Y = 4; 17 | static final int DEFAULT_TILE_TYPES = 24; 18 | private static final int DEFAULT_STARTING_TILES = 2; 19 | private int mPositionsX = DEFAULT_HEIGHT_X; 20 | private int mPositionsY = DEFAULT_WIDTH_Y; 21 | private int mTileTypes = DEFAULT_TILE_TYPES; 22 | private int mStartingTiles = DEFAULT_STARTING_TILES; 23 | private boolean mCanUndo; 24 | private State mLastGameState; 25 | private State mBufferGameState; 26 | private State mGameState = State.NORMAL; 27 | private Context mContext; 28 | private GameView mView; 29 | private ScoreListener mScoreListener; 30 | private long mScore = 0; 31 | private long mLastScore = 0; 32 | private long mBufferScore = 0; 33 | private GameStateListener mGameStateListener; 34 | 35 | public enum State { 36 | NORMAL, WON, LOST, ENDLESS, ENLESS_WON 37 | } 38 | 39 | public Game(Context context) { 40 | mContext = context; 41 | } 42 | 43 | 44 | public interface ScoreListener { 45 | public void onNewScore(long score); 46 | } 47 | 48 | public interface GameStateListener { 49 | public void onGameStateChanged(State state); 50 | } 51 | 52 | public void setGameStateListener(GameStateListener listener) { 53 | this.mGameStateListener = listener; 54 | } 55 | 56 | public GameGrid getGameGrid() { 57 | return mGameGrid; 58 | } 59 | 60 | public boolean isCanUndo() { 61 | return mCanUndo; 62 | } 63 | 64 | public void setCanUndo(boolean canUndo) { 65 | mCanUndo = canUndo; 66 | } 67 | 68 | public State getLastGameState() { 69 | return mLastGameState; 70 | } 71 | 72 | public void setLastGameState(State lastGameState) { 73 | mLastGameState = lastGameState; 74 | } 75 | 76 | public State getGameState() { 77 | return mGameState; 78 | } 79 | 80 | public long getScore() { 81 | return mScore; 82 | } 83 | 84 | public void setScore(long score) { 85 | mScore = score; 86 | } 87 | 88 | public long getLastScore() { 89 | return mLastScore; 90 | } 91 | 92 | public void setLastScore(long lastScore) { 93 | mLastScore = lastScore; 94 | } 95 | 96 | public void setScoreListener(ScoreListener listener) { 97 | mScoreListener = listener; 98 | } 99 | 100 | boolean isGameWon() { 101 | return mGameState == State.WON || mGameState == State.ENLESS_WON; 102 | } 103 | 104 | public boolean isGameOnGoing() { 105 | return mGameState != State.WON && mGameState != State.LOST && mGameState != State.ENLESS_WON; 106 | } 107 | 108 | public boolean isEndlessMode() { 109 | return mGameState == State.ENDLESS || mGameState == State.ENLESS_WON; 110 | } 111 | 112 | public void setup(GameView view) { 113 | mView = view; 114 | } 115 | 116 | private void updateScore(long score) { 117 | mScore = score; 118 | if (mScoreListener != null) 119 | mScoreListener.onNewScore(mScore); 120 | } 121 | 122 | public void newGame() { 123 | if (mGameGrid == null) { 124 | mGameGrid = new GameGrid(mPositionsX, mPositionsY); 125 | } else { 126 | prepareUndoState(); 127 | saveUndoState(); 128 | mGameGrid.clearGrid(); 129 | } 130 | 131 | if (BuildConfig.DEBUG) { 132 | int value = 2; 133 | for (Position pos : mGameGrid.getAvailableCells().subList(0, mGameGrid.getAvailableCells().size())) { 134 | spawnTile(new Tile(pos, value)); 135 | value *= 2; 136 | } 137 | } 138 | endingMaxValue = (int) Math.pow(2, mTileTypes - 1); 139 | mView.updateGrid(mGameGrid); 140 | 141 | updateScore(0); 142 | updateGameState(State.NORMAL); 143 | mView.setGameState(mGameState); 144 | addStartTiles(); 145 | mView.setRefreshLastTime(true); 146 | mView.resyncTime(); 147 | mView.invalidate(); 148 | } 149 | 150 | private void addStartTiles() { 151 | for (int xx = 0; xx < mStartingTiles; xx++) { 152 | addRandomTile(); 153 | } 154 | } 155 | 156 | private void addRandomTile() { 157 | if (mGameGrid.isCellsAvailable()) { 158 | int value = Math.random() < 0.9 ? 2 : 4; 159 | Tile tile = new Tile(mGameGrid.randomAvailableCell(), value); 160 | spawnTile(tile); 161 | } 162 | } 163 | 164 | private void spawnTile(Tile tile) { 165 | mGameGrid.insertTile(tile); 166 | mView.spawnTile(tile); 167 | } 168 | 169 | private void prepareTiles() { 170 | for (Tile[] array : mGameGrid.getGrid()) { 171 | for (Tile tile : array) { 172 | if (mGameGrid.isCellOccupied(tile)) { 173 | tile.setMergedFrom(null); 174 | } 175 | } 176 | } 177 | } 178 | 179 | private void moveTile(Tile tile, Position cell) { 180 | mGameGrid.getGrid()[tile.getX()][tile.getY()] = null; 181 | mGameGrid.getGrid()[cell.getX()][cell.getY()] = tile; 182 | tile.updatePosition(cell); 183 | } 184 | 185 | private void saveUndoState() { 186 | mGameGrid.saveTiles(); 187 | mCanUndo = true; 188 | mLastScore = mBufferScore; 189 | mLastGameState = mBufferGameState; 190 | } 191 | 192 | private void prepareUndoState() { 193 | mGameGrid.prepareSaveTiles(); 194 | mBufferScore = mScore; 195 | mBufferGameState = mGameState; 196 | } 197 | 198 | public void revertUndoState() { 199 | if (mCanUndo) { 200 | mCanUndo = false; 201 | mView.cancelAnimations(); 202 | mGameGrid.revertTiles(); 203 | updateScore(mLastScore); 204 | updateGameState(mLastGameState); 205 | mView.setGameState(mGameState); 206 | mView.setRefreshLastTime(true); 207 | mView.invalidate(); 208 | } 209 | } 210 | 211 | public void updateUI() { 212 | updateScore(mScore); 213 | mView.setGameState(mGameState); 214 | mView.setRefreshLastTime(true); 215 | mView.invalidate(); 216 | } 217 | 218 | public static final int DIRECTION_UP = 0; 219 | public static final int DIRECTION_RIGHT = 1; 220 | public static final int DIRECTION_DOWN = 2; 221 | public static final int DIRECTION_LEFT = 3; 222 | 223 | public void move(int direction) { 224 | mView.cancelAnimations(); 225 | if (!isGameOnGoing()) { 226 | return; 227 | } 228 | prepareUndoState(); 229 | Position vector = Position.getVector(direction); 230 | List traversalsX = buildTraversalsX(vector); 231 | List traversalsY = buildTraversalsY(vector); 232 | boolean moved = false; 233 | 234 | prepareTiles(); 235 | 236 | for (int xx : traversalsX) { 237 | for (int yy : traversalsY) { 238 | Position cell = new Position(xx, yy); 239 | Tile tile = mGameGrid.getTile(cell); 240 | 241 | if (tile != null) { 242 | Position[] positions = findFarthestPosition(cell, vector); 243 | Tile next = mGameGrid.getTile(positions[1]); 244 | 245 | if (next != null && next.getValue() == tile.getValue() && next.getMergedFrom() == null) { 246 | Tile merged = new Tile(positions[1], tile.getValue() * 2); 247 | Tile[] temp = {tile, next}; 248 | merged.setMergedFrom(temp); 249 | 250 | mGameGrid.insertTile(merged); 251 | mGameGrid.removeTile(tile); 252 | 253 | // Converge the two tiles' positions 254 | tile.updatePosition(positions[1]); 255 | 256 | int[] extras = {xx, yy}; 257 | //Direction: 0 = MOVING MERGED 258 | mView.moveTile(merged.getX(), merged.getY(), extras); 259 | mView.mergeTile(merged.getX(), merged.getY()); 260 | 261 | updateScore(mScore + merged.getValue()); 262 | 263 | // The mighty 2048 tile 264 | if (merged.getValue() >= winValue() && !isGameWon()) { 265 | if (mGameState == State.ENDLESS) { 266 | updateGameState(State.ENLESS_WON); 267 | } else if (mGameState == State.NORMAL) { 268 | updateGameState(State.WON); 269 | } else { 270 | throw new RuntimeException("Can't move into win state"); 271 | } 272 | mView.setGameState(mGameState); 273 | endGame(); 274 | } 275 | } else { 276 | moveTile(tile, positions[0]); 277 | int[] extras = {xx, yy, 0}; 278 | //Direction: 1 = MOVING NO MERGE 279 | mView.moveTile(positions[0].getX(), positions[0].getY(), extras); 280 | } 281 | 282 | if (!Position.equal(cell, tile)) { 283 | moved = true; 284 | } 285 | } 286 | } 287 | } 288 | mView.updateGrid(mGameGrid); 289 | if (moved) { 290 | saveUndoState(); 291 | addRandomTile(); 292 | checkLose(); 293 | } 294 | mView.resyncTime(); 295 | mView.invalidate(); 296 | } 297 | 298 | private Position[] findFarthestPosition(Position cell, Position vector) { 299 | Position previous; 300 | Position nextCell = new Position(cell.getX(), cell.getY()); 301 | do { 302 | previous = nextCell; 303 | nextCell = new Position(previous.getX() + vector.getX(), 304 | previous.getY() + vector.getY()); 305 | } while (mGameGrid.isCellWithinBounds(nextCell) && mGameGrid.isCellAvailable(nextCell)); 306 | Position[] answer = {previous, nextCell}; 307 | return answer; 308 | } 309 | 310 | public void updateGameState(State state) { 311 | mGameState = state; 312 | if (mGameStateListener != null) 313 | mGameStateListener.onGameStateChanged(mGameState); 314 | } 315 | 316 | private void checkLose() { 317 | if (!isMovePossible() && !isGameWon()) { 318 | updateGameState(State.LOST); 319 | mView.setGameState(mGameState); 320 | endGame(); 321 | } 322 | } 323 | 324 | private void endGame() { 325 | mView.endGame(); 326 | updateScore(mScore); 327 | } 328 | 329 | private List buildTraversalsX(Position vector) { 330 | List traversals = new ArrayList(); 331 | for (int xx = 0; xx < mPositionsX; xx++) { 332 | traversals.add(xx); 333 | } 334 | if (vector.getX() == 1) { 335 | Collections.reverse(traversals); 336 | } 337 | return traversals; 338 | } 339 | 340 | private List buildTraversalsY(Position vector) { 341 | List traversals = new ArrayList(); 342 | for (int xx = 0; xx < mPositionsY; xx++) { 343 | traversals.add(xx); 344 | } 345 | if (vector.getY() == 1) { 346 | Collections.reverse(traversals); 347 | } 348 | return traversals; 349 | } 350 | 351 | private boolean isMovePossible() { 352 | return mGameGrid.isCellsAvailable() || tileMatchesAvailable(); 353 | } 354 | 355 | private boolean tileMatchesAvailable() { 356 | Tile tile; 357 | for (int xx = 0; xx < mPositionsX; xx++) { 358 | for (int yy = 0; yy < mPositionsY; yy++) { 359 | tile = mGameGrid.getTile(new Position(xx, yy)); 360 | if (tile != null) { 361 | for (int direction = 0; direction < 4; direction++) { 362 | Position vector = Position.getVector(direction); 363 | Position cell = new Position(xx + vector.getX(), yy + vector.getY()); 364 | Tile other = mGameGrid.getTile(cell); 365 | if (other != null && other.getValue() == tile.getValue()) { 366 | return true; 367 | } 368 | } 369 | } 370 | } 371 | } 372 | 373 | return false; 374 | } 375 | 376 | private int winValue() { 377 | if (isEndlessMode()) { 378 | return endingMaxValue; 379 | } else { 380 | return startingMaxValue; 381 | } 382 | } 383 | 384 | public void setEndlessMode() { 385 | updateGameState(State.ENDLESS); 386 | mView.setGameState(mGameState); 387 | mView.invalidate(); 388 | mView.setRefreshLastTime(true); 389 | } 390 | 391 | } 392 | --------------------------------------------------------------------------------