├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── xml │ │ │ │ ├── preferences_theme.xml │ │ │ │ ├── preferences_storage.xml │ │ │ │ ├── preferences_header.xml │ │ │ │ ├── device_filter.xml │ │ │ │ ├── preferences_info.xml │ │ │ │ └── setting.xml │ │ │ ├── values │ │ │ │ ├── codes.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ └── color.xml │ │ │ ├── drawable │ │ │ │ ├── btn.png │ │ │ │ ├── btn_.png │ │ │ │ ├── next.png │ │ │ │ ├── play.png │ │ │ │ ├── prev.png │ │ │ │ ├── chain.png │ │ │ │ ├── chain_.png │ │ │ │ ├── chain__.png │ │ │ │ ├── next_.png │ │ │ │ ├── pause.png │ │ │ │ ├── pause_.png │ │ │ │ ├── phantom.png │ │ │ │ ├── play_.png │ │ │ │ ├── playbg.png │ │ │ │ ├── prev_.png │ │ │ │ ├── chainled.png │ │ │ │ ├── midi_lp_s.png │ │ │ │ ├── midi_lp_x.png │ │ │ │ ├── phantom_.png │ │ │ │ ├── theme_add.png │ │ │ │ ├── theme_ic.png │ │ │ │ ├── custom_logo.png │ │ │ │ ├── midi_lp_mini.png │ │ │ │ ├── midi_lp_mk2.png │ │ │ │ ├── midi_lp_mk3.png │ │ │ │ ├── midi_lp_pro.png │ │ │ │ ├── midi_matrix.png │ │ │ │ ├── community_cafe.png │ │ │ │ ├── community_mail.png │ │ │ │ ├── community_web.png │ │ │ │ ├── community_discord.png │ │ │ │ ├── midi_midifighter.png │ │ │ │ ├── community_facebook.png │ │ │ │ ├── community_kakaotalk.png │ │ │ │ ├── midi_master_keyboard.png │ │ │ │ ├── community_facebook_group.png │ │ │ │ ├── border_divider.xml │ │ │ │ ├── border_apply_off.xml │ │ │ │ ├── border_gray.xml │ │ │ │ ├── border_settings.xml │ │ │ │ ├── xml_next.xml │ │ │ │ ├── xml_play.xml │ │ │ │ ├── xml_prev.xml │ │ │ │ ├── xml_pause.xml │ │ │ │ ├── border_apply.xml │ │ │ │ ├── border_apply_on.xml │ │ │ │ ├── ic_delete_24dp.xml │ │ │ │ ├── ic_music_note_24dp.xml │ │ │ │ ├── ic_bookmark_clip_back.xml │ │ │ │ ├── ic_info.xml │ │ │ │ ├── ic_pad_24dp.xml │ │ │ │ ├── ic_edit_24dp.xml │ │ │ │ ├── ic_exit.xml │ │ │ │ ├── ic_storage.xml │ │ │ │ ├── ic_payment.xml │ │ │ │ ├── ic_error.xml │ │ │ │ ├── ic_star_border_24dp.xml │ │ │ │ ├── ic_bookmark_on.xml │ │ │ │ ├── ic_bookmark_off.xml │ │ │ │ ├── ic_palette.xml │ │ │ │ ├── ic_bookmark_clip_front.xml │ │ │ │ ├── ic_sort_desc.xml │ │ │ │ ├── ic_sort_asc.xml │ │ │ │ ├── ic_youtube_24dp.xml │ │ │ │ ├── ic_web_24dp.xml │ │ │ │ ├── ic_led_event_24dp.xml │ │ │ │ ├── ic_launcher_foreground.xml │ │ │ │ └── ic_chain_24dp.xml │ │ │ ├── font │ │ │ │ ├── quicksand_bold.ttf │ │ │ │ └── montserrat_regular.ttf │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── drawable-hdpi │ │ │ │ ├── baseline_launchpad_24dp.png │ │ │ │ ├── baseline_settings_white_24.png │ │ │ │ ├── baseline_folder_open_white_24.png │ │ │ │ └── baseline_shopping_basket_white_24.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── baseline_launchpad_24dp.png │ │ │ │ ├── baseline_settings_white_24.png │ │ │ │ ├── baseline_folder_open_white_24.png │ │ │ │ └── baseline_shopping_basket_white_24.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── baseline_launchpad_24dp.png │ │ │ │ ├── baseline_settings_white_24.png │ │ │ │ ├── baseline_folder_open_white_24.png │ │ │ │ └── baseline_shopping_basket_white_24.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── baseline_launchpad_24dp.png │ │ │ │ ├── baseline_settings_white_24.png │ │ │ │ ├── baseline_folder_open_white_24.png │ │ │ │ └── baseline_shopping_basket_white_24.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── baseline_launchpad_24dp.png │ │ │ │ ├── baseline_settings_white_24.png │ │ │ │ ├── baseline_folder_open_white_24.png │ │ │ │ └── baseline_shopping_basket_white_24.png │ │ │ ├── anim │ │ │ │ ├── activity_in.xml │ │ │ │ ├── activity_out.xml │ │ │ │ ├── pack_in.xml │ │ │ │ ├── panel_in.xml │ │ │ │ ├── panel_out.xml │ │ │ │ └── pack_new_in.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ └── layout │ │ │ │ ├── activity_settings.xml │ │ │ │ ├── activity_setting_legacy.xml │ │ │ │ ├── item_midi_device.xml │ │ │ │ ├── include_property_verticle.xml │ │ │ │ ├── include_property_horizontal.xml │ │ │ │ ├── activity_theme.xml │ │ │ │ ├── item_theme.xml │ │ │ │ ├── item_setting.xml │ │ │ │ ├── activity_importpack.xml │ │ │ │ ├── activity_splash.xml │ │ │ │ ├── fragment_main_list.xml │ │ │ │ ├── include_property_block.xml │ │ │ │ └── activity_store.xml │ │ ├── ic_launcher-playstore.png │ │ └── java │ │ │ └── com │ │ │ └── kimjisub │ │ │ └── launchpad │ │ │ ├── manager │ │ │ ├── Constant.kt │ │ │ ├── Functions.kt │ │ │ ├── ColorManager.kt │ │ │ ├── NotificationManager.kt │ │ │ └── ChannelManager.kt │ │ │ ├── tool │ │ │ ├── BindUtil.kt │ │ │ ├── splitties │ │ │ │ ├── Browse.kt │ │ │ │ └── LiveData.kt │ │ │ ├── serializer │ │ │ │ └── UriSerializer.kt │ │ │ ├── UIManager.kt │ │ │ ├── ArrayListExtra.kt │ │ │ ├── LiveDataEvent.kt │ │ │ ├── AutorunTimer.kt │ │ │ └── Log.kt │ │ │ ├── unipack │ │ │ ├── struct │ │ │ │ ├── Sound.kt │ │ │ │ ├── LedAnimation.kt │ │ │ │ └── AutoPlay.kt │ │ │ └── runner │ │ │ │ └── ChainObserver.kt │ │ │ ├── midi │ │ │ ├── driver │ │ │ │ ├── Noting.kt │ │ │ │ ├── MasterKeyboard.kt │ │ │ │ ├── MidiFighter.kt │ │ │ │ ├── LaunchpadMK2.kt │ │ │ │ ├── LaunchpadS.kt │ │ │ │ ├── LaunchpadX.kt │ │ │ │ └── DriverRef.kt │ │ │ └── controller │ │ │ │ └── MidiController.kt │ │ │ ├── activity │ │ │ ├── ImportPackByFileActivity.kt │ │ │ ├── StoreActivity.kt │ │ │ ├── SettingsActivity.kt │ │ │ ├── SplashActivity.kt │ │ │ └── ThemeActivity.kt │ │ │ ├── fragment │ │ │ ├── settings │ │ │ │ ├── ThemeFragment.kt │ │ │ │ ├── HeaderFragment.kt │ │ │ │ └── StorageFragment.kt │ │ │ ├── BaseFragment.kt │ │ │ ├── MainTotalPanelFragment.kt │ │ │ └── MainPackPanelFragment.kt │ │ │ ├── api │ │ │ ├── unipad │ │ │ │ ├── vo │ │ │ │ │ └── UnishareVO.kt │ │ │ │ └── UniPadApi.kt │ │ │ ├── file │ │ │ │ └── FileApi.kt │ │ │ └── BaseApiService.kt │ │ │ ├── network │ │ │ ├── fb │ │ │ │ ├── StoreVO.kt │ │ │ │ └── FsStore.kt │ │ │ └── Networks.kt │ │ │ ├── db │ │ │ ├── util │ │ │ │ ├── DateConverter.kt │ │ │ │ └── LiveDataUtil.kt │ │ │ ├── AppDatabase.kt │ │ │ ├── repository │ │ │ │ └── UnipackRepository.kt │ │ │ ├── dao │ │ │ │ └── UnipackDao.kt │ │ │ └── ent │ │ │ │ └── Unipack.kt │ │ │ ├── binding │ │ │ ├── ImageView.kt │ │ │ ├── IconSwitch.kt │ │ │ └── SpinnerBindingAdapter.kt │ │ │ ├── adapter │ │ │ ├── DialogListAdapter.kt │ │ │ └── ThemeAdapter.kt │ │ │ ├── AppOpenManager.kt │ │ │ └── BaseApplication.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── kimjisub │ │ │ └── launchpad │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── kimjisub │ │ └── launchpad │ │ └── ExampleInstrumentedTest.kt ├── proguard-okhttp3.pro ├── proguard-firebase.pro ├── proguard-common.pro ├── proguard-rules.pro └── proguard-retrofit2.pro ├── design ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── border_icon_off.xml │ │ │ │ ├── border_no_round.xml │ │ │ │ ├── border_icon_on.xml │ │ │ │ ├── border_all_round.xml │ │ │ │ ├── border_all_round_gray3.xml │ │ │ │ ├── border_all_round_gray.xml │ │ │ │ ├── border_right_round.xml │ │ │ │ ├── border_icon.xml │ │ │ │ ├── border_panel_pack.xml │ │ │ │ ├── border_panel_total.xml │ │ │ │ ├── ic_delete_24dp.xml │ │ │ │ ├── ic_music_note_24dp.xml │ │ │ │ ├── ic_bookmark_clip_back.xml │ │ │ │ ├── ic_pad_24dp.xml │ │ │ │ ├── ic_edit_24dp.xml │ │ │ │ ├── ic_star_border_24dp.xml │ │ │ │ ├── ic_bookmark_on.xml │ │ │ │ ├── ic_bookmark_off.xml │ │ │ │ ├── ic_bookmark_clip_front.xml │ │ │ │ ├── ic_sort_desc.xml │ │ │ │ ├── ic_sort_asc.xml │ │ │ │ ├── ic_youtube_24dp.xml │ │ │ │ ├── ic_web_24dp.xml │ │ │ │ ├── ic_led_event_24dp.xml │ │ │ │ ├── ic_chain_24dp.xml │ │ │ │ └── playbtn.xml │ │ │ ├── values │ │ │ │ ├── styles.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── strings.xml │ │ │ │ └── color.xml │ │ │ ├── layout │ │ │ │ ├── view_chain.xml │ │ │ │ ├── view_pad.xml │ │ │ │ └── view_midi_item.xml │ │ │ └── values-ko │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── kimjisub │ │ │ └── design │ │ │ ├── binding │ │ │ ├── SpinnerBindingAdapter.kt │ │ │ └── ImageViewBindingAdapter.kt │ │ │ ├── manage │ │ │ ├── UIManager.kt │ │ │ └── SyncCheckBox.kt │ │ │ ├── panel │ │ │ └── StoreTotalPanel.kt │ │ │ ├── view │ │ │ ├── MidiItemView.kt │ │ │ ├── ChainView.kt │ │ │ └── PadView.kt │ │ │ └── layout │ │ │ └── RadioButtonGroupTableLayout.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── kimjisub │ │ │ └── design │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── kimjisub │ │ └── design │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── keystore.properties.example ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ └── codeStyleConfig.xml └── dictionaries │ ├── kimjisub.xml │ └── rlawl.xml ├── .gitignore ├── gradle.properties ├── README.md └── gradlew.bat /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /design/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':design' 2 | -------------------------------------------------------------------------------- /keystore.properties.example: -------------------------------------------------------------------------------- 1 | storeFile= 2 | storePassword= 3 | keyAlias= 4 | keyPassword= -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/codes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/btn.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/btn_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/next.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/prev.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/chain.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/chain_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/chain_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/chain__.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/chain__.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/next_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/next_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/pause_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/pause_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/phantom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/phantom.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/play_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/play_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/playbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/playbg.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/prev_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/prev_.png -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/chainled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/chainled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_s.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_x.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/phantom_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/phantom_.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/theme_add.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_ic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/theme_ic.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/custom_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/custom_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_mini.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_mk2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_mk2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_mk3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_mk3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_lp_pro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_lp_pro.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_matrix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_matrix.png -------------------------------------------------------------------------------- /app/src/main/res/font/quicksand_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/font/quicksand_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_cafe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_cafe.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_mail.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_web.png -------------------------------------------------------------------------------- /app/src/main/res/font/montserrat_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/font/montserrat_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_discord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_discord.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_midifighter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_midifighter.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_facebook.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_kakaotalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_kakaotalk.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/midi_master_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/midi_master_keyboard.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/community_facebook_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable/community_facebook_group.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_launchpad_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-hdpi/baseline_launchpad_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_launchpad_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-mdpi/baseline_launchpad_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_launchpad_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xhdpi/baseline_launchpad_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_settings_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-hdpi/baseline_settings_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_settings_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-mdpi/baseline_settings_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_settings_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xhdpi/baseline_settings_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_launchpad_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxhdpi/baseline_launchpad_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_launchpad_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxxhdpi/baseline_launchpad_24dp.png -------------------------------------------------------------------------------- /app/proguard-okhttp3.pro: -------------------------------------------------------------------------------- 1 | # Begin: Proguard rules for okhttp3 2 | 3 | -dontwarn okhttp3.** 4 | -dontwarn okio.** 5 | 6 | -dontnote okhttp3.** 7 | 8 | # End: Proguard rules for okhttp3 -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_folder_open_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-hdpi/baseline_folder_open_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_folder_open_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-mdpi/baseline_folder_open_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_settings_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxhdpi/baseline_settings_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_settings_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxxhdpi/baseline_settings_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_folder_open_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xhdpi/baseline_folder_open_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_folder_open_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxhdpi/baseline_folder_open_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_folder_open_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxxhdpi/baseline_folder_open_white_24.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/manager/Constant.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.manager 2 | 3 | object Constant { 4 | const val AUTOPLAY_AUTOMAPPING_DELAY_PRESET = -15 5 | 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/baseline_shopping_basket_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-hdpi/baseline_shopping_basket_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/baseline_shopping_basket_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-mdpi/baseline_shopping_basket_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/baseline_shopping_basket_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xhdpi/baseline_shopping_basket_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/baseline_shopping_basket_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxhdpi/baseline_shopping_basket_white_24.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/baseline_shopping_basket_white_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kimjisub/unipad-android/HEAD/app/src/main/res/drawable-xxxhdpi/baseline_shopping_basket_white_24.png -------------------------------------------------------------------------------- /.idea/dictionaries/kimjisub.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | splitties 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/BindUtil.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool 2 | 3 | object BindUtil { 4 | 5 | fun valueOrDefault(value: T?, defaultValue: T): T { 6 | return value ?: defaultValue 7 | } 8 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_icon_off.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_no_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /design/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /design/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_icon_on.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16sp 5 | 12sp 6 | 10sp 7 | 8 | 40dp 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_apply_off.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_all_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_all_round_gray3.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/xml_next.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/xml_play.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/xml_prev.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_all_round_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/unipack/struct/Sound.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.unipack.struct 2 | 3 | import java.io.File 4 | 5 | data class Sound( 6 | val file: File, 7 | val loop: Int, 8 | val wormhole: Int = -1, 9 | var num: Int = 0, 10 | var id: Int = -1, 11 | ) -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/anim/activity_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/xml_pause.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 15 17:40:43 KST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_apply.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences_storage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /design/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14sp 5 | 20sp 6 | 17sp 7 | 10sp 8 | 9 | 40dp 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_right_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_icon.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/proguard-firebase.pro: -------------------------------------------------------------------------------- 1 | # Begin: Proguard rules for Firebase 2 | 3 | # Authentication 4 | -keepattributes *Annotation* 5 | 6 | # Realtime database 7 | -keepattributes Signature 8 | 9 | -keepattributes Signature 10 | -keepclassmembers class com.kimjisub.launchpad.network.fb.* { 11 | *; 12 | } 13 | 14 | # End: Proguard rules for Firebase -------------------------------------------------------------------------------- /app/src/main/res/drawable/border_apply_on.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_panel_pack.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/border_panel_total.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_delete_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/dictionaries/rlawl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | admob 5 | autorun 6 | btns 7 | cooltime 8 | gson 9 | kimjisub 10 | lateinit 11 | unipack 12 | unipacks 13 | unipad 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_music_note_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/Noting.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | class Noting : DriverRef() { 4 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) {} 5 | override fun sendPadLed(x: Int, y: Int, velocity: Int) {} 6 | override fun sendChainLed(c: Int, velocity: Int) {} 7 | override fun sendFunctionkeyLed(f: Int, velocity: Int) {} 8 | override fun sendClearLed() {} 9 | } -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_music_note_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/anim/pack_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/splitties/Browse.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool.splitties 2 | 3 | import android.content.Intent 4 | import android.content.Intent.FLAG_ACTIVITY_NEW_TASK 5 | import android.net.Uri 6 | 7 | fun android.content.Context.browse(link: String) { 8 | val myIntent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) 9 | myIntent.addFlags(FLAG_ACTIVITY_NEW_TASK) 10 | startActivity(myIntent) 11 | } 12 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/binding/SpinnerBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.binding 2 | 3 | import androidx.appcompat.widget.AppCompatSpinner 4 | import androidx.databinding.BindingAdapter 5 | 6 | object SpinnerBindingAdapter { 7 | 8 | @JvmStatic 9 | @BindingAdapter("android:selectedItemPosition") 10 | fun setSelectedItemPosition(spinner: AppCompatSpinner, position: Int) { 11 | spinner.setSelection(position) 12 | } 13 | } -------------------------------------------------------------------------------- /design/src/test/java/com/kimjisub/design/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * @see [Testing documentation](http://d.android.com/tools/testing) 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bookmark_clip_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/test/java/com/kimjisub/launchpad/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * @see [Testing documentation](http://d.android.com/tools/testing) 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_bookmark_clip_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/activity/ImportPackByFileActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.activity 2 | 3 | import android.os.Bundle 4 | 5 | class ImportPackByFileActivity : BaseActivity() { 6 | override fun onCreate(savedInstanceState: Bundle?) { 7 | super.onCreate(savedInstanceState) 8 | // Todo 9 | // UniPackImporter( 10 | // context = this, 11 | // File(intent?.data?.path!!), 12 | // uniPackWorkspace 13 | // ) 14 | finish() 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/settings/ThemeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment.settings 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import com.kimjisub.launchpad.R 6 | 7 | class ThemeFragment : PreferenceFragmentCompat() { 8 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 9 | setPreferencesFromResource(R.xml.preferences_theme, rootKey) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/proguard-common.pro: -------------------------------------------------------------------------------- 1 | # Begin: Common Proguard rules 2 | 3 | # Don't note duplicate definition (Legacy Apche Http Client) 4 | -dontnote android.net.http.* 5 | -dontnote org.apache.http.** 6 | 7 | # Add when compile with JDK 1.7 8 | -keepattributes EnclosingMethod 9 | 10 | # GSON 11 | -keepattributes *Annotation* 12 | -keep class com.google.gson.** { *; } 13 | 14 | -keep class com.kimjisub.launchpad.activity.MainActivity { *; } 15 | 16 | 17 | # End: Common Proguard rules -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pad_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_pad_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/activity/StoreActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.activity 2 | 3 | import android.os.Bundle 4 | import com.kimjisub.launchpad.databinding.ActivityStoreBinding 5 | 6 | class StoreActivity : BaseActivity() { 7 | private lateinit var b: ActivityStoreBinding 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | b = ActivityStoreBinding.inflate(layoutInflater) 11 | setContentView(b.root) 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/api/unipad/vo/UnishareVO.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.api.unipad.vo 2 | 3 | data class UnishareVO( 4 | var _id: String? = null, 5 | var title: String? = null, 6 | var producer: String? = null, 7 | var content: String? = null, 8 | var website: String? = null, 9 | var youtube: String? = null, 10 | var fileSize: Long = 0, 11 | var isPublic: Boolean = false,//todo remove is 12 | var password: String? = null, 13 | var downloadCount: Int = 0, 14 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_exit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_edit_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/network/fb/StoreVO.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.network.fb 2 | 3 | import com.google.firebase.database.IgnoreExtraProperties 4 | 5 | @IgnoreExtraProperties 6 | data class StoreVO( 7 | var code: String? = null, 8 | var title: String? = null, 9 | var producerName: String? = null, 10 | @JvmField 11 | var isAutoPlay: Boolean = false, 12 | @JvmField 13 | var isLED: Boolean = false, 14 | var downloadCount: Int = 0, 15 | var URL: String? = null, 16 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/splitties/LiveData.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool.splitties 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MediatorLiveData 5 | import androidx.lifecycle.MutableLiveData 6 | 7 | fun LiveData.ignoreFirst(): MutableLiveData { 8 | val result = MediatorLiveData() 9 | var isFirst = true 10 | result.addSource(this) { 11 | if (isFirst) isFirst = false 12 | else result.value = it 13 | } 14 | return result 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment 2 | 3 | import androidx.fragment.app.Fragment 4 | import com.kimjisub.launchpad.manager.PreferenceManager 5 | import com.kimjisub.launchpad.manager.WorkspaceManager 6 | import org.koin.android.ext.android.inject 7 | 8 | open class BaseFragment : Fragment() { 9 | val p: PreferenceManager by inject() 10 | val ws: WorkspaceManager by inject() 11 | // val unipackRepo: UnipackRepository by inject() 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_storage.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_payment.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/util/DateConverter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db.util 2 | 3 | import androidx.room.TypeConverter 4 | import java.util.Date 5 | 6 | // https://developer.android.com/training/data-storage/room/referencing-data?hl=ko 7 | 8 | class DateConverter { 9 | @TypeConverter 10 | fun fromTimestamp(value: Long?): Date? { 11 | return value?.let { Date(it) } 12 | } 13 | 14 | @TypeConverter 15 | fun dateToTimestamp(date: Date?): Long? { 16 | return date?.time?.toLong() 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_error.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_star_border_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/controller/MidiController.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.controller 2 | 3 | abstract class MidiController { 4 | 5 | abstract fun onAttach() 6 | 7 | abstract fun onDetach() 8 | 9 | abstract fun onPadTouch(x: Int, y: Int, upDown: Boolean, velocity: Int) 10 | 11 | abstract fun onFunctionKeyTouch(f: Int, upDown: Boolean) 12 | 13 | abstract fun onChainTouch(c: Int, upDown: Boolean) 14 | 15 | abstract fun onUnknownEvent(cmd: Int, sig: Int, note: Int, velocity: Int) 16 | } 17 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_star_border_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/unipack/struct/LedAnimation.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.unipack.struct 2 | 3 | class LedAnimation( 4 | val ledEvents: ArrayList, 5 | val loop: Int, 6 | val num: Int, 7 | ) { 8 | interface LedEvent { 9 | class On( 10 | val x: Int, 11 | val y: Int, 12 | val color: Int = -1, 13 | val velocity: Int = 4, 14 | ) : LedEvent 15 | 16 | class Off( 17 | val x: Int, 18 | val y: Int, 19 | ) : LedEvent 20 | 21 | class Delay( 22 | val delay: Int, 23 | ) : LedEvent 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/panel_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/anim/panel_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/unipack/struct/AutoPlay.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.unipack.struct 2 | 3 | class AutoPlay( 4 | val elements: ArrayList, 5 | ) { 6 | interface Element { 7 | class On( 8 | val x: Int, 9 | val y: Int, 10 | val currChain: Int, 11 | val num: Int, 12 | ) : Element 13 | 14 | class Off( 15 | val x: Int, 16 | val y: Int, 17 | val currChain: Int, 18 | ) : Element 19 | 20 | class Chain( 21 | val c: Int, 22 | ) : Element 23 | 24 | class Delay( 25 | var delay: Int, 26 | val currChain: Int, 27 | ) : Element 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/pack_new_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bookmark_on.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_bookmark_on.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/serializer/UriSerializer.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool.serializer 2 | 3 | import android.net.Uri 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.Serializer 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | 9 | @Serializer(forClass = Uri::class) 10 | object UriSerializer : KSerializer { 11 | override fun deserialize(decoder: Decoder): Uri { 12 | return Uri.parse(decoder.decodeString()) 13 | } 14 | 15 | override fun serialize(encoder: Encoder, value: Uri) { 16 | encoder.encodeString(value.toString()) 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/binding/ImageView.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.binding 2 | 3 | import android.graphics.drawable.Drawable 4 | import android.widget.ImageView 5 | import androidx.databinding.BindingAdapter 6 | 7 | @BindingAdapter("imgRes") 8 | fun imgRes(imageView: ImageView, resId: Int?) { 9 | if (resId != null) 10 | imageView.setImageResource(resId) 11 | else 12 | imageView.setImageDrawable(null) 13 | 14 | } 15 | 16 | @BindingAdapter("imgRes") 17 | fun imgRes(imageView: ImageView, drawable: Drawable?) { 18 | if (drawable != null) 19 | imageView.setImageDrawable(drawable) 20 | else 21 | imageView.setImageDrawable(null) 22 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bookmark_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_bookmark_off.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/UIManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool 2 | 3 | import android.content.Context 4 | import android.util.DisplayMetrics 5 | 6 | object UIManager { 7 | fun pxToDp(context: Context, pixel: Int): Int { 8 | var dp = 0f 9 | try { 10 | val metrics: DisplayMetrics = context.resources.displayMetrics 11 | dp = pixel / (metrics.densityDpi / 160f) 12 | } catch (e: Exception) { 13 | } 14 | return dp.toInt() 15 | } 16 | 17 | fun dpToPx(context: Context, dp: Float): Int { 18 | val metrics: DisplayMetrics = context.resources.displayMetrics 19 | val px = dp * (metrics.densityDpi / 160f) 20 | return Math.round(px) 21 | } 22 | } -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/manage/UIManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.manage 2 | 3 | import android.content.Context 4 | import android.util.DisplayMetrics 5 | 6 | object UIManager { 7 | fun pxToDp(context: Context, pixel: Int): Int { 8 | var dp = 0f 9 | try { 10 | val metrics: DisplayMetrics = context.resources.displayMetrics 11 | dp = pixel / (metrics.densityDpi / 160f) 12 | } catch (e: Exception) { 13 | } 14 | return dp.toInt() 15 | } 16 | 17 | fun dpToPx(context: Context, dp: Float): Int { 18 | val metrics: DisplayMetrics = context.resources.displayMetrics 19 | val px = dp * (metrics.densityDpi / 160f) 20 | return Math.round(px) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/manager/Functions.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.manager 2 | 3 | import android.app.Activity 4 | import android.content.ClipData 5 | import android.content.ClipboardManager 6 | import android.content.Context 7 | import androidx.fragment.app.Fragment 8 | 9 | fun Context.putClipboard(text: String) { 10 | val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 11 | val clip: ClipData = ClipData.newPlainText("UniPad", text) 12 | clipboard.setPrimaryClip(clip) 13 | } 14 | 15 | fun Activity.putClipboard(text: String) { 16 | baseContext.putClipboard(text) 17 | } 18 | 19 | fun Fragment.putClipboard(text: String) { 20 | requireContext().putClipboard(text) 21 | } 22 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/binding/ImageViewBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.binding 2 | 3 | import android.graphics.drawable.Drawable 4 | import android.widget.ImageView 5 | import androidx.databinding.BindingAdapter 6 | 7 | object ImageViewBindingAdapter { 8 | @JvmStatic 9 | @BindingAdapter("imgRes") 10 | fun imgRes(imageView: ImageView, resId: Int?) { 11 | if (resId != null) 12 | imageView.setImageResource(resId) 13 | else 14 | imageView.setImageDrawable(null) 15 | 16 | } 17 | 18 | @JvmStatic 19 | @BindingAdapter("imgRes") 20 | fun imgRes(imageView: ImageView, drawable: Drawable?) { 21 | if (drawable != null) 22 | imageView.setImageDrawable(drawable) 23 | else 24 | imageView.setImageDrawable(null) 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_setting_legacy.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/kimjisub/launchpad/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * @see [Testing documentation](http://d.android.com/tools/testing) 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.kimjisub.launchpad", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /design/src/androidTest/java/com/kimjisub/design/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * @see [Testing documentation](http://d.android.com/tools/testing) 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.kimjisub.design.test", appContext.packageName) 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/ArrayListExtra.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.extra 2 | 3 | import androidx.databinding.Observable 4 | 5 | 6 | fun T.addOnPropertyChanged(callback: (T) -> Unit) = 7 | addOnPropertyChangedCallback( 8 | object : Observable.OnPropertyChangedCallback() { 9 | override fun onPropertyChanged(observable: Observable?, i: Int) = 10 | callback(observable as T) 11 | }) 12 | 13 | /** 14 | * Be sure the array is sort by the comparator you provided. 15 | * */ 16 | fun ArrayList.getVirtualIndexFormSorted(comparator: Comparator, target: T): Int { 17 | var index = 0 18 | for ((i, item: T) in this.withIndex()) { 19 | if (comparator.compare(target, item) < 0) { 20 | index = i 21 | break 22 | } 23 | } 24 | return index 25 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | #*.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log files 27 | *.log 28 | 29 | # Android Studio files 30 | # .idea/ 31 | .idea/*.* 32 | .idea/caches/ 33 | .idea/libraries/ 34 | .idea/modules/ 35 | *.iml 36 | 37 | # Android Studio Navigation editor temp files 38 | .navigation/ 39 | 40 | # Android Studio captures folder 41 | captures/ 42 | .DS_Store 43 | 44 | **/ndkHelperBin 45 | **/.cxx 46 | *.jks 47 | /app/release/app-release.aab 48 | keystore.properties 49 | 50 | app/release -------------------------------------------------------------------------------- /design/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/network/fb/FsStore.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.network.fb 2 | 3 | import com.google.firebase.database.IgnoreExtraProperties 4 | import java.util.Date 5 | 6 | @IgnoreExtraProperties 7 | class FsStore { 8 | var chainCount: Long = 0 9 | var code: String? = null 10 | var description: String? = null 11 | var difficulty: Long = 0 12 | var downloadCount: Long = 0 13 | 14 | @JvmField 15 | var isAutoPlay = false 16 | 17 | @JvmField 18 | var isLed = false 19 | 20 | @JvmField 21 | var isNew = false 22 | 23 | @JvmField 24 | var isProLight = false 25 | 26 | @JvmField 27 | var isWormhole = false 28 | var playTime: Long = 0 29 | var producerName: String? = null 30 | var rank: Long = 0 31 | var title: String? = null 32 | var uploadAt: Date? = null 33 | var url: String? = null 34 | var website: String? = null 35 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_palette.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/panel/StoreTotalPanel.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.panel 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.widget.RelativeLayout 7 | import com.kimjisub.design.R.styleable 8 | import com.kimjisub.design.databinding.PanelStoreTotalBinding 9 | 10 | class StoreTotalPanel 11 | @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0, 15 | ) : RelativeLayout(context, attrs, defStyleAttr) { 16 | val b: PanelStoreTotalBinding = 17 | PanelStoreTotalBinding.inflate(LayoutInflater.from(context), this, true) 18 | 19 | init { 20 | attrs?.let { 21 | val typedArray = 22 | context.obtainStyledAttributes(it, styleable.TotalPanel, defStyleAttr, 0) 23 | 24 | // code 25 | 26 | typedArray.recycle() 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/LiveDataEvent.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | 8 | open class Event(value: T) { 9 | 10 | var value = value 11 | private set 12 | 13 | private var isAlreadyHandled = false 14 | 15 | fun isActive(): Boolean = if (isAlreadyHandled) { 16 | false 17 | } else { 18 | isAlreadyHandled = true 19 | true 20 | } 21 | } 22 | 23 | fun LiveData>.observeEvent(owner: LifecycleOwner, observer: Observer) = 24 | observe(owner) { 25 | if (it.isActive()) { 26 | observer.onChanged(it.value) 27 | } 28 | } 29 | 30 | fun MutableLiveData>.emit() = postValue(Event(Unit)) 31 | 32 | fun MutableLiveData>.emit(value: T) = postValue(Event(value)) -------------------------------------------------------------------------------- /design/src/main/res/layout/view_chain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/util/LiveDataUtil.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db.util 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.Observer 6 | 7 | fun LiveData.observeOnce(observer: Observer) { 8 | observeForever(object : Observer { 9 | override fun onChanged(t: T) { 10 | observer.onChanged(t) 11 | removeObserver(this) 12 | } 13 | }) 14 | } 15 | 16 | fun LiveData.observeRealChange( 17 | owner: LifecycleOwner, 18 | realChangeObserver: Observer, 19 | clone: (T) -> T, 20 | ): Observer { 21 | val observer = object : Observer { 22 | var prev: T? = null 23 | 24 | override fun onChanged(it: T) { 25 | if (prev != it) { 26 | realChangeObserver.onChanged(it) 27 | prev = clone(it) 28 | } 29 | } 30 | } 31 | observe(owner, observer) 32 | 33 | return observer 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/binding/IconSwitch.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.binding 2 | 3 | import androidx.databinding.BindingAdapter 4 | import androidx.databinding.InverseBindingAdapter 5 | import androidx.databinding.InverseBindingListener 6 | import com.polyak.iconswitch.IconSwitch 7 | 8 | @BindingAdapter("checkedBoolean") 9 | fun setChecked(view: IconSwitch, checked: Boolean) { 10 | view.checked = if (checked) IconSwitch.Checked.RIGHT else IconSwitch.Checked.LEFT 11 | } 12 | 13 | @BindingAdapter("checkedBooleanAttrChanged") 14 | fun setCheckedListeners( 15 | view: IconSwitch, 16 | attrChange: InverseBindingListener, 17 | ) { 18 | view.setCheckedChangeListener { 19 | attrChange.onChange() 20 | } 21 | } 22 | 23 | @InverseBindingAdapter(attribute = "checkedBoolean", event = "checkedBooleanAttrChanged") 24 | fun getChecked(view: IconSwitch): Boolean { 25 | return view.checked == IconSwitch.Checked.RIGHT 26 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bookmark_clip_front.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 25 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_bookmark_clip_front.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort_desc.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_sort_desc.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort_asc.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_sort_asc.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | 14 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/unipack/runner/ChainObserver.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.unipack.runner 2 | 3 | class ChainObserver { 4 | var range: IntRange = Integer.MIN_VALUE..Integer.MAX_VALUE 5 | var value: Int = 0 6 | set(value) { 7 | val realValue = 8 | when { 9 | range.first > value -> range.first 10 | range.last < value -> range.last 11 | else -> value 12 | } 13 | 14 | val prev = field 15 | field = realValue 16 | refresh(field, prev) 17 | } 18 | 19 | private val observerList: ArrayList<(curr: Int, prev: Int) -> Unit> = ArrayList() 20 | 21 | fun refresh(curr: Int = value, prev: Int = value) { 22 | for (observer in observerList) 23 | observer.invoke(curr, prev) 24 | } 25 | 26 | fun addObserver(observer: (curr: Int, prev: Int) -> Unit) = observerList.add(observer) 27 | 28 | fun removeObserver(observer: (curr: Int, prev: Int) -> Unit) = observerList.remove(observer) 29 | 30 | fun clearObserver() = observerList.clear() 31 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_youtube_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_youtube_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 13 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/view/MidiItemView.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.view 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.LayoutInflater 6 | import android.widget.RelativeLayout 7 | import com.kimjisub.design.R.styleable 8 | import com.kimjisub.design.databinding.ViewMidiItemBinding 9 | 10 | class MidiItemView 11 | @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0, 15 | ) : RelativeLayout(context, attrs, defStyleAttr) { 16 | private val b: ViewMidiItemBinding = 17 | ViewMidiItemBinding.inflate(LayoutInflater.from(context), this, true) 18 | 19 | init { 20 | attrs?.let { 21 | val typedArray = 22 | context.obtainStyledAttributes(it, styleable.MidiItemView, defStyleAttr, 0) 23 | 24 | b.title = typedArray.getString(styleable.MidiItemView_title) 25 | b.subscription = typedArray.getString(styleable.MidiItemView_subscription) 26 | 27 | typedArray.recycle() 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_midi_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 17 | 18 | 24 | 25 | 26 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/adapter/DialogListAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.adapter 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.BaseAdapter 7 | import com.kimjisub.launchpad.R.layout 8 | import com.kimjisub.launchpad.databinding.ItemSettingBinding 9 | 10 | data class DialogListItem( 11 | val title: String, 12 | val subtitle: String, 13 | val iconResId: Int? = null, 14 | ) 15 | 16 | 17 | class DialogListAdapter( 18 | private val list: Array, 19 | ) : BaseAdapter() { 20 | 21 | override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { 22 | val view = LayoutInflater.from(parent.context).inflate(layout.item_setting, parent, false) 23 | val bind = ItemSettingBinding.bind(view) 24 | 25 | val item = list[position] 26 | 27 | bind.data = item 28 | 29 | return bind.root 30 | } 31 | 32 | override fun getCount() = list.size 33 | 34 | override fun getItem(position: Int) = list[position].title 35 | 36 | override fun getItemId(position: Int) = position.toLong() 37 | 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/AppDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import androidx.room.TypeConverters 8 | import com.kimjisub.launchpad.db.dao.UnipackDao 9 | import com.kimjisub.launchpad.db.ent.Unipack 10 | import com.kimjisub.launchpad.db.util.DateConverter 11 | 12 | @TypeConverters(DateConverter::class) 13 | @Database(entities = [Unipack::class], version = 2) 14 | abstract class AppDatabase : RoomDatabase() { 15 | abstract fun unipackDAO(): UnipackDao 16 | 17 | companion object { 18 | private var INSTANCE: AppDatabase? = null 19 | 20 | fun getInstance(context: Context): AppDatabase? { 21 | if (INSTANCE == null) { 22 | synchronized(AppDatabase::class) { 23 | INSTANCE = Room.databaseBuilder( 24 | context.applicationContext, 25 | AppDatabase::class.java, "UniPad.db" 26 | ).fallbackToDestructiveMigration().build() 27 | } 28 | } 29 | return INSTANCE 30 | } 31 | 32 | fun destroyInstance() { 33 | INSTANCE = null 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/device_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /design/src/main/res/layout/view_pad.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 11 | 12 | 16 | 17 | 21 | 22 | 30 | 31 | 37 | 38 | -------------------------------------------------------------------------------- /design/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/MasterKeyboard.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | class MasterKeyboard : DriverRef() { 4 | companion object; 5 | 6 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 7 | val x: Int 8 | val y: Int 9 | if (cmd == 9) { 10 | if (note in 36..67) { 11 | x = (67 - note) / 4 + 1 12 | y = 4 - (67 - note) % 4 13 | onPadTouch(x - 1, y - 1, velocity != 0, velocity) 14 | } else if (note in 68..99) { 15 | x = (99 - note) / 4 + 1 16 | y = 8 - (99 - note) % 4 17 | onPadTouch(x - 1, y - 1, velocity != 0, velocity) 18 | } 19 | } else if (velocity == 0) { 20 | if (note in 36..67) { 21 | x = (67 - note) / 4 + 1 22 | y = 4 - (67 - note) % 4 23 | onPadTouch(x - 1, y - 1, false, 0) 24 | } else if (note in 68..99) { 25 | x = (99 - note) / 4 + 1 26 | y = 8 - (99 - note) % 4 27 | onPadTouch(x - 1, y - 1, false, 0) 28 | } 29 | } 30 | } 31 | 32 | override fun sendPadLed(x: Int, y: Int, velocity: Int) {} 33 | override fun sendChainLed(c: Int, velocity: Int) {} 34 | override fun sendFunctionkeyLed(f: Int, velocity: Int) {} 35 | override fun sendClearLed() {} 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/repository/UnipackRepository.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db.repository 2 | 3 | import androidx.lifecycle.LiveData 4 | import com.kimjisub.launchpad.db.dao.UnipackDao 5 | import com.kimjisub.launchpad.db.ent.Unipack 6 | import java.util.Date 7 | 8 | class UnipackRepository( 9 | private val unipackDao: UnipackDao, 10 | ) { 11 | fun find(id: String): LiveData { 12 | return unipackDao.find(id) 13 | } 14 | 15 | suspend fun getOrCreate(id: String): LiveData { 16 | val unipack = unipackDao.find(id).value 17 | if (unipack == null) { 18 | unipackDao.insert(Unipack.create(id)) 19 | } 20 | return unipackDao.find(id) 21 | } 22 | 23 | suspend fun toggleBookmark(id: String) { 24 | unipackDao.toggleBookmark(id) 25 | } 26 | 27 | fun totalOpenCount(): LiveData { 28 | return unipackDao.totalOpenCount() 29 | } 30 | 31 | fun openCount(id: String): LiveData { 32 | return unipackDao.openCount(id) 33 | } 34 | 35 | fun lastOpenedAt(id: String): LiveData { 36 | return unipackDao.lastOpenedAt(id) 37 | } 38 | 39 | suspend fun recordOpen(id: String) { 40 | unipackDao.addOpenCount(id) 41 | unipackDao.setLastOpenedAt(id, Date()) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_web_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/xml/preferences_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 12 | 13 | 18 | 22 | 23 | 26 | 30 | 31 | 35 | 36 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_web_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/AutorunTimer.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool 2 | 3 | import java.util.Timer 4 | import java.util.TimerTask 5 | 6 | class AutorunTimer(val listener: OnListener, val timeout: Long) { 7 | private val everySecTimer = Timer() 8 | private val timeoutTimer = Timer() 9 | private var elapsedTime: Long = 0 10 | var running = false 11 | 12 | interface OnListener { 13 | fun onEverySec(leftTime: Long, elapsedTime: Long) 14 | fun onTimeOut() 15 | fun onCanceled() 16 | } 17 | 18 | fun start() { 19 | running = true 20 | everySecTimer.schedule(object : TimerTask() { 21 | override fun run() { 22 | listener.onEverySec(timeout - elapsedTime, elapsedTime) 23 | elapsedTime += 1000 24 | } 25 | 26 | }, 0, 1000) 27 | 28 | timeoutTimer.schedule(object : TimerTask() { 29 | override fun run() { 30 | timeout() 31 | } 32 | }, timeout) 33 | } 34 | 35 | fun cancel() { 36 | if (running) { 37 | running = false 38 | timeoutTimer.cancel() 39 | everySecTimer.cancel() 40 | listener.onCanceled() 41 | } 42 | } 43 | 44 | fun timeout() { 45 | if (running) { 46 | running = false 47 | timeoutTimer.cancel() 48 | everySecTimer.cancel() 49 | listener.onTimeOut() 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/binding/SpinnerBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.binding 2 | 3 | import android.view.View 4 | import android.widget.AdapterView 5 | import androidx.appcompat.widget.AppCompatSpinner 6 | import androidx.databinding.BindingAdapter 7 | import androidx.databinding.InverseBindingAdapter 8 | import androidx.databinding.InverseBindingListener 9 | 10 | @BindingAdapter("selectedItemPosition") 11 | fun setSelectedItemPosition(spinner: AppCompatSpinner, position: Int) { 12 | spinner.setSelection(position) 13 | } 14 | 15 | @BindingAdapter("selectedItemPositionAttrChanged") 16 | fun selectedItemPositionListeners( 17 | spinner: AppCompatSpinner, 18 | attrChange: InverseBindingListener, 19 | ) { 20 | spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { 21 | override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { 22 | attrChange.onChange() 23 | } 24 | 25 | override fun onNothingSelected(p0: AdapterView<*>?) { 26 | } 27 | } 28 | } 29 | 30 | @InverseBindingAdapter( 31 | attribute = "selectedItemPosition", 32 | event = "selectedItemPositionAttrChanged" 33 | ) 34 | fun getSelectedItemPosition(spinner: AppCompatSpinner): Int { 35 | return spinner.selectedItemPosition 36 | } -------------------------------------------------------------------------------- /app/src/main/res/xml/setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 12 | 15 | 16 | 17 | 18 | 19 | 22 | 25 | 28 | 31 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/AppOpenManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.os.Bundle 6 | import androidx.lifecycle.LifecycleObserver 7 | import androidx.lifecycle.ProcessLifecycleOwner 8 | 9 | 10 | class AppOpenManager( 11 | val application: BaseApplication, 12 | ) : LifecycleObserver, Application.ActivityLifecycleCallbacks { 13 | 14 | private var currentActivity: Activity? = null 15 | 16 | 17 | init { 18 | application.registerActivityLifecycleCallbacks(this) 19 | ProcessLifecycleOwner.get().lifecycle.addObserver(this) 20 | } 21 | 22 | 23 | // Activity 24 | 25 | override fun onActivityCreated(activity: Activity, p1: Bundle?) { 26 | } 27 | 28 | override fun onActivityStarted(activity: Activity) { 29 | currentActivity = activity 30 | } 31 | 32 | override fun onActivityResumed(activity: Activity) { 33 | currentActivity = activity 34 | } 35 | 36 | override fun onActivityPaused(activity: Activity) { 37 | } 38 | 39 | override fun onActivityStopped(activity: Activity) { 40 | } 41 | 42 | override fun onActivitySaveInstanceState(activity: Activity, p1: Bundle) { 43 | } 44 | 45 | override fun onActivityDestroyed(activity: Activity) { 46 | currentActivity = null 47 | } 48 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | #-keepattributes Signature 24 | #-keepclassmembers class com.kimjisub.launchpad.networks.fb.** { 25 | # *; 26 | #} 27 | #-keepclassmembers class com.kimjisub.launchpad.manage.network.** { 28 | # *; 29 | # } 30 | #-keep class android.support.v8.renderscript.** { *; } 31 | 32 | # Coroutine 33 | -keepclassmembernames class kotlinx.* { 34 | volatile ; 35 | } 36 | 37 | 38 | # todo fix this 39 | -keep class com.kimjisub.launchpad.viewmodel.**{ *; } -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 20 | 21 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /design/src/main/res/layout/view_midi_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 19 | 24 | 25 | 32 | 33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_property_verticle.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 28 | 29 | 30 | 37 | 38 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/include_property_horizontal.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 16 | 17 | 18 | 28 | 29 | 30 | 37 | 38 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | #android.enableAapt2=false 10 | org.gradle.jvmargs=-Xmx1536m 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | # AndroidX package structure to make it clearer which packages are bundled with the 16 | # Android operating system, and which are packaged with your app's APK 17 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 18 | android.useAndroidX=true 19 | # Automatically convert third-party libraries to use AndroidX 20 | android.enableJetifier=true 21 | # Kotlin code style for this project: "official" or "obsolete": 22 | kotlin.code.style=official 23 | org.gradle.caching=true 24 | android.defaults.buildfeatures.buildconfig=true 25 | android.nonTransitiveRClass=false 26 | android.nonFinalResIds=false 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 17 | 18 | 24 | 25 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /design/src/main/res/values-ko/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$tY-%1$tm-%1$td 4 | 5 | 6 | 유니팩 개수 7 | 유니팩 용량 8 | 플레이 횟수 9 | 테마 10 | 11 | 제목 12 | 제작자 13 | 플레이 횟수 14 | 최근 연 날짜 15 | 다운로드 날짜 16 | 17 | 18 | 19 | 패드 크기 20 | 체인 21 | 사운드 파일 22 | Led 이벤트 23 | 용량 24 | 플레이 횟수 25 | 다운로드한 날짜 26 | 최근 플레이 27 | - 28 | 29 | 30 | 유니팩 개수 31 | 다운로드 32 | 33 | 34 | 다운로드 수 35 | 36 | 37 | 확인 38 | 폴더를 읽을 수 없습니다. 39 | 파일을 읽을 수 없습니다. 40 | MB 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/settings/HeaderFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment.settings 2 | 3 | import android.os.Bundle 4 | import androidx.preference.Preference 5 | import androidx.preference.PreferenceFragmentCompat 6 | import com.kimjisub.launchpad.R 7 | 8 | class HeaderFragment(val callback: (Category) -> Unit) : PreferenceFragmentCompat() { 9 | private val infoPreference: Preference by lazy { findPreference("info")!! } 10 | private val storagePreference: Preference by lazy { findPreference("storage")!! } 11 | private val themePreference: Preference by lazy { findPreference("theme")!! } 12 | 13 | private val preferences by lazy { 14 | arrayOf(infoPreference, storagePreference, themePreference) 15 | } 16 | 17 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 18 | setPreferencesFromResource(R.xml.preferences_header, rootKey) 19 | 20 | select = 0 21 | preferences.forEachIndexed { index, preference -> 22 | preference.setOnPreferenceClickListener { 23 | select = index 24 | true 25 | } 26 | } 27 | } 28 | 29 | var select: Int = 0 30 | set(value) { 31 | field = value 32 | preferences.forEachIndexed { index, preference -> 33 | if (field == index) { 34 | callback(Category.valueOf(preference.key.uppercase())) 35 | } 36 | } 37 | } 38 | 39 | companion object { 40 | enum class Category(val key: String) { 41 | INFO("info"), STORAGE("storage"), THEME("theme") 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/api/file/FileApi.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.api.file 2 | 3 | import com.kimjisub.launchpad.api.BaseApiService 4 | import com.kimjisub.launchpad.tool.Log 5 | import okhttp3.OkHttpClient 6 | import okhttp3.ResponseBody 7 | import okhttp3.logging.HttpLoggingInterceptor 8 | import okhttp3.logging.HttpLoggingInterceptor.Level 9 | import okhttp3.logging.HttpLoggingInterceptor.Logger 10 | import retrofit2.Call 11 | import retrofit2.Retrofit 12 | import retrofit2.Retrofit.Builder 13 | import retrofit2.converter.gson.GsonConverterFactory 14 | import retrofit2.http.GET 15 | import retrofit2.http.Streaming 16 | import retrofit2.http.Url 17 | 18 | class FileApi : BaseApiService() { 19 | 20 | companion object { 21 | private const val URL = "https://api.unipad.io" 22 | 23 | val service: FileService by lazy { 24 | val httpLoggingInterceptor = HttpLoggingInterceptor { message -> Log.network(message) } 25 | httpLoggingInterceptor.level = Level.BODY 26 | val client: OkHttpClient = unsafeOkHttpClient 27 | .addInterceptor(httpLoggingInterceptor) 28 | .build() 29 | val retrofit: Retrofit = Builder() 30 | .baseUrl(URL) 31 | .addConverterFactory(GsonConverterFactory.create(gson)) 32 | .client(client) 33 | .build() 34 | retrofit.create(FileService::class.java) 35 | } 36 | 37 | interface FileService { 38 | 39 | // unishare 40 | 41 | @GET 42 | @Streaming 43 | fun download(@Url url: String): Call 44 | } 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/MidiFighter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | class MidiFighter : DriverRef() { 4 | companion object; 5 | 6 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 7 | val x: Int 8 | val y: Int 9 | if (cmd == 9) { 10 | if (note in 36..67) { 11 | x = (67 - note) / 4 + 1 12 | y = 4 - (67 - note) % 4 13 | onPadTouch(x - 1, y - 1, true, velocity) 14 | } else if (note in 68..99) { 15 | x = (99 - note) / 4 + 1 16 | y = 8 - (99 - note) % 4 17 | onPadTouch(x - 1, y - 1, true, velocity) 18 | } 19 | } else if (cmd == 8) { 20 | if (note in 36..67) { 21 | x = (67 - note) / 4 + 1 22 | y = 4 - (67 - note) % 4 23 | onPadTouch(x - 1, y - 1, false, velocity) 24 | } else if (note in 68..99) { 25 | x = (99 - note) / 4 + 1 26 | y = 8 - (99 - note) % 4 27 | onPadTouch(x - 1, y - 1, false, velocity) 28 | } 29 | } 30 | } 31 | 32 | override fun sendPadLed(x: Int, y: Int, velocity: Int) { 33 | var x = x 34 | var y = y 35 | x += 1 36 | y += 1 37 | if (y in 1..4) 38 | sendSignal(9, -110, -4 * x + y + 67, velocity) else if (y in 5..8) sendSignal( 39 | 9, 40 | -110, 41 | -4 * x + y + 95, 42 | velocity 43 | ) 44 | } 45 | 46 | override fun sendChainLed(c: Int, velocity: Int) {} 47 | override fun sendFunctionkeyLed(f: Int, velocity: Int) {} 48 | override fun sendClearLed() { 49 | for (i in 0..7) 50 | for (j in 0..7) 51 | sendPadLed(i, j, 0) 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/dao/UnipackDao.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db.dao 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.OnConflictStrategy 7 | import androidx.room.Query 8 | import com.kimjisub.launchpad.db.ent.Unipack 9 | import java.util.* 10 | 11 | @Dao 12 | interface UnipackDao { 13 | // Get 14 | 15 | @Query("SELECT * FROM Unipack WHERE id=:id") 16 | fun find(id: String): LiveData 17 | 18 | @Query("SELECT SUM(openCount) FROM Unipack") 19 | fun totalOpenCount(): LiveData 20 | 21 | @Query("SELECT MAX(lastOpenedAt) FROM Unipack") 22 | fun lastOpenedAt(): LiveData 23 | 24 | // Update 25 | 26 | @Insert(onConflict = OnConflictStrategy.IGNORE) 27 | fun insert(item: Unipack): Long 28 | 29 | @Query("UPDATE Unipack SET openCount = openCount + 1 WHERE id=:id") 30 | fun addOpenCount(id: String): Int 31 | 32 | @Query("UPDATE Unipack SET lastOpenedAt=:lastOpenedAt WHERE id=:id") 33 | fun setLastOpenedAt(id: String, lastOpenedAt: Date): Int 34 | 35 | @Query("UPDATE Unipack SET bookmark=NOT(bookmark) WHERE id=:id") 36 | fun toggleBookmark(id: String): Int 37 | 38 | @Query("SELECT openCount FROM Unipack WHERE id=:id") 39 | fun openCount(id: String): LiveData 40 | 41 | @Query("SELECT openCount FROM Unipack WHERE id=:id") 42 | fun openCountSync(id: String): Long 43 | 44 | @Query("SELECT lastOpenedAt FROM Unipack WHERE id=:id") 45 | fun lastOpenedAt(id: String): LiveData 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/db/ent/Unipack.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.db.ent 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import java.util.Date 6 | 7 | @Entity 8 | class Unipack( 9 | @PrimaryKey 10 | var id: String, 11 | var bookmark: Boolean, 12 | var openCount: Long, 13 | var lastOpenedAt: Date?, 14 | // var deleted: Boolean, 15 | // var downloadedAt: Date, 16 | // var downloadFrom: String, 17 | var createdAt: Date, 18 | ) { 19 | override fun toString(): String { 20 | return "Unipack(id='$id', bookmark=$bookmark, openCount=$openCount, lastOpenedAt=$lastOpenedAt, createdAt=$createdAt)" 21 | } 22 | 23 | override fun equals(other: Any?): Boolean { 24 | if (this === other) return true 25 | if (javaClass != other?.javaClass) return false 26 | 27 | other as Unipack 28 | 29 | if (id != other.id) return false 30 | if (bookmark != other.bookmark) return false 31 | if (openCount != other.openCount) return false 32 | if (lastOpenedAt != other.lastOpenedAt) return false 33 | if (createdAt != other.createdAt) return false 34 | 35 | return true 36 | } 37 | 38 | override fun hashCode(): Int { 39 | var result = id.hashCode() 40 | result = 31 * result + bookmark.hashCode() 41 | result = 31 * result + openCount.hashCode() 42 | result = 31 * result + lastOpenedAt.hashCode() 43 | result = 31 * result + createdAt.hashCode() 44 | return result 45 | } 46 | 47 | companion object { 48 | fun create(id: String) = Unipack(id, false, 0, null, Date()) 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/item_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 17 | 18 | 24 | 25 | 38 | 39 | 47 | 48 | -------------------------------------------------------------------------------- /design/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | 5 | android { 6 | compileSdk 34 7 | defaultConfig { 8 | minSdkVersion 21 9 | targetSdkVersion 34 10 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 11 | vectorDrawables.useSupportLibrary true 12 | } 13 | buildTypes { 14 | release { 15 | 16 | minifyEnabled true 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | debug { 20 | 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 23 | } 24 | } 25 | buildFeatures { 26 | viewBinding true 27 | dataBinding true 28 | } 29 | compileOptions { 30 | sourceCompatibility JavaVersion.VERSION_1_8 31 | targetCompatibility JavaVersion.VERSION_1_8 32 | } 33 | kotlinOptions { 34 | jvmTarget = '1.8' 35 | } 36 | namespace 'com.kimjisub.design' 37 | } 38 | 39 | dependencies { 40 | implementation fileTree(dir: 'libs', include: ['*.jar']) 41 | testImplementation 'junit:junit:4.13.2' 42 | 43 | implementation 'androidx.appcompat:appcompat:1.6.1' 44 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 45 | implementation 'androidx.recyclerview:recyclerview:1.3.1' 46 | androidTestImplementation 'androidx.test:runner:1.5.2' 47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 48 | 49 | kapt 'com.android.databinding:compiler:3.5.0' 50 | 51 | implementation 'com.polyak:icon-switch:1.0.0' 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_led_event_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 49 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_led_event_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 49 | -------------------------------------------------------------------------------- /design/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$td/%1$tm/%1$tY 4 | 5 | 6 | UniPacks Count 7 | UniPacks Size 8 | Play Count 9 | Theme 10 | 11 | Title 12 | Producer 13 | Play Count 14 | Last Opened Date 15 | Download Date 16 | 17 | 18 | 19 | Pad Size 20 | Chain 21 | Sound files 22 | Led Events 23 | File Size 24 | Play Count 25 | Downloaded Date 26 | Last Played 27 | - 28 | 29 | 30 | Store UniPacks 31 | Downloaded UniPacks 32 | 33 | 34 | Download count 35 | 36 | 37 | Accept 38 | Couldn\'t read the folder. 39 | Couldn\'t read the file. 40 | MB 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/manager/ColorManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.manager 2 | 3 | import android.content.Context 4 | import androidx.core.content.ContextCompat 5 | import com.kimjisub.launchpad.R.color 6 | 7 | class ColorManager(val context: Context) { 8 | fun get(id: Int) = ContextCompat.getColor(context, id) 9 | private fun getLazy(id: Int) = lazy { get(id) } 10 | 11 | val background1 by getLazy(color.background1) 12 | val gray1 by getLazy(color.gray1) 13 | val background2 by getLazy(color.background2) 14 | val gray2 by getLazy(color.gray2) 15 | val white by getLazy(color.white) 16 | 17 | 18 | val title by getLazy(color.title) 19 | val subtitle by getLazy(color.subtitle) 20 | 21 | 22 | val checkbox by getLazy(color.checkbox) 23 | val trace_log by getLazy(color.trace_log) 24 | val option_window by getLazy(color.option_window) 25 | val option_window_checkbox by getLazy(color.option_window_checkbox) 26 | val option_window_btn by getLazy(color.option_window_btn) 27 | val option_window_btn_text by getLazy(color.option_window_btn_text) 28 | 29 | 30 | val skyblue by getLazy(color.skyblue) 31 | val blue by getLazy(color.blue) 32 | val green by getLazy(color.green) 33 | val orange by getLazy(color.orange) 34 | val red by getLazy(color.red) 35 | val pink by getLazy(color.pink) 36 | 37 | 38 | val border_icon_on_background by getLazy(color.border_icon_on_background) 39 | val popup_background by getLazy(color.popup_background) 40 | 41 | 42 | val app_orange by getLazy(color.app_orange) 43 | val app_blue by getLazy(color.app_blue) 44 | val app_blue_dark by getLazy(color.app_blue_dark) 45 | } -------------------------------------------------------------------------------- /design/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #161e2b 5 | #414F66 6 | #a6b4c9 7 | #808A99 8 | #f5f5f5 9 | #FFFFFF 10 | 11 | 12 | #FFFFFF 13 | #f5f5f5 14 | #9E9E9E 15 | #838383 16 | #535353 17 | #333 18 | #000 19 | 20 | 21 | #767d89 22 | #767d89 23 | #a1a6ae 24 | 25 | 26 | #a6b4c9 27 | #161e2b 28 | #FFFFFF 29 | #414F66 30 | #d6d8d7 31 | #0f0f0f 32 | 33 | 34 | #00bcda 35 | #4283e6 36 | #66bb6a 37 | #ffa726 38 | #ff6b4e 39 | #ffa5a5 40 | 41 | 42 | #ccc 43 | #3000 44 | 45 | 46 | #ff8f00 47 | #00b8d4 48 | #009db5 49 | -------------------------------------------------------------------------------- /app/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #161e2b 5 | #414F66 6 | #a6b4c9 7 | #808A99 8 | #f5f5f5 9 | #FFFFFF 10 | 11 | 12 | #FFFFFF 13 | #f5f5f5 14 | #9E9E9E 15 | #838383 16 | #535353 17 | #333 18 | #000 19 | 20 | 21 | #767d89 22 | #a1a6ae 23 | 24 | 25 | #a6b4c9 26 | #161e2b 27 | #FFFFFF 28 | #414F66 29 | #d6d8d7 30 | #0f0f0f 31 | 32 | 33 | #00bcda 34 | #4283e6 35 | #66bb6a 36 | #ffa726 37 | #ff6b4e 38 | #ffa5a5 39 | 40 | 41 | #ccc 42 | #3000 43 | 44 | 45 | #ff8f00 46 | #00b8d4 47 | #009db5 48 | #F5F5F5 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/activity/SettingsActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import com.kimjisub.launchpad.databinding.ActivitySettingsBinding 6 | import com.kimjisub.launchpad.fragment.settings.HeaderFragment 7 | import com.kimjisub.launchpad.fragment.settings.HeaderFragment.Companion.Category 8 | import com.kimjisub.launchpad.fragment.settings.InfoFragment 9 | import com.kimjisub.launchpad.fragment.settings.StorageFragment 10 | import splitties.activities.start 11 | 12 | class SettingsActivity : AppCompatActivity() { 13 | 14 | lateinit var b: ActivitySettingsBinding 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | b = ActivitySettingsBinding.inflate(layoutInflater) 19 | setContentView(b.root) 20 | 21 | supportFragmentManager 22 | .beginTransaction() 23 | .replace(b.category.id, HeaderFragment { 24 | val fragment = when (it) { 25 | Category.INFO -> InfoFragment() 26 | Category.STORAGE -> StorageFragment() 27 | Category.THEME -> { 28 | start() 29 | // todo 이런식으로 fragment 띄우지 않는 경우에는 30 | // HeaderFragment 내부에서 select 값 바꾸지 않도록 31 | // ThemeFragment() 32 | null 33 | } 34 | 35 | else -> null 36 | } 37 | 38 | if (fragment != null) { 39 | supportFragmentManager.beginTransaction() 40 | .replace(b.settings.id, fragment) 41 | .commit() 42 | } 43 | }) 44 | .commit() 45 | } 46 | 47 | override fun onSupportNavigateUp(): Boolean { 48 | if (supportFragmentManager.popBackStackImmediate()) { 49 | return true 50 | } 51 | return super.onSupportNavigateUp() 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/tool/Log.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.tool 2 | 3 | import android.util.Log 4 | 5 | object Log { 6 | private const val DEBUG = true 7 | fun log(msg: String) = printLog("com.kimjisub._log", msg) 8 | fun fbmsg(msg: String) = printLog("com.kimjisub._fbmsg", msg) 9 | fun test(msg: String) = printLog("com.kimjisub._test", msg) 10 | fun play(msg: String) = printLog("com.kimjisub._play", msg) 11 | fun download(msg: String) = printLog("com.kimjisub._download", msg) 12 | fun network(msg: String) = printLog("com.kimjisub._network", msg) 13 | fun activity(msg: String) = printLog("com.kimjisub._activity", msg) 14 | fun firebase(msg: String) = printLog("com.kimjisub._firebase", msg) 15 | fun midi(msg: String) = printLog("com.kimjisub._midi", msg) 16 | fun thread(msg: String) = printLog("com.kimjisub._thread", msg) 17 | fun midiDetail(msg: String) = printLog("com.kimjisub._mididetail", msg) 18 | fun driver(msg: String) = printLog("com.kimjisub._driver", msg) 19 | fun driverCycle(msg: String) = printLog("com.kimjisub._cycle", msg) 20 | fun err(msg: String) = printLog("com.kimjisub._err", msg) 21 | 22 | private fun printLog(tag: String, msg: String?) { 23 | val msg: String = msg ?: "(null)" 24 | var space = "" 25 | for (i in 0 until 30 - tag.length) space += " " 26 | val trace1 = trace(Thread.currentThread().stackTrace, 5) 27 | val trace2 = trace(Thread.currentThread().stackTrace, 4) 28 | val detailMsg = String.format("%s%-50s %-40s %-40s", space, msg, trace1, trace2) 29 | Log.e(tag, if (DEBUG) detailMsg else msg) 30 | } 31 | 32 | fun trace(e: Array?, level: Int): String? { 33 | if (e != null && e.size >= level) 34 | return "${e[level].methodName}(${e[level].fileName}:${e[level].lineNumber})" 35 | return null 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/item_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 30 | 31 | 36 | 37 | 45 | 46 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/layout/RadioButtonGroupTableLayout.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.layout 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.RadioButton 8 | import android.widget.TableLayout 9 | import android.widget.TableRow 10 | 11 | 12 | class RadioButtonGroupTableLayout 13 | @JvmOverloads constructor( 14 | context: Context, 15 | attrs: AttributeSet? = null, 16 | ) : TableLayout(context, attrs), View.OnClickListener { 17 | companion object { 18 | private const val TAG = "RadioButtonGroupTableLayout" 19 | } 20 | 21 | private var activeRadioButton: RadioButton? = null 22 | 23 | var onCheckedChangeListener: ((radioButton: RadioButton) -> Unit)? = null 24 | 25 | override fun onClick(v: View) { 26 | val rb = v as RadioButton 27 | if (activeRadioButton != null) 28 | activeRadioButton!!.isChecked = false 29 | rb.isChecked = true 30 | activeRadioButton = rb 31 | 32 | onCheckedChangeListener?.invoke(rb) 33 | } 34 | 35 | override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) { 36 | super.addView(child, index, params) 37 | setChildrenOnClickListener(child as TableRow) 38 | } 39 | 40 | override fun addView(child: View, params: ViewGroup.LayoutParams?) { 41 | super.addView(child, params) 42 | setChildrenOnClickListener(child as TableRow) 43 | } 44 | 45 | private fun setChildrenOnClickListener(tr: TableRow) { 46 | val c: Int = tr.childCount 47 | for (i in 0 until c) { 48 | val v: View = tr.getChildAt(i) 49 | if (v is RadioButton) { 50 | v.setOnClickListener(this) 51 | } 52 | } 53 | } 54 | 55 | val checkedRadioButtonId: Int 56 | get() { 57 | return if (activeRadioButton != null) 58 | activeRadioButton!!.id 59 | else -1 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/api/unipad/UniPadApi.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.api.unipad 2 | 3 | import com.kimjisub.launchpad.api.BaseApiService 4 | import com.kimjisub.launchpad.api.unipad.vo.UnishareVO 5 | import com.kimjisub.launchpad.tool.Log 6 | import okhttp3.OkHttpClient 7 | import okhttp3.logging.HttpLoggingInterceptor 8 | import okhttp3.logging.HttpLoggingInterceptor.Level 9 | import okhttp3.logging.HttpLoggingInterceptor.Logger 10 | import retrofit2.Call 11 | import retrofit2.Retrofit 12 | import retrofit2.Retrofit.Builder 13 | import retrofit2.converter.gson.GsonConverterFactory 14 | import retrofit2.http.Body 15 | import retrofit2.http.GET 16 | import retrofit2.http.POST 17 | import retrofit2.http.Path 18 | 19 | class UniPadApi : BaseApiService() { 20 | 21 | companion object { 22 | private const val URL = "https://api.unipad.io" 23 | 24 | val service: UniPadApiService by lazy { 25 | val httpLoggingInterceptor = HttpLoggingInterceptor(object : Logger { 26 | override fun log(message: String) { 27 | Log.network(message) 28 | } 29 | }) 30 | httpLoggingInterceptor.level = Level.BODY 31 | val client: OkHttpClient = unsafeOkHttpClient 32 | .addInterceptor(httpLoggingInterceptor) 33 | .build() 34 | val retrofit: Retrofit = Builder() 35 | .baseUrl(URL) 36 | .addConverterFactory(GsonConverterFactory.create(gson)) 37 | .client(client) 38 | .build() 39 | retrofit.create(UniPadApiService::class.java) 40 | } 41 | 42 | interface UniPadApiService { 43 | 44 | // unishare 45 | 46 | @GET("/unishare") 47 | fun getUnishares(): Call?>? 48 | 49 | @POST("/unishare") 50 | fun createUnishare(@Body item: UnishareVO?): Call? 51 | 52 | @GET("/unishare/{code}") 53 | fun getUnishare(@Path("code") code: String?): Call? 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/LaunchpadMK2.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | class LaunchpadMK2 : DriverRef() { 4 | companion object { 5 | internal val circleCode = arrayOf( 6 | intArrayOf(11, -80, 104), 7 | intArrayOf(11, -80, 105), 8 | intArrayOf(11, -80, 106), 9 | intArrayOf(11, -80, 107), 10 | intArrayOf(11, -80, 108), 11 | intArrayOf(11, -80, 109), 12 | intArrayOf(11, -80, 110), 13 | intArrayOf(11, -80, 111), 14 | intArrayOf(9, -112, 89), 15 | intArrayOf(9, -112, 79), 16 | intArrayOf(9, -112, 69), 17 | intArrayOf(9, -112, 59), 18 | intArrayOf(9, -112, 49), 19 | intArrayOf(9, -112, 39), 20 | intArrayOf(9, -112, 29), 21 | intArrayOf(9, -112, 19) 22 | ) 23 | } 24 | 25 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 26 | if (cmd == 9) { 27 | val x = 9 - note / 10 28 | val y = note % 10 29 | if (y in 1..8) 30 | onPadTouch(x - 1, y - 1, velocity != 0, velocity) 31 | else if (y == 9) { 32 | onChainTouch(x - 1, velocity != 0) 33 | onFunctionKeyTouch(x - 1 + 8, velocity != 0) 34 | } 35 | } else if (cmd == 11) { 36 | if (note in 104..111) 37 | onFunctionKeyTouch(note - 104, velocity != 0) 38 | } 39 | } 40 | 41 | override fun sendPadLed(x: Int, y: Int, velocity: Int) { 42 | sendSignal(9, -112, 10 * (8 - x) + y + 1, velocity) 43 | } 44 | 45 | override fun sendChainLed(c: Int, velocity: Int) { 46 | if (c in 0..7) 47 | sendFunctionkeyLed(c + 8, velocity) 48 | } 49 | 50 | override fun sendFunctionkeyLed(f: Int, velocity: Int) { 51 | if (f in 0..15) 52 | sendSignal(circleCode[f][0], circleCode[f][1], circleCode[f][2], velocity) 53 | } 54 | 55 | override fun sendClearLed() { 56 | for (i in 0..7) 57 | for (j in 0..7) 58 | sendPadLed(i, j, 0) 59 | for (i in 0..15) sendFunctionkeyLed(i, 0) 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_importpack.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 15 | 24 | 25 | 30 | 31 | 35 | 36 | 44 | 45 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 30 | 31 | 38 | 39 | 42 | 43 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/activity/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.activity 2 | 3 | import android.Manifest.permission 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.os.Handler 7 | import android.os.Looper 8 | import com.gun0912.tedpermission.PermissionListener 9 | import com.gun0912.tedpermission.TedPermission 10 | import com.kimjisub.launchpad.BuildConfig 11 | import com.kimjisub.launchpad.R.string 12 | import com.kimjisub.launchpad.databinding.ActivitySplashBinding 13 | import splitties.activities.start 14 | 15 | class SplashActivity : BaseActivity() { 16 | private lateinit var b: ActivitySplashBinding 17 | 18 | 19 | // Timer 20 | internal var handler = Handler(Looper.getMainLooper()) 21 | internal var runnable = Runnable { 22 | finish() 23 | start() 24 | } 25 | 26 | override fun onCreate(savedInstanceState: Bundle?) { 27 | super.onCreate(savedInstanceState) 28 | b = ActivitySplashBinding.inflate(layoutInflater) 29 | setContentView(b.root) 30 | 31 | b.version.text = BuildConfig.VERSION_NAME 32 | 33 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { 34 | TedPermission.with(this) 35 | .setPermissionListener(object : PermissionListener { 36 | override fun onPermissionGranted() { 37 | handler.postDelayed(runnable, 2000) 38 | } 39 | 40 | override fun onPermissionDenied(deniedPermissions: List?) { 41 | handler.postDelayed(runnable, 2000) 42 | } 43 | }) 44 | .setRationaleMessage(string.permissionRequire) 45 | .setDeniedMessage(string.permissionDenied) 46 | .setPermissions( 47 | permission.READ_EXTERNAL_STORAGE, 48 | permission.WRITE_EXTERNAL_STORAGE 49 | ) 50 | .check() 51 | } else { 52 | handler.postDelayed(runnable, 2000) 53 | } 54 | } 55 | 56 | override fun onStop() { 57 | super.onStop() 58 | handler.removeCallbacks(runnable) 59 | finish() 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/settings/StorageFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment.settings 2 | 3 | import android.os.Bundle 4 | import androidx.preference.CheckBoxPreference 5 | import androidx.preference.PreferenceCategory 6 | import androidx.preference.PreferenceFragmentCompat 7 | import androidx.preference.children 8 | import com.kimjisub.launchpad.R 9 | import com.kimjisub.launchpad.manager.PreferenceManager 10 | import com.kimjisub.launchpad.manager.WorkspaceManager 11 | import org.koin.android.ext.android.inject 12 | 13 | class StorageFragment : PreferenceFragmentCompat() { 14 | 15 | val p: PreferenceManager by inject() 16 | val ws: WorkspaceManager by inject() 17 | 18 | private val storageListPreferenceCategory: PreferenceCategory by lazy { findPreference("storage_list")!! } 19 | 20 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 21 | setPreferencesFromResource(R.xml.preferences_storage, rootKey) 22 | 23 | 24 | initStorage() 25 | } 26 | 27 | private fun initStorage() { 28 | storageListPreferenceCategory.removeAll() 29 | ws.availableWorkspaces.forEach { 30 | val preference = CheckBoxPreference(context!!).apply { 31 | title = it.name 32 | summary = it.file.path 33 | isIconSpaceReserved = false 34 | setOnPreferenceChangeListener { _, newValue -> 35 | val list = p.storageActive.toMutableSet() 36 | if (newValue == true) { 37 | list.clear() 38 | list.add(it.file.path) 39 | } else if (list.size > 1) 40 | list.remove(it.file.path) 41 | p.storageActive = list 42 | ws.validateWorkspace() 43 | updateStorage() 44 | 45 | false 46 | } 47 | } 48 | storageListPreferenceCategory.addPreference(preference) 49 | } 50 | updateStorage() 51 | } 52 | 53 | private fun updateStorage() { 54 | storageListPreferenceCategory.children.forEach { 55 | (it as CheckBoxPreference).apply { 56 | isChecked = p.storageActive.contains(it.summary) 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 23 | 29 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/view/ChainView.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.view 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.widget.RelativeLayout 8 | import com.kimjisub.design.databinding.ViewChainBinding 9 | 10 | class ChainView 11 | @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0, 15 | ) : RelativeLayout(context, attrs, defStyleAttr) { 16 | private val b: ViewChainBinding = 17 | ViewChainBinding.inflate(LayoutInflater.from(context), this, true) 18 | 19 | 20 | override fun setOnClickListener(listener: OnClickListener?) { 21 | b.touchSpace.setOnClickListener(listener) 22 | } 23 | 24 | override fun setOnTouchListener(listener: OnTouchListener) { 25 | b.touchSpace.setOnTouchListener(listener) 26 | } 27 | //========================================================================================= Background 28 | 29 | 30 | fun setBackgroundImageDrawable(drawable: Drawable?): ChainView { 31 | b.background.setImageDrawable(drawable) 32 | return this 33 | } 34 | 35 | //========================================================================================= LED 36 | 37 | 38 | fun setLedBackground(drawable: Drawable?): ChainView { 39 | b.led.background = drawable 40 | return this 41 | } 42 | 43 | fun setLedBackgroundColor(color: Int): ChainView { 44 | b.led.setBackgroundColor(color) 45 | return this 46 | } 47 | 48 | fun setLedVisibility(visibility: Int): ChainView { 49 | b.led.visibility = visibility 50 | return this 51 | } 52 | 53 | //========================================================================================= Phantom 54 | 55 | 56 | fun setPhantomImageDrawable(drawable: Drawable?): ChainView { 57 | b.phantom.setImageDrawable(drawable) 58 | return this 59 | } 60 | 61 | fun setPhantomRotation(rotation: Float): ChainView { 62 | b.phantom.rotation = rotation 63 | return this 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/LaunchpadS.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | import com.kimjisub.launchpad.manager.LaunchpadColor 4 | 5 | class LaunchpadS : DriverRef() { 6 | companion object { 7 | internal val circleCode = arrayOf( 8 | intArrayOf(11, -80, 104), 9 | intArrayOf(11, -80, 105), 10 | intArrayOf(11, -80, 106), 11 | intArrayOf(11, -80, 107), 12 | intArrayOf(11, -80, 108), 13 | intArrayOf(11, -80, 109), 14 | intArrayOf(11, -80, 110), 15 | intArrayOf(11, -80, 111), 16 | intArrayOf(9, -112, 8), 17 | intArrayOf(9, -112, 24), 18 | intArrayOf(9, -112, 40), 19 | intArrayOf(9, -112, 56), 20 | intArrayOf(9, -112, 72), 21 | intArrayOf(9, -112, 88), 22 | intArrayOf(9, -112, 104), 23 | intArrayOf(9, -112, 120) 24 | ) 25 | } 26 | 27 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 28 | if (cmd == 9) { 29 | val x = note / 16 + 1 30 | val y = note % 16 + 1 31 | if (y in 1..8) 32 | onPadTouch(x - 1, y - 1, velocity != 0, velocity) 33 | else if (y == 9) { 34 | onChainTouch(x - 1, velocity != 0) 35 | onFunctionKeyTouch(x - 1 + 8, velocity != 0) 36 | } 37 | } else if (cmd == 11) { 38 | if (note in 104..111) 39 | onFunctionKeyTouch(note - 104, velocity != 0) 40 | } 41 | } 42 | 43 | override fun sendPadLed(x: Int, y: Int, velocity: Int) { 44 | sendSignal(9, -112, x * 16 + y, LaunchpadColor.SCode[velocity]) 45 | } 46 | 47 | override fun sendChainLed(c: Int, velocity: Int) { 48 | if (c in 0..7) 49 | sendFunctionkeyLed(c + 8, velocity) 50 | } 51 | 52 | override fun sendFunctionkeyLed(f: Int, velocity: Int) { 53 | if (f in 0..15) 54 | sendSignal( 55 | circleCode[f][0].toByte(), 56 | circleCode[f][1].toByte(), 57 | circleCode[f][2].toByte(), 58 | LaunchpadColor.SCode[velocity].toByte() 59 | ) 60 | } 61 | 62 | override fun sendClearLed() { 63 | for (i in 0..7) 64 | for (j in 0..7) 65 | sendPadLed(i, j, 0) 66 | for (i in 0..15) 67 | sendFunctionkeyLed(i, 0) 68 | } 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/manager/NotificationManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.manager 2 | 3 | import android.app.Notification 4 | import android.app.NotificationChannel 5 | import android.app.NotificationChannelGroup 6 | import android.app.NotificationManager 7 | import android.app.NotificationManager.IMPORTANCE_LOW 8 | import android.content.Context 9 | import android.graphics.Color 10 | import android.os.Build.VERSION 11 | import android.os.Build.VERSION_CODES 12 | import com.kimjisub.launchpad.R.string 13 | 14 | object NotificationManager { 15 | fun createChannel(context: Context) { 16 | if (VERSION.SDK_INT >= VERSION_CODES.O) { 17 | val manager = getManager(context) 18 | 19 | enumValues().forEach { 20 | val group = NotificationChannelGroup(it.name, context.getString(it.titleId)) 21 | manager.createNotificationChannelGroup(group) 22 | } 23 | 24 | enumValues().forEach { 25 | val channel = 26 | NotificationChannel(it.name, context.getString(it.titleId), IMPORTANCE_LOW) 27 | channel.apply { 28 | description = context.getString(it.titleId) + " disc" 29 | group = it.group.name 30 | lightColor = it.colorId 31 | lockscreenVisibility = Notification.VISIBILITY_PUBLIC 32 | } 33 | manager.createNotificationChannel(channel) 34 | } 35 | } 36 | } 37 | 38 | fun getManager(context: Context): NotificationManager { 39 | return context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 40 | } 41 | 42 | fun deleteChannel(context: Context, channel: Channel) { 43 | if (VERSION.SDK_INT >= VERSION_CODES.O) 44 | getManager(context).deleteNotificationChannel(channel.name) 45 | } 46 | 47 | enum class Group(val titleId: Int) { 48 | General(string.general), 49 | Notice(string.notice) 50 | } 51 | 52 | enum class Channel(val titleId: Int, val group: Group, val colorId: Int) { 53 | Download(string.download, Group.General, Color.GREEN), 54 | Notice(string.notice, Group.Notice, Color.BLUE), 55 | NewPack(string.newUniPack, Group.Notice, Color.BLUE) 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/MainTotalPanelFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.view.LayoutInflater 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import androidx.lifecycle.ViewModelProvider 9 | import com.kimjisub.launchpad.databinding.FragmentMainTotalPanelBinding 10 | import com.kimjisub.launchpad.tool.observeEvent 11 | import com.kimjisub.launchpad.viewmodel.MainTotalPanelViewModel 12 | 13 | class MainTotalPanelFragment : BaseFragment() { 14 | private var _b: FragmentMainTotalPanelBinding? = null 15 | private val b get() = _b!! 16 | private lateinit var vm: MainTotalPanelViewModel 17 | 18 | var sort: Pair? = null 19 | 20 | private var callbacks: Callbacks? = null 21 | 22 | interface Callbacks { 23 | fun onSortChangeListener(sort: Pair) 24 | } 25 | 26 | override fun onCreateView( 27 | inflater: LayoutInflater, container: ViewGroup?, 28 | savedInstanceState: Bundle?, 29 | ): View { 30 | vm = ViewModelProvider( 31 | this, 32 | MainTotalPanelViewModel.Factory( 33 | requireActivity().application, 34 | p, 35 | ws 36 | ) 37 | )[MainTotalPanelViewModel::class.java] 38 | _b = FragmentMainTotalPanelBinding.inflate(inflater, container, false) 39 | b.apply { 40 | lifecycleOwner = this@MainTotalPanelFragment 41 | vm = this@MainTotalPanelFragment.vm 42 | } 43 | 44 | vm.eventSort.observeEvent(viewLifecycleOwner) { 45 | sort = it 46 | callbacks?.onSortChangeListener(it) 47 | } 48 | 49 | return b.root 50 | } 51 | 52 | fun update() { 53 | vm.update() 54 | } 55 | 56 | // Lifecycle 57 | 58 | override fun onAttach(context: Context) { 59 | super.onAttach(context) 60 | callbacks = context as Callbacks? 61 | } 62 | 63 | override fun onDetach() { 64 | super.onDetach() 65 | callbacks = null 66 | } 67 | 68 | 69 | override fun onDestroyView() { 70 | super.onDestroyView() 71 | _b = null 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_chain_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/ic_chain_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/proguard-retrofit2.pro: -------------------------------------------------------------------------------- 1 | # Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and 2 | # EnclosingMethod is required to use InnerClasses. 3 | -keepattributes Signature, InnerClasses, EnclosingMethod 4 | 5 | # Retrofit does reflection on method and parameter annotations. 6 | -keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations 7 | 8 | # Keep annotation default values (e.g., retrofit2.http.Field.encoded). 9 | -keepattributes AnnotationDefault 10 | 11 | # Retain service method parameters when optimizing. 12 | -keepclassmembers,allowshrinking,allowobfuscation interface * { 13 | @retrofit2.http.* ; 14 | } 15 | 16 | # Ignore annotation used for build tooling. 17 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 18 | 19 | # Ignore JSR 305 annotations for embedding nullability information. 20 | -dontwarn javax.annotation.** 21 | 22 | # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. 23 | -dontwarn kotlin.Unit 24 | 25 | # Top-level functions that can only be used by Kotlin. 26 | -dontwarn retrofit2.KotlinExtensions 27 | -dontwarn retrofit2.KotlinExtensions$* 28 | 29 | # With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy 30 | # and replaces all potential values with null. Explicitly keeping the interfaces prevents this. 31 | -if interface * { @retrofit2.http.* ; } 32 | -keep,allowobfuscation interface <1> 33 | 34 | # Keep inherited services. 35 | -if interface * { @retrofit2.http.* ; } 36 | -keep,allowobfuscation interface * extends <1> 37 | 38 | # With R8 full mode generic signatures are stripped for classes that are not 39 | # kept. Suspend functions are wrapped in continuations where the type argument 40 | # is used. 41 | -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation 42 | 43 | # R8 full mode strips generic signatures from return types if not kept. 44 | -if interface * { @retrofit2.http.* public *** *(...); } 45 | -keep,allowoptimization,allowshrinking,allowobfuscation class <3> 46 | 47 | # With R8 full mode generic signatures are stripped for classes that are not kept. 48 | -keep,allowobfuscation,allowshrinking class retrofit2.Response -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/activity/ThemeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.activity 2 | 3 | import android.os.Bundle 4 | import com.azoft.carousellayoutmanager.CarouselLayoutManager 5 | import com.azoft.carousellayoutmanager.CarouselZoomPostLayoutListener 6 | import com.azoft.carousellayoutmanager.CenterScrollListener 7 | import com.kimjisub.launchpad.adapter.ThemeAdapter 8 | import com.kimjisub.launchpad.adapter.ThemeItem 9 | import com.kimjisub.launchpad.adapter.ThemeTool 10 | import com.kimjisub.launchpad.databinding.ActivityThemeBinding 11 | import com.kimjisub.launchpad.tool.Log 12 | import com.kimjisub.launchpad.tool.splitties.browse 13 | 14 | class ThemeActivity : BaseActivity() { 15 | private lateinit var b: ActivityThemeBinding 16 | val list: ArrayList by lazy { ThemeTool.getThemePackList(applicationContext) } 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | b = ActivityThemeBinding.inflate(layoutInflater) 21 | setContentView(b.root) 22 | } 23 | 24 | override fun onResume() { 25 | super.onResume() 26 | val manager = CarouselLayoutManager(CarouselLayoutManager.HORIZONTAL, false).apply { 27 | setPostLayoutListener(CarouselZoomPostLayoutListener()) 28 | } 29 | 30 | b.list.apply { 31 | layoutManager = manager 32 | adapter = ThemeAdapter(list) 33 | 34 | setHasFixedSize(true) 35 | addOnScrollListener(CenterScrollListener()) 36 | } 37 | 38 | manager.scrollToPosition(getSavedTheme()) 39 | 40 | b.apply.setOnClickListener { 41 | selectTheme(manager.centerItemPosition) 42 | finish() 43 | } 44 | } 45 | 46 | private fun selectTheme(i: Int) { 47 | if (list.size != i) 48 | p.selectedTheme = list[i].package_name 49 | else 50 | browse("https://play.google.com/store/search?q=com.kimjisub.launchpad.theme.") 51 | } 52 | 53 | private fun getSavedTheme(): Int { 54 | var ret = 0 55 | val selectedThemePackageName: String = p.selectedTheme 56 | var i = 0 57 | for (themeItem in list) { 58 | Log.log(selectedThemePackageName + ", " + themeItem.package_name) 59 | if (themeItem.package_name == selectedThemePackageName) { 60 | ret = i 61 | break 62 | } 63 | i++ 64 | } 65 | return ret 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/manager/ChannelManager.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.manager 2 | 3 | class ChannelManager(x: Int, y: Int) { 4 | private var btn: Array>> 5 | private var cir: Array> 6 | private var btnIgnoreList: BooleanArray 7 | private var cirIgnoreList: BooleanArray 8 | 9 | 10 | enum class Channel(val priority: Int) { 11 | UI(0), 12 | UI_UNIPAD(1), 13 | GUIDE(2), 14 | PRESSED(3), 15 | CHAIN(3), 16 | LED(4); 17 | } 18 | 19 | data class Item( 20 | var channel: Channel, 21 | var color: Int, 22 | var code: Int, 23 | ) { 24 | override fun toString(): String { 25 | return "Item(channel=$channel, color=$color, code=$code)" 26 | } 27 | } 28 | 29 | 30 | init { 31 | btn = Array(x) { Array(y) { arrayOfNulls(Channel.values().size) } } 32 | cir = Array(36) { arrayOfNulls(Channel.values().size) } 33 | btnIgnoreList = BooleanArray(Channel.values().size) 34 | cirIgnoreList = BooleanArray(Channel.values().size) 35 | } 36 | 37 | 38 | fun get(x: Int, y: Int): Item? { 39 | var ret: Item? = null 40 | if (x != -1) { 41 | for (i in Channel.values().indices) { 42 | if (btnIgnoreList[i]) 43 | continue 44 | if (btn[x][y][i] != null) { 45 | ret = btn[x][y][i] 46 | break 47 | } 48 | } 49 | } else { 50 | for (i in Channel.values().indices) { 51 | if (cirIgnoreList[i]) 52 | continue 53 | if (cir[y][i] != null) { 54 | ret = cir[y][i] 55 | break 56 | } 57 | } 58 | } 59 | return ret 60 | } 61 | 62 | fun add(x: Int, y: Int, channel: Channel, color: Int, code: Int) { 63 | var color = color 64 | if (color == -1) 65 | color = LaunchpadColor.ARGB[code].toInt() 66 | if (x != -1) 67 | btn[x][y][channel.priority] = Item(channel, color, code) 68 | else 69 | cir[y][channel.priority] = Item(channel, color, code) 70 | } 71 | 72 | fun remove(x: Int, y: Int, channel: Channel) { 73 | if (x != -1) 74 | btn[x][y][channel.priority] = null 75 | else 76 | cir[y][channel.priority] = null 77 | } 78 | 79 | fun setBtnIgnore(channel: Channel, ignore: Boolean) { 80 | btnIgnoreList[channel.priority] = ignore 81 | } 82 | 83 | fun setCirIgnore(channel: Channel, ignore: Boolean) { 84 | cirIgnoreList[channel.priority] = ignore 85 | } 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/network/Networks.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.network 2 | 3 | import com.google.firebase.database.ChildEventListener 4 | import com.google.firebase.database.DatabaseReference 5 | import com.google.firebase.database.FirebaseDatabase 6 | import com.google.firebase.database.ValueEventListener 7 | import java.io.BufferedReader 8 | import java.io.InputStreamReader 9 | import java.net.HttpURLConnection 10 | import java.net.URL 11 | 12 | object Networks { 13 | fun sendGet(str: String?): String { 14 | val html = StringBuilder() 15 | try { 16 | val url = URL(str) 17 | val conn = url.openConnection() as HttpURLConnection 18 | conn.connectTimeout = 10000 19 | conn.useCaches = false 20 | if (conn.responseCode == HttpURLConnection.HTTP_OK) { 21 | val br = BufferedReader( 22 | InputStreamReader(conn.inputStream) 23 | ) 24 | while (true) { 25 | val line = br.readLine() ?: break 26 | html.append(line) 27 | html.append('\n') 28 | } 29 | br.close() 30 | } 31 | conn.disconnect() 32 | } catch (e: Exception) { 33 | e.printStackTrace() 34 | } 35 | return html.toString() 36 | } 37 | 38 | class FirebaseManager(key: String) { 39 | private var database: FirebaseDatabase = FirebaseDatabase.getInstance() 40 | private var myRef: DatabaseReference 41 | private var childEventListener: ChildEventListener? = null 42 | private var valueEventListener: ValueEventListener? = null 43 | fun setEventListener(childEventListener: ChildEventListener): FirebaseManager { 44 | this.childEventListener = childEventListener 45 | return this 46 | } 47 | 48 | fun setEventListener(valueEventListener: ValueEventListener): FirebaseManager { 49 | this.valueEventListener = valueEventListener 50 | return this 51 | } 52 | 53 | fun attachEventListener(bool: Boolean): FirebaseManager { 54 | if (childEventListener != null) if (bool) myRef.addChildEventListener(childEventListener!!) else myRef.removeEventListener( 55 | childEventListener!! 56 | ) 57 | if (valueEventListener != null) if (bool) myRef.addValueEventListener(valueEventListener!!) else myRef.removeEventListener( 58 | valueEventListener!! 59 | ) 60 | return this 61 | } 62 | 63 | init { 64 | myRef = database.getReference(key) 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 13 | 14 | 23 | 24 | 25 | 26 | 37 | 38 | 44 | 45 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/api/BaseApiService.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.api 2 | 3 | 4 | import android.annotation.SuppressLint 5 | import com.google.gson.* 6 | import okhttp3.OkHttpClient 7 | import java.lang.reflect.Type 8 | import java.security.SecureRandom 9 | import java.security.cert.X509Certificate 10 | import java.text.DateFormat 11 | import java.text.ParseException 12 | import java.text.SimpleDateFormat 13 | import java.util.* 14 | import javax.net.ssl.* 15 | 16 | 17 | open class BaseApiService { 18 | 19 | companion object { 20 | 21 | @SuppressLint("SimpleDateFormat") 22 | private val df: DateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") 23 | 24 | val unsafeOkHttpClient: OkHttpClient.Builder 25 | get() { 26 | // Create a trust manager that does not validate certificate chains 27 | 28 | 29 | val trustAllCerts = arrayOf( 30 | object : X509TrustManager { 31 | override fun checkClientTrusted( 32 | chain: Array?, 33 | authType: String?, 34 | ) { 35 | } 36 | 37 | override fun checkServerTrusted( 38 | chain: Array?, 39 | authType: String?, 40 | ) { 41 | } 42 | 43 | override fun getAcceptedIssuers(): Array { 44 | return arrayOf() 45 | } 46 | } 47 | ) 48 | val sslContext = SSLContext.getInstance("SSL") 49 | sslContext.init(null, trustAllCerts, SecureRandom()) 50 | val sslSocketFactory = sslContext.socketFactory 51 | return OkHttpClient.Builder() 52 | .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager) 53 | .hostnameVerifier(HostnameVerifier { _: String?, _: SSLSession? -> true }) 54 | 55 | } 56 | 57 | val gson: Gson? 58 | get() { 59 | return GsonBuilder().registerTypeAdapter( 60 | Date::class.java, 61 | object : JsonDeserializer { 62 | @Throws(JsonParseException::class) 63 | override fun deserialize( 64 | json: JsonElement, 65 | typeOfT: Type?, 66 | context: JsonDeserializationContext?, 67 | ): Date? { 68 | return try { 69 | df.parse(json.asString) 70 | } catch (e: ParseException) { 71 | e.printStackTrace() 72 | null 73 | } 74 | } 75 | }).create() 76 | } 77 | 78 | } 79 | 80 | 81 | } -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/view/PadView.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.view 2 | 3 | import android.content.Context 4 | import android.graphics.drawable.Drawable 5 | import android.util.AttributeSet 6 | import android.view.LayoutInflater 7 | import android.widget.RelativeLayout 8 | import com.kimjisub.design.databinding.ViewPadBinding 9 | 10 | class PadView 11 | @JvmOverloads constructor( 12 | context: Context, 13 | attrs: AttributeSet? = null, 14 | defStyleAttr: Int = 0, 15 | ) : RelativeLayout(context, attrs, defStyleAttr) { 16 | private val b: ViewPadBinding = 17 | ViewPadBinding.inflate(LayoutInflater.from(context), this, true) 18 | 19 | override fun setOnClickListener(listener: OnClickListener?) { 20 | b.touchSpace.setOnClickListener(listener) 21 | } 22 | 23 | override fun setOnTouchListener(listener: OnTouchListener) { 24 | b.touchSpace.setOnTouchListener(listener) 25 | } 26 | //========================================================================================= Background 27 | 28 | 29 | fun setBackgroundImageDrawable(drawable: Drawable?): PadView { 30 | b.background.setImageDrawable(drawable) 31 | return this 32 | } 33 | 34 | //========================================================================================= LED 35 | 36 | 37 | fun setLedBackground(drawable: Drawable?): PadView { 38 | b.led.background = drawable 39 | return this 40 | } 41 | 42 | fun setLedBackgroundColor(color: Int): PadView { 43 | b.led.setBackgroundColor(color) 44 | return this 45 | } 46 | 47 | //========================================================================================= Phantom 48 | 49 | 50 | fun setPhantomImageDrawable(drawable: Drawable?): PadView { 51 | b.phantom.setImageDrawable(drawable) 52 | return this 53 | } 54 | 55 | fun setPhantomRotation(rotation: Float): PadView { 56 | b.phantom.rotation = rotation 57 | return this 58 | } 59 | 60 | //========================================================================================= TraceLog 61 | 62 | 63 | fun setTraceLogText(string: String?): PadView { 64 | b.traceLog.text = string 65 | return this 66 | } 67 | 68 | fun appendTraceLog(string: String?): PadView { 69 | b.traceLog.append(string) 70 | return this 71 | } 72 | 73 | fun setTraceLogTextColor(color: Int): PadView { 74 | b.traceLog.setTextColor(color) 75 | return this 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/include_property_block.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 17 | 18 | 21 | 22 | 23 | 28 | 29 | 33 | 34 | 46 | 47 | 56 | 57 | 63 | 64 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /design/src/main/java/com/kimjisub/design/manage/SyncCheckBox.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.design.manage 2 | 3 | import android.widget.CheckBox 4 | import android.widget.CompoundButton 5 | import java.util.* 6 | 7 | class SyncCheckBox(vararg cbs: CheckBox) { 8 | private val checkBoxes: ArrayList = ArrayList() 9 | 10 | init { 11 | for (cb in cbs) 12 | addCheckBox(cb) 13 | } 14 | 15 | fun addCheckBox(vararg cbs: CheckBox) { 16 | for (cb in cbs) { 17 | cb.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> setChecked(b) } 18 | cb.setOnLongClickListener { 19 | onLongClick() 20 | false 21 | } 22 | checkBoxes.add(cb) 23 | } 24 | } 25 | 26 | // Checked Manage 27 | 28 | private var isChecked = false 29 | var isLocked: Boolean = false 30 | set(value) { 31 | field = value 32 | for (checkBox in checkBoxes) { 33 | checkBox.isEnabled = !field 34 | checkBox.alpha = if (!field) 1f else 0.3f 35 | } 36 | } 37 | 38 | fun isChecked() = isChecked 39 | 40 | fun toggleChecked() { 41 | if (!isLocked) 42 | forceToggleChecked() 43 | } 44 | 45 | 46 | fun setChecked(b: Boolean) { 47 | if (!isLocked) 48 | forceSetChecked(b) 49 | else 50 | fix() 51 | } 52 | 53 | 54 | fun forceToggleChecked() = forceSetChecked(!isChecked) 55 | 56 | fun forceSetChecked(b: Boolean) { 57 | isChecked = b 58 | fix() 59 | onCheckedChange(b) 60 | } 61 | 62 | fun fix() { 63 | for (checkBox in checkBoxes) { 64 | if (checkBox.isChecked != isChecked) { 65 | checkBox.setOnCheckedChangeListener(null) 66 | checkBox.isChecked = isChecked 67 | checkBox.setOnCheckedChangeListener { _: CompoundButton?, b: Boolean -> setChecked(b) } 68 | } 69 | } 70 | } 71 | 72 | 73 | // view 74 | 75 | 76 | fun setVisibility(visibility: Int) { 77 | for (checkBox in checkBoxes) checkBox.visibility = visibility 78 | } 79 | 80 | 81 | // listener 82 | 83 | var onCheckedChange: OnCheckedChange? = null 84 | 85 | var onLongClick: OnLongClick? = null 86 | 87 | interface OnCheckedChange { 88 | fun onCheckedChange(b: Boolean) 89 | } 90 | 91 | interface OnLongClick { 92 | fun onLongClick() 93 | } 94 | 95 | private fun onCheckedChange(bool: Boolean) { 96 | onCheckedChange?.onCheckedChange(bool) 97 | } 98 | 99 | private fun onLongClick() { 100 | onLongClick?.onLongClick() 101 | } 102 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/BaseApplication.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad 2 | 3 | import android.app.Application 4 | import android.os.Build 5 | import com.google.firebase.remoteconfig.FirebaseRemoteConfig 6 | import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings 7 | import com.kimjisub.launchpad.db.AppDatabase 8 | import com.kimjisub.launchpad.db.repository.UnipackRepository 9 | import com.kimjisub.launchpad.manager.NotificationManager 10 | import com.kimjisub.launchpad.manager.PreferenceManager 11 | import com.kimjisub.launchpad.manager.WorkspaceManager 12 | import com.orhanobut.logger.AndroidLogAdapter 13 | import com.orhanobut.logger.Logger 14 | import com.orhanobut.logger.PrettyFormatStrategy 15 | import org.koin.android.ext.koin.androidContext 16 | import org.koin.core.context.GlobalContext.startKoin 17 | import org.koin.dsl.module 18 | 19 | class BaseApplication : Application() { 20 | override fun onCreate() { 21 | super.onCreate() 22 | 23 | setupNotification() 24 | setupLogger() 25 | setupRemoteConfig() 26 | 27 | appOpenManager = AppOpenManager(this) 28 | 29 | startKoin { 30 | androidContext(applicationContext) 31 | modules( 32 | module { 33 | single { 34 | val db = AppDatabase.getInstance(applicationContext)!! 35 | UnipackRepository(db.unipackDAO()) 36 | } 37 | 38 | single { 39 | PreferenceManager(applicationContext) 40 | } 41 | 42 | single { 43 | WorkspaceManager(applicationContext) 44 | } 45 | } 46 | ) 47 | } 48 | } 49 | 50 | private fun setupNotification() { 51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 52 | NotificationManager.createChannel(this) 53 | } 54 | 55 | private fun setupLogger() { 56 | val formatStrategy = PrettyFormatStrategy.newBuilder() 57 | .showThreadInfo(true) 58 | .methodCount(2) 59 | .methodOffset(5) 60 | .tag("com.kimjisub._") 61 | .build() 62 | Logger.addLogAdapter(AndroidLogAdapter()) 63 | Logger.d("Logger Ready") 64 | } 65 | 66 | private fun setupRemoteConfig() { 67 | val remoteConfig = FirebaseRemoteConfig.getInstance() 68 | 69 | val configSettings = FirebaseRemoteConfigSettings.Builder().apply { 70 | if (BuildConfig.DEBUG) 71 | minimumFetchIntervalInSeconds = 60 72 | }.build() 73 | 74 | remoteConfig.setConfigSettingsAsync(configSettings) 75 | remoteConfig.fetchAndActivate() 76 | } 77 | 78 | companion object { 79 | private lateinit var appOpenManager: AppOpenManager 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/fragment/MainPackPanelFragment.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.fragment 2 | 3 | import android.content.Context 4 | import android.content.DialogInterface 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import androidx.appcompat.app.AlertDialog 10 | import androidx.lifecycle.ViewModelProvider 11 | import com.kimjisub.launchpad.R 12 | import com.kimjisub.launchpad.adapter.UniPackItem 13 | import com.kimjisub.launchpad.databinding.FragmentMainPackPanelBinding 14 | import com.kimjisub.launchpad.tool.observeEvent 15 | import com.kimjisub.launchpad.viewmodel.MainPackPanelViewModel 16 | 17 | class MainPackPanelFragment(private val unipackItem: UniPackItem) : BaseFragment() { 18 | private var _b: FragmentMainPackPanelBinding? = null 19 | private val b get() = _b!! 20 | private lateinit var vm: MainPackPanelViewModel 21 | 22 | private var callbacks: Callbacks? = null 23 | 24 | interface Callbacks { 25 | fun onDelete() 26 | } 27 | 28 | override fun onCreateView( 29 | inflater: LayoutInflater, container: ViewGroup?, 30 | savedInstanceState: Bundle?, 31 | ): View { 32 | vm = ViewModelProvider( 33 | this, 34 | MainPackPanelViewModel.Factory( 35 | requireActivity().application, 36 | unipackItem.unipack 37 | ) 38 | )[MainPackPanelViewModel::class.java] 39 | _b = FragmentMainPackPanelBinding.inflate(inflater, container, false) 40 | b.apply { 41 | lifecycleOwner = this@MainPackPanelFragment 42 | vm = this@MainPackPanelFragment.vm 43 | } 44 | 45 | vm.eventDelete.observeEvent(viewLifecycleOwner) { 46 | 47 | AlertDialog.Builder(requireContext()) 48 | .setTitle(R.string.warning) 49 | .setMessage(R.string.doYouWantToDeleteUniPack) 50 | .setPositiveButton(R.string.accept) { _: DialogInterface?, _: Int -> 51 | unipackItem.unipack.delete() 52 | callbacks?.onDelete() 53 | }.setNegativeButton(R.string.cancel, null) 54 | .show() 55 | } 56 | 57 | // 텍스트가 흘러갈 수 있도록 선택 58 | b.title.isSelected = true 59 | b.subtitle.isSelected = true 60 | b.path.isSelected = true 61 | 62 | return b.root 63 | } 64 | 65 | // Lifecycle 66 | 67 | override fun onAttach(context: Context) { 68 | super.onAttach(context) 69 | callbacks = context as Callbacks? 70 | } 71 | 72 | override fun onDetach() { 73 | super.onDetach() 74 | callbacks = null 75 | } 76 | 77 | override fun onDestroyView() { 78 | super.onDestroyView() 79 | _b = null 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UniPad Android Project 2 | 3 | Get it on Google Play 4 | 5 | ## Overview 6 | 7 | UniPad is a performance-based rhythm game that enables connection with a launchpad and allows users to create their own beatmaps using the unique "unipack" format, fostering creativity and sharing within the community. 8 | 9 | ## Features 10 | 11 | - Create custom beatmaps: Design your unique beatmaps and rhythms for various songs. 12 | - Share beatmaps: Share your creations with other users in the community. 13 | - Download community beatmaps: Access a diverse library of beatmaps created by others. 14 | - In-app song library: Utilize the Public Domain licensed music library provided by the app. 15 | - Frequent updates and bug fixes: Stay up-to-date with the latest features and improvements. 16 | 17 | ## Getting Started 18 | 19 | ### Prerequisites 20 | 21 | To run this project locally, you need to have the following software installed on your computer: 22 | 23 | 1. Android Studio (latest version) 24 | 2. Android SDK (latest version) 25 | 3. Git (for cloning the repository) 26 | 27 | ### Installation 28 | 29 | To set up this project on your local machine, follow these steps: 30 | 31 | 1. Clone the repository: 32 | git clone https://github.com/kimjisub/unipad-android.git 33 | 34 | 2. Open the project in Android Studio and let it sync the Gradle files. 35 | 36 | 3. Once the sync is done, build the project and run it on your Android device or emulator. 37 | 38 | ## Contributing 39 | 40 | We welcome contributions to this project! Please follow these steps if you would like to contribute: 41 | 42 | 1. Fork the repository. 43 | 2. Create a new branch with a descriptive name (e.g., `feat/new-functionality` or `fix/bug`). 44 | 3. Commit your changes to the new branch. 45 | 4. Push your changes to your forked repository. 46 | 5. Create a new pull request in the main repository. 47 | 48 | ## License 49 | 50 | This project is licensed under the [GNU Lesser General Public License v2.1](LICENSE.md) - see the `LICENSE.md` file for details. 51 | 52 | ## Acknowledgements 53 | 54 | - Thank you to the original UniPad app for inspiring this open-source project. 55 | - Thank you to all our contributors and users for making this project possible. 56 | - For questions or more information, feel free to reach out to 0226daniel@gmail.com. 57 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /design/src/main/res/drawable/playbtn.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/LaunchpadX.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | class LaunchpadX : DriverRef() { 4 | companion object { 5 | internal val circleCode = arrayOf( 6 | intArrayOf(27, -80, 91), 7 | intArrayOf(27, -80, 92), 8 | intArrayOf(27, -80, 93), 9 | intArrayOf(27, -80, 94), 10 | intArrayOf(27, -80, 95), 11 | intArrayOf(27, -80, 96), 12 | intArrayOf(27, -80, 97), 13 | intArrayOf(27, -80, 98), 14 | intArrayOf(27, -80, 89), 15 | intArrayOf(27, -80, 79), 16 | intArrayOf(27, -80, 69), 17 | intArrayOf(27, -80, 59), 18 | intArrayOf(27, -80, 49), 19 | intArrayOf(27, -80, 39), 20 | intArrayOf(27, -80, 29), 21 | intArrayOf(27, -80, 19), 22 | intArrayOf(27, -80, 8), 23 | intArrayOf(27, -80, 7), 24 | intArrayOf(27, -80, 6), 25 | intArrayOf(27, -80, 5), 26 | intArrayOf(27, -80, 4), 27 | intArrayOf(27, -80, 3), 28 | intArrayOf(27, -80, 2), 29 | intArrayOf(27, -80, 1), 30 | intArrayOf(27, -80, 10), 31 | intArrayOf(27, -80, 20), 32 | intArrayOf(27, -80, 30), 33 | intArrayOf(27, -80, 40), 34 | intArrayOf(27, -80, 50), 35 | intArrayOf(27, -80, 60), 36 | intArrayOf(27, -80, 70), 37 | intArrayOf(27, -80, 80) 38 | ) 39 | } 40 | 41 | override fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 42 | if (cmd == 25) { 43 | val x = 9 - note / 10 44 | val y = note % 10 45 | if (y in 1..8) 46 | onPadTouch(x - 1, y - 1, velocity != 0, velocity) 47 | } else if (cmd == 27 && sig == -80) { 48 | if (note in 91..98) { 49 | onFunctionKeyTouch(note - 91, velocity != 0) 50 | } 51 | if (note in 19..89 && note % 10 == 9) { 52 | val c = 9 - note / 10 - 1 53 | onChainTouch(c, velocity != 0) 54 | onFunctionKeyTouch(c + 8, velocity != 0) 55 | } 56 | if (note in 1..8) { 57 | onChainTouch(8 - note + 16 - 8, velocity != 0) 58 | onFunctionKeyTouch(8 - note + 16, velocity != 0) 59 | } 60 | if (note in 10..80 && note % 10 == 0) { 61 | onChainTouch(note / 10 - 1 + 24 - 8, velocity != 0) 62 | onFunctionKeyTouch(note / 10 - 1 + 24, velocity != 0) 63 | } 64 | } else { 65 | onUnknownReceived(cmd, sig, note, velocity) 66 | } 67 | } 68 | 69 | override fun sendPadLed(x: Int, y: Int, velocity: Int) { 70 | sendSignal(25, -112, 10 * (8 - x) + y + 1, velocity) 71 | } 72 | 73 | override fun sendChainLed(c: Int, velocity: Int) { 74 | if (c in 0..7) 75 | sendFunctionkeyLed(c + 8, velocity) 76 | } 77 | 78 | override fun sendFunctionkeyLed(f: Int, velocity: Int) { 79 | if (f in 0..31) 80 | sendSignal( 81 | circleCode[f][0].toByte(), 82 | circleCode[f][1].toByte(), 83 | circleCode[f][2].toByte(), 84 | velocity.toByte() 85 | ) 86 | } 87 | 88 | 89 | override fun sendClearLed() { 90 | for (i in 0..7) 91 | for (j in 0..7) 92 | sendPadLed(i, j, 0) 93 | for (i in 0..31) 94 | sendFunctionkeyLed(i, 0) 95 | } 96 | } -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/adapter/ThemeAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.adapter 2 | 3 | import android.content.Context 4 | import android.content.pm.ApplicationInfo 5 | import android.content.pm.PackageManager 6 | import android.content.res.Resources 7 | import android.graphics.drawable.Drawable 8 | import android.view.LayoutInflater 9 | import android.view.ViewGroup 10 | import androidx.recyclerview.widget.RecyclerView 11 | import com.kimjisub.launchpad.R.layout 12 | import com.kimjisub.launchpad.databinding.ItemThemeBinding 13 | import com.kimjisub.launchpad.manager.ThemeResources 14 | 15 | class ThemeItem(context: Context, val package_name: String) { 16 | val res: Resources = context.packageManager.getResourcesForApplication(package_name) 17 | 18 | val icon: Drawable = 19 | res.getDrawable(res.getIdentifier("$package_name:drawable/theme_ic", null, null)) 20 | val name: String = 21 | res.getString(res.getIdentifier("$package_name:string/theme_name", null, null)) 22 | val author: String = 23 | res.getString(res.getIdentifier("$package_name:string/theme_author", null, null)) 24 | val description: String = 25 | res.getString(res.getIdentifier("$package_name:string/theme_description", null, null)) 26 | val version: String? = context.packageManager.getPackageInfo(package_name, 0).versionName 27 | 28 | val resources: ThemeResources? = null 29 | } 30 | 31 | class ThemeHolder(val binding: ItemThemeBinding) : RecyclerView.ViewHolder(binding.root) 32 | 33 | 34 | class ThemeAdapter(val list: ArrayList) : RecyclerView.Adapter() { 35 | 36 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThemeHolder { 37 | val view = LayoutInflater.from(parent.context) 38 | .inflate(layout.item_theme, parent, false) 39 | 40 | return ThemeHolder(ItemThemeBinding.bind(view)) 41 | } 42 | 43 | override fun onBindViewHolder(holder: ThemeHolder, position: Int) { 44 | holder.apply { 45 | if (position < list.size) 46 | binding.data = list[position] 47 | else { 48 | val context = holder.binding.root.context 49 | binding.data = null 50 | } 51 | } 52 | } 53 | 54 | override fun getItemCount(): Int = list.size + 1 55 | } 56 | 57 | object ThemeTool { 58 | fun getThemePackList(context: Context): ArrayList { 59 | val ret = ArrayList() 60 | try { 61 | ret.add(ThemeItem(context, context.packageName)) 62 | } catch (e: Exception) { 63 | e.printStackTrace() 64 | } 65 | val packages: List = 66 | context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA) 67 | for (applicationInfo in packages) { 68 | val packageName: String = applicationInfo.packageName 69 | if (packageName.startsWith("com.kimjisub.launchpad.theme.")) { 70 | try { 71 | ret.add(ThemeItem(context, packageName)) 72 | } catch (e: Exception) { 73 | e.printStackTrace() 74 | } 75 | } 76 | } 77 | return ret 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_store.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | 25 | 26 | 32 | 33 | 34 | 35 | 39 | 40 | 43 | 44 | 53 | 54 | 55 | 56 | 63 | 64 | 69 | 70 | 76 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /app/src/main/java/com/kimjisub/launchpad/midi/driver/DriverRef.kt: -------------------------------------------------------------------------------- 1 | package com.kimjisub.launchpad.midi.driver 2 | 3 | import com.kimjisub.launchpad.tool.Log 4 | 5 | abstract class DriverRef { 6 | 7 | // OnCycleListener 8 | 9 | private var onCycleListener: OnCycleListener? = null 10 | fun setOnCycleListener(listener: OnCycleListener?): DriverRef { 11 | onCycleListener = listener 12 | return this 13 | } 14 | 15 | interface OnCycleListener { 16 | fun onConnected() 17 | fun onDisconnected() 18 | } 19 | 20 | //// 21 | 22 | fun onConnected() = onCycleListener?.onConnected() 23 | 24 | fun onDisconnected() = onCycleListener?.onDisconnected() 25 | 26 | // OnReceiveSignalListener 27 | 28 | private var onReceiveSignalListener: OnReceiveSignalListener? = null 29 | fun setOnGetSignalListener(listener: OnReceiveSignalListener?): DriverRef { 30 | onReceiveSignalListener = listener 31 | return this 32 | } 33 | 34 | interface OnReceiveSignalListener { 35 | fun onReceived(cmd: Int, sig: Int, note: Int, velocity: Int) 36 | fun onUnknownReceived(cmd: Int, sig: Int, note: Int, velocity: Int) 37 | 38 | fun onPadTouch(x: Int, y: Int, upDown: Boolean, velocity: Int) 39 | fun onChainTouch(c: Int, upDown: Boolean) 40 | fun onFunctionkeyTouch(f: Int, upDown: Boolean) 41 | } 42 | 43 | //// 44 | 45 | 46 | open fun getSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 47 | Log.midiDetail("onReceived($cmd, $sig, $note, $velocity)") 48 | onReceiveSignalListener?.onReceived(cmd, sig, note, velocity) 49 | } 50 | 51 | fun onPadTouch(x: Int, y: Int, upDown: Boolean, velocity: Int) { 52 | Log.midiDetail("onPadTouch($x, $y, $upDown, $velocity)") 53 | onReceiveSignalListener?.onPadTouch(x, y, upDown, velocity) 54 | } 55 | 56 | fun onChainTouch(c: Int, upDown: Boolean) { 57 | Log.midiDetail("onChainTouch($c, $upDown)") 58 | onReceiveSignalListener?.onChainTouch(c, upDown) 59 | } 60 | 61 | fun onFunctionKeyTouch(f: Int, upDown: Boolean) { 62 | Log.midiDetail("onFunctionKeyTouch($f, $upDown)") 63 | onReceiveSignalListener?.onFunctionkeyTouch(f, upDown) 64 | } 65 | 66 | fun onUnknownReceived(cmd: Int, sig: Int, note: Int, velocity: Int) { 67 | Log.midiDetail("onUnknownReceived($cmd, $sig, $note, $velocity)") 68 | onReceiveSignalListener?.onUnknownReceived(cmd, sig, note, velocity) 69 | } 70 | 71 | // OnSendSignalListener 72 | 73 | private var onSendSignalListener: OnSendSignalListener? = null 74 | fun setOnSendSignalListener(listener: OnSendSignalListener?): DriverRef { 75 | onSendSignalListener = listener 76 | return this 77 | } 78 | 79 | interface OnSendSignalListener { 80 | fun onSend(cmd: Byte, sig: Byte, note: Byte, velocity: Byte) 81 | } 82 | 83 | //// 84 | 85 | internal fun sendSignal(cmd: Byte, sig: Byte, note: Byte, velocity: Byte) { 86 | onSendSignalListener?.onSend(cmd, sig, note, velocity) 87 | } 88 | 89 | internal fun sendSignal(cmd: Int, sig: Int, note: Int, velocity: Int) { 90 | sendSignal(cmd.toByte(), sig.toByte(), note.toByte(), velocity.toByte()) 91 | } 92 | 93 | abstract fun sendPadLed(x: Int, y: Int, velocity: Int) 94 | abstract fun sendChainLed(c: Int, velocity: Int) 95 | abstract fun sendFunctionkeyLed(f: Int, velocity: Int) 96 | abstract fun sendClearLed() 97 | } --------------------------------------------------------------------------------