diff options
author | Roman Birg <roman@cyngn.com> | 2016-08-25 06:49:57 -0700 |
---|---|---|
committer | Roman Birg <roman@cyngn.com> | 2016-08-25 06:49:57 -0700 |
commit | 173320f2fcc50d819a0ed681e12f34e8739ed610 (patch) | |
tree | 4c12e256a18eb75aeacfd47ae852a0644598d93d | |
parent | 47248a06db1abcefd0668bd2dd6d61d2abb3bbb3 (diff) | |
parent | e95b0267e571f128d881b9c0881f5e25f725239e (diff) | |
download | android_packages_apps_AudioFX-173320f2fcc50d819a0ed681e12f34e8739ed610.tar.gz android_packages_apps_AudioFX-173320f2fcc50d819a0ed681e12f34e8739ed610.tar.bz2 android_packages_apps_AudioFX-173320f2fcc50d819a0ed681e12f34e8739ed610.zip |
Merge branch 'cm-13.0-oss' into HEAD
Change-Id: Ie762fdbe5b70dec6843ce489e78d802662e148fb
285 files changed, 11035 insertions, 3058 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..259e997 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +src_effects_priv @@ -1,13 +1,42 @@ -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := framework -LOCAL_STATIC_JAVA_LIBRARIES := org.cyanogenmod.platform.sdk +LOCAL_PACKAGE_NAME := AudioFX +LOCAL_MODULE_TAGS := optional + LOCAL_OVERRIDES_PACKAGES := DSPManager -LOCAL_PACKAGE_NAME := AudioFX + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +ifeq ($(wildcard $(LOCAL_PATH)/src_effects_priv),) +LOCAL_SRC_FILES += $(call all-java-files-under, src_effects) +else +$(warning *** including private implementations of effects ***) +LOCAL_AAPT_FLAGS += --rename-manifest-package com.cyngn.audiofx +LOCAL_SRC_FILES += $(call all-java-files-under, src_effects_priv) +endif + +LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 org.cyanogenmod.platform.sdk + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res) +LOCAL_AAPT_FLAGS := --auto-add-overlay +LOCAL_AAPT_FLAGS += --extra-packages com.cyanogen.ambient + +LOCAL_STATIC_JAVA_AAR_LIBRARIES := ambientsdk + LOCAL_PRIVILEGED_MODULE := true LOCAL_CERTIFICATE := platform +# Sign the package when not using test-keys +ifneq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/target/product/security/testkey) +LOCAL_CERTIFICATE := cyngn-app +endif + include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call all-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3d87d52..5941995 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -15,58 +15,50 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.cyanogenmod.audiofx" - android:installLocation="auto" - android:versionName="1.4.0" - android:versionCode="10400"> + package="com.cyngn.audiofx" + android:versionName="2.1.0" + android:versionCode="20100"> - <uses-sdk android:minSdkVersion="21"/> + <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" /> - <uses-feature android:name="android.software.leanback" - android:required="false" /> - - <uses-feature android:name="android.hardware.touchscreen" - android:required="false" /> - - <uses-feature android:name="android.hardware.screen.portrait" - android:required="false" /> - - <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> - <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> - <uses-permission android:name="android.permission.INTERNET"/> - <uses-permission android:name="android.permission.BLUETOOTH"/> - <uses-permission android:name="android.permission.RECORD_AUDIO"/> - <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> + <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + <uses-permission android:name="android.permission.VIBRATE" /> + + <uses-permission android:name="cyanogenmod.permission.PUBLISH_CUSTOM_TILE" /> <uses-permission android:name="cyanogenmod.permission.OBSERVE_AUDIO_SESSIONS" /> + <permission android:name="com.cyngn.audiofx.ANALYTICS_PERMISSION" + android:protectionLevel="signature" /> + <application - android:icon="@mipmap/ic_launcher_audiofx" - android:label="@string/app_name_cm" + android:icon="@mipmap/ic_launcher" + android:allowBackup="false" + android:label="@string/app_name" android:supportsRtl="true" android:hardwareAccelerated="true" - android:persistent="true" + android:name=".AudioFxApplication" android:theme="@android:style/Theme.NoTitleBar"> <activity - android:name=".ActivityMusic" - android:theme="@style/AppTheme" - android:label="@string/app_name_cm" - android:launchMode="singleTop" - android:noHistory="false"> + android:name=".activity.ActivityMusic" + android:theme="@style/AppThemeV2" + android:label="@string/eq_dialog_title" + android:launchMode="singleInstance" + android:configChanges="locale|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> - <category android:name="android.intent.category.LEANBACK_SETTINGS" /> </intent-filter> <intent-filter> <action android:name="android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"/> <category android:name="android.intent.category.DEFAULT"/> - <category android:name="android.intent.category.CATEGORY_CONTENT_MUSIC"/> </intent-filter> </activity> <activity - android:name=".ControlPanelPicker" + android:name=".activity.ControlPanelPicker" android:theme="@*android:style/Theme.Material.Dialog.Alert" android:excludeFromRecents="true"/> @@ -86,10 +78,15 @@ </intent-filter> </receiver> + <receiver android:name=".receiver.QuickSettingsTileReceiver"> + <intent-filter> + <action android:name="com.cyngn.audiofx.action.TOGGLE_DEVICE" /> + </intent-filter> + </receiver> + <service android:name="Compatibility$Service" android:exported="false"/> - <service android:name=".HeadsetService"/> <activity android:name="Compatibility$Redirector"> @@ -100,15 +97,9 @@ </intent-filter> </activity> - <service android:name=".HeadsetService" /> - - <receiver android:name=".BootReceiver"> - <intent-filter> - <action android:name="android.intent.action.BOOT_COMPLETED"/> - </intent-filter> - </receiver> + <service android:name=".service.AudioFxService"/> - <receiver android:name=".ServiceDispatcher"> + <receiver android:name=".receiver.ServiceDispatcher"> <intent-filter> <action android:name="android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"/> <action android:name="android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"/> @@ -118,5 +109,20 @@ </intent-filter> </receiver> + <receiver android:name=".service.BootReceiver"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED"/> + </intent-filter> + </receiver> + + <provider + android:name="com.cyanogen.ambient.analytics.provider.AnalyticsProvider" + android:authorities="com.cyngn.audiofx" + android:exported="true" + android:grantUriPermissions="true" + android:readPermission="com.cyngn.audiofx.ANALYTICS_PERMISSION"/> + + <meta-data android:name="com.cyanogen.ambient.analytics.key" + android:value="38dsfaasfdk2347dfsuiadslk43"/> </application> </manifest> diff --git a/res/drawable-hdpi/ab_transparent_dark_holo.9.png b/res/drawable-hdpi/ab_transparent_dark_holo.9.png Binary files differdeleted file mode 100644 index 8c6afff..0000000 --- a/res/drawable-hdpi/ab_transparent_dark_holo.9.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_action_device_bluetooth.png b/res/drawable-hdpi/ic_action_device_bluetooth.png Binary files differnew file mode 100644 index 0000000..4405ab0 --- /dev/null +++ b/res/drawable-hdpi/ic_action_device_bluetooth.png diff --git a/res/drawable-hdpi/ic_action_device_headphones.png b/res/drawable-hdpi/ic_action_device_headphones.png Binary files differnew file mode 100644 index 0000000..3753efb --- /dev/null +++ b/res/drawable-hdpi/ic_action_device_headphones.png diff --git a/res/drawable-hdpi/ic_action_device_speaker.png b/res/drawable-hdpi/ic_action_device_speaker.png Binary files differnew file mode 100644 index 0000000..47e868b --- /dev/null +++ b/res/drawable-hdpi/ic_action_device_speaker.png diff --git a/res/drawable-hdpi/ic_action_device_usb.png b/res/drawable-hdpi/ic_action_device_usb.png Binary files differnew file mode 100644 index 0000000..ebcec02 --- /dev/null +++ b/res/drawable-hdpi/ic_action_device_usb.png diff --git a/res/drawable-hdpi/ic_action_dsp_icons_bluetoof.png b/res/drawable-hdpi/ic_action_dsp_icons_bluetoof.png Binary files differnew file mode 100644 index 0000000..886ad7c --- /dev/null +++ b/res/drawable-hdpi/ic_action_dsp_icons_bluetoof.png diff --git a/res/drawable-hdpi/ic_action_dsp_icons_headphones.png b/res/drawable-hdpi/ic_action_dsp_icons_headphones.png Binary files differnew file mode 100644 index 0000000..6b16ec9 --- /dev/null +++ b/res/drawable-hdpi/ic_action_dsp_icons_headphones.png diff --git a/res/drawable-hdpi/ic_action_dsp_icons_speaker.png b/res/drawable-hdpi/ic_action_dsp_icons_speaker.png Binary files differnew file mode 100644 index 0000000..0b0a440 --- /dev/null +++ b/res/drawable-hdpi/ic_action_dsp_icons_speaker.png diff --git a/res/drawable-hdpi/ic_action_dsp_icons_usb.png b/res/drawable-hdpi/ic_action_dsp_icons_usb.png Binary files differnew file mode 100644 index 0000000..c5653f2 --- /dev/null +++ b/res/drawable-hdpi/ic_action_dsp_icons_usb.png diff --git a/res/drawable-hdpi/ic_action_dsp_icons_wifi.png b/res/drawable-hdpi/ic_action_dsp_icons_wifi.png Binary files differnew file mode 100644 index 0000000..6ad6a1f --- /dev/null +++ b/res/drawable-hdpi/ic_action_dsp_icons_wifi.png diff --git a/res/drawable-hdpi/ic_action_lock_open.png b/res/drawable-hdpi/ic_action_lock_open.png Binary files differnew file mode 100644 index 0000000..63b06ba --- /dev/null +++ b/res/drawable-hdpi/ic_action_lock_open.png diff --git a/res/drawable-hdpi/ic_action_lock_outline.png b/res/drawable-hdpi/ic_action_lock_outline.png Binary files differnew file mode 100644 index 0000000..78549eb --- /dev/null +++ b/res/drawable-hdpi/ic_action_lock_outline.png diff --git a/res/drawable-hdpi/ic_content_add_circle_outline.png b/res/drawable-hdpi/ic_content_add_circle_outline.png Binary files differnew file mode 100644 index 0000000..413ae29 --- /dev/null +++ b/res/drawable-hdpi/ic_content_add_circle_outline.png diff --git a/res/drawable-hdpi/ic_content_clear.png b/res/drawable-hdpi/ic_content_clear.png Binary files differnew file mode 100644 index 0000000..9fa0919 --- /dev/null +++ b/res/drawable-hdpi/ic_content_clear.png diff --git a/res/drawable-hdpi/ic_content_create.png b/res/drawable-hdpi/ic_content_create.png Binary files differnew file mode 100644 index 0000000..80eeb02 --- /dev/null +++ b/res/drawable-hdpi/ic_content_create.png diff --git a/res/drawable-hdpi/knob.png b/res/drawable-hdpi/knob.png Binary files differdeleted file mode 100644 index df50b22..0000000 --- a/res/drawable-hdpi/knob.png +++ /dev/null diff --git a/res/drawable-hdpi/knob_large.png b/res/drawable-hdpi/knob_large.png Binary files differdeleted file mode 100644 index 1eba937..0000000 --- a/res/drawable-hdpi/knob_large.png +++ /dev/null diff --git a/res/drawable-hdpi/knob_toggle_off.png b/res/drawable-hdpi/knob_toggle_off.png Binary files differdeleted file mode 100644 index a89595e..0000000 --- a/res/drawable-hdpi/knob_toggle_off.png +++ /dev/null diff --git a/res/drawable-hdpi/knob_toggle_on.png b/res/drawable-hdpi/knob_toggle_on.png Binary files differdeleted file mode 100644 index ab8d3d7..0000000 --- a/res/drawable-hdpi/knob_toggle_on.png +++ /dev/null diff --git a/res/drawable-hdpi/preset_bar.9.png b/res/drawable-hdpi/preset_bar.9.png Binary files differdeleted file mode 100644 index 71cbc96..0000000 --- a/res/drawable-hdpi/preset_bar.9.png +++ /dev/null diff --git a/res/drawable-hdpi/progress_vertical_bg_holo_dark.9.png b/res/drawable-hdpi/progress_vertical_bg_holo_dark.9.png Binary files differdeleted file mode 100644 index 92e34bf..0000000 --- a/res/drawable-hdpi/progress_vertical_bg_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-hdpi/progress_vertical_primary_holo_dark.9.png b/res/drawable-hdpi/progress_vertical_primary_holo_dark.9.png Binary files differdeleted file mode 100644 index cbd537b..0000000 --- a/res/drawable-hdpi/progress_vertical_primary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-hdpi/progress_vertical_secondary_holo_dark.9.png b/res/drawable-hdpi/progress_vertical_secondary_holo_dark.9.png Binary files differdeleted file mode 100644 index 73a0e40..0000000 --- a/res/drawable-hdpi/progress_vertical_secondary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_control_disabled_holo.png b/res/drawable-hdpi/scrubber_control_disabled_holo.png Binary files differdeleted file mode 100644 index c173f0e..0000000 --- a/res/drawable-hdpi/scrubber_control_disabled_holo.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_control_focused_holo.png b/res/drawable-hdpi/scrubber_control_focused_holo.png Binary files differdeleted file mode 100644 index d302df6..0000000 --- a/res/drawable-hdpi/scrubber_control_focused_holo.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_control_normal_holo.png b/res/drawable-hdpi/scrubber_control_normal_holo.png Binary files differdeleted file mode 100644 index 802d046..0000000 --- a/res/drawable-hdpi/scrubber_control_normal_holo.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_control_pressed_holo.png b/res/drawable-hdpi/scrubber_control_pressed_holo.png Binary files differdeleted file mode 100644 index 5e82872..0000000 --- a/res/drawable-hdpi/scrubber_control_pressed_holo.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_vertical_primary_holo.9.png b/res/drawable-hdpi/scrubber_vertical_primary_holo.9.png Binary files differdeleted file mode 100644 index 874dcc8..0000000 --- a/res/drawable-hdpi/scrubber_vertical_primary_holo.9.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_vertical_secondary_holo.9.png b/res/drawable-hdpi/scrubber_vertical_secondary_holo.9.png Binary files differdeleted file mode 100644 index a783343..0000000 --- a/res/drawable-hdpi/scrubber_vertical_secondary_holo.9.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_vertical_track_holo_dark.9.png b/res/drawable-hdpi/scrubber_vertical_track_holo_dark.9.png Binary files differdeleted file mode 100644 index 9f12c60..0000000 --- a/res/drawable-hdpi/scrubber_vertical_track_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-hdpi/scrubber_vertical_track_holo_light.9.png b/res/drawable-hdpi/scrubber_vertical_track_holo_light.9.png Binary files differdeleted file mode 100644 index 325b86a..0000000 --- a/res/drawable-hdpi/scrubber_vertical_track_holo_light.9.png +++ /dev/null diff --git a/res/drawable-hdpi/switch_thumb_activated.9.png b/res/drawable-hdpi/switch_thumb_activated.9.png Binary files differdeleted file mode 100644 index 072aa64..0000000 --- a/res/drawable-hdpi/switch_thumb_activated.9.png +++ /dev/null diff --git a/res/drawable-hdpi/switch_thumb_off.9.png b/res/drawable-hdpi/switch_thumb_off.9.png Binary files differdeleted file mode 100644 index 45786a3..0000000 --- a/res/drawable-hdpi/switch_thumb_off.9.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_action_device_bluetooth.png b/res/drawable-mdpi/ic_action_device_bluetooth.png Binary files differnew file mode 100644 index 0000000..73efdb4 --- /dev/null +++ b/res/drawable-mdpi/ic_action_device_bluetooth.png diff --git a/res/drawable-mdpi/ic_action_device_headphones.png b/res/drawable-mdpi/ic_action_device_headphones.png Binary files differnew file mode 100644 index 0000000..5eab0ae --- /dev/null +++ b/res/drawable-mdpi/ic_action_device_headphones.png diff --git a/res/drawable-mdpi/ic_action_device_speaker.png b/res/drawable-mdpi/ic_action_device_speaker.png Binary files differnew file mode 100644 index 0000000..3b5da86 --- /dev/null +++ b/res/drawable-mdpi/ic_action_device_speaker.png diff --git a/res/drawable-mdpi/ic_action_device_usb.png b/res/drawable-mdpi/ic_action_device_usb.png Binary files differnew file mode 100644 index 0000000..8d96938 --- /dev/null +++ b/res/drawable-mdpi/ic_action_device_usb.png diff --git a/res/drawable-mdpi/ic_action_dsp_icons_bluetoof.png b/res/drawable-mdpi/ic_action_dsp_icons_bluetoof.png Binary files differnew file mode 100644 index 0000000..8ae42f2 --- /dev/null +++ b/res/drawable-mdpi/ic_action_dsp_icons_bluetoof.png diff --git a/res/drawable-mdpi/ic_action_dsp_icons_headphones.png b/res/drawable-mdpi/ic_action_dsp_icons_headphones.png Binary files differnew file mode 100644 index 0000000..0891cd2 --- /dev/null +++ b/res/drawable-mdpi/ic_action_dsp_icons_headphones.png diff --git a/res/drawable-mdpi/ic_action_dsp_icons_speaker.png b/res/drawable-mdpi/ic_action_dsp_icons_speaker.png Binary files differnew file mode 100644 index 0000000..055e078 --- /dev/null +++ b/res/drawable-mdpi/ic_action_dsp_icons_speaker.png diff --git a/res/drawable-mdpi/ic_action_dsp_icons_usb.png b/res/drawable-mdpi/ic_action_dsp_icons_usb.png Binary files differnew file mode 100644 index 0000000..a0ad195 --- /dev/null +++ b/res/drawable-mdpi/ic_action_dsp_icons_usb.png diff --git a/res/drawable-mdpi/ic_action_dsp_icons_wifi.png b/res/drawable-mdpi/ic_action_dsp_icons_wifi.png Binary files differnew file mode 100644 index 0000000..da14e4d --- /dev/null +++ b/res/drawable-mdpi/ic_action_dsp_icons_wifi.png diff --git a/res/drawable-mdpi/ic_action_lock_open.png b/res/drawable-mdpi/ic_action_lock_open.png Binary files differnew file mode 100644 index 0000000..ac0f891 --- /dev/null +++ b/res/drawable-mdpi/ic_action_lock_open.png diff --git a/res/drawable-mdpi/ic_action_lock_outline.png b/res/drawable-mdpi/ic_action_lock_outline.png Binary files differnew file mode 100644 index 0000000..86eeca4 --- /dev/null +++ b/res/drawable-mdpi/ic_action_lock_outline.png diff --git a/res/drawable-mdpi/ic_content_add_circle_outline.png b/res/drawable-mdpi/ic_content_add_circle_outline.png Binary files differnew file mode 100644 index 0000000..5928019 --- /dev/null +++ b/res/drawable-mdpi/ic_content_add_circle_outline.png diff --git a/res/drawable-mdpi/ic_content_clear.png b/res/drawable-mdpi/ic_content_clear.png Binary files differnew file mode 100644 index 0000000..ee7dea1 --- /dev/null +++ b/res/drawable-mdpi/ic_content_clear.png diff --git a/res/drawable-mdpi/ic_content_create.png b/res/drawable-mdpi/ic_content_create.png Binary files differnew file mode 100644 index 0000000..13dd7da --- /dev/null +++ b/res/drawable-mdpi/ic_content_create.png diff --git a/res/drawable-mdpi/knob.png b/res/drawable-mdpi/knob.png Binary files differdeleted file mode 100644 index bfdf28f..0000000 --- a/res/drawable-mdpi/knob.png +++ /dev/null diff --git a/res/drawable-mdpi/knob_large.png b/res/drawable-mdpi/knob_large.png Binary files differdeleted file mode 100644 index 43b508e..0000000 --- a/res/drawable-mdpi/knob_large.png +++ /dev/null diff --git a/res/drawable-mdpi/preset_bar.9.png b/res/drawable-mdpi/preset_bar.9.png Binary files differdeleted file mode 100644 index 5cadbb4..0000000 --- a/res/drawable-mdpi/preset_bar.9.png +++ /dev/null diff --git a/res/drawable-mdpi/progress_vertical_bg_holo_dark.9.png b/res/drawable-mdpi/progress_vertical_bg_holo_dark.9.png Binary files differdeleted file mode 100644 index 662e59b..0000000 --- a/res/drawable-mdpi/progress_vertical_bg_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-mdpi/progress_vertical_primary_holo_dark.9.png b/res/drawable-mdpi/progress_vertical_primary_holo_dark.9.png Binary files differdeleted file mode 100644 index e7e32cb..0000000 --- a/res/drawable-mdpi/progress_vertical_primary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-mdpi/progress_vertical_secondary_holo_dark.9.png b/res/drawable-mdpi/progress_vertical_secondary_holo_dark.9.png Binary files differdeleted file mode 100644 index 2fee3a8..0000000 --- a/res/drawable-mdpi/progress_vertical_secondary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-mdpi/scrubber_vertical_primary_holo.9.png b/res/drawable-mdpi/scrubber_vertical_primary_holo.9.png Binary files differdeleted file mode 100644 index ecea48d..0000000 --- a/res/drawable-mdpi/scrubber_vertical_primary_holo.9.png +++ /dev/null diff --git a/res/drawable-mdpi/scrubber_vertical_secondary_holo.9.png b/res/drawable-mdpi/scrubber_vertical_secondary_holo.9.png Binary files differdeleted file mode 100644 index 35bf199..0000000 --- a/res/drawable-mdpi/scrubber_vertical_secondary_holo.9.png +++ /dev/null diff --git a/res/drawable-mdpi/scrubber_vertical_track_holo_dark.9.png b/res/drawable-mdpi/scrubber_vertical_track_holo_dark.9.png Binary files differdeleted file mode 100644 index 8a7fbde..0000000 --- a/res/drawable-mdpi/scrubber_vertical_track_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-mdpi/scrubber_vertical_track_holo_light.9.png b/res/drawable-mdpi/scrubber_vertical_track_holo_light.9.png Binary files differdeleted file mode 100644 index 097d05e..0000000 --- a/res/drawable-mdpi/scrubber_vertical_track_holo_light.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_action_device_bluetooth.png b/res/drawable-xhdpi/ic_action_device_bluetooth.png Binary files differnew file mode 100644 index 0000000..d599eea --- /dev/null +++ b/res/drawable-xhdpi/ic_action_device_bluetooth.png diff --git a/res/drawable-xhdpi/ic_action_device_headphones.png b/res/drawable-xhdpi/ic_action_device_headphones.png Binary files differnew file mode 100644 index 0000000..ed14cb6 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_device_headphones.png diff --git a/res/drawable-xhdpi/ic_action_device_speaker.png b/res/drawable-xhdpi/ic_action_device_speaker.png Binary files differnew file mode 100644 index 0000000..31400d3 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_device_speaker.png diff --git a/res/drawable-xhdpi/ic_action_device_usb.png b/res/drawable-xhdpi/ic_action_device_usb.png Binary files differnew file mode 100644 index 0000000..721b1c6 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_device_usb.png diff --git a/res/drawable-xhdpi/ic_action_dsp_icons_bluetoof.png b/res/drawable-xhdpi/ic_action_dsp_icons_bluetoof.png Binary files differnew file mode 100644 index 0000000..8940456 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_dsp_icons_bluetoof.png diff --git a/res/drawable-xhdpi/ic_action_dsp_icons_headphones.png b/res/drawable-xhdpi/ic_action_dsp_icons_headphones.png Binary files differnew file mode 100644 index 0000000..8543892 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_dsp_icons_headphones.png diff --git a/res/drawable-xhdpi/ic_action_dsp_icons_speaker.png b/res/drawable-xhdpi/ic_action_dsp_icons_speaker.png Binary files differnew file mode 100644 index 0000000..7365ba3 --- /dev/null +++ b/res/drawable-xhdpi/ic_action_dsp_icons_speaker.png diff --git a/res/drawable-xhdpi/ic_action_dsp_icons_usb.png b/res/drawable-xhdpi/ic_action_dsp_icons_usb.png Binary files differnew file mode 100644 index 0000000..79a6eaa --- /dev/null +++ b/res/drawable-xhdpi/ic_action_dsp_icons_usb.png diff --git a/res/drawable-xhdpi/ic_action_dsp_icons_wifi.png b/res/drawable-xhdpi/ic_action_dsp_icons_wifi.png Binary files differnew file mode 100644 index 0000000..d1e365a --- /dev/null +++ b/res/drawable-xhdpi/ic_action_dsp_icons_wifi.png diff --git a/res/drawable-xhdpi/ic_action_lock_open.png b/res/drawable-xhdpi/ic_action_lock_open.png Binary files differnew file mode 100644 index 0000000..fc93a7e --- /dev/null +++ b/res/drawable-xhdpi/ic_action_lock_open.png diff --git a/res/drawable-xhdpi/ic_action_lock_outline.png b/res/drawable-xhdpi/ic_action_lock_outline.png Binary files differnew file mode 100644 index 0000000..ecb4c0a --- /dev/null +++ b/res/drawable-xhdpi/ic_action_lock_outline.png diff --git a/res/drawable-xhdpi/ic_content_add_circle_outline.png b/res/drawable-xhdpi/ic_content_add_circle_outline.png Binary files differnew file mode 100644 index 0000000..3efd9aa --- /dev/null +++ b/res/drawable-xhdpi/ic_content_add_circle_outline.png diff --git a/res/drawable-xhdpi/ic_content_clear.png b/res/drawable-xhdpi/ic_content_clear.png Binary files differnew file mode 100644 index 0000000..6a3e8ca --- /dev/null +++ b/res/drawable-xhdpi/ic_content_clear.png diff --git a/res/drawable-xhdpi/ic_content_create.png b/res/drawable-xhdpi/ic_content_create.png Binary files differnew file mode 100644 index 0000000..6cbbd01 --- /dev/null +++ b/res/drawable-xhdpi/ic_content_create.png diff --git a/res/drawable-xhdpi/knob.png b/res/drawable-xhdpi/knob.png Binary files differdeleted file mode 100644 index 39225b7..0000000 --- a/res/drawable-xhdpi/knob.png +++ /dev/null diff --git a/res/drawable-xhdpi/knob_large.png b/res/drawable-xhdpi/knob_large.png Binary files differdeleted file mode 100644 index 232e726..0000000 --- a/res/drawable-xhdpi/knob_large.png +++ /dev/null diff --git a/res/drawable-xhdpi/preset_bar.9.png b/res/drawable-xhdpi/preset_bar.9.png Binary files differdeleted file mode 100644 index 11fbdc5..0000000 --- a/res/drawable-xhdpi/preset_bar.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/progress_vertical_primary_holo_dark.9.png b/res/drawable-xhdpi/progress_vertical_primary_holo_dark.9.png Binary files differdeleted file mode 100644 index 27c8647..0000000 --- a/res/drawable-xhdpi/progress_vertical_primary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/progress_vertical_secondary_holo_dark.9.png b/res/drawable-xhdpi/progress_vertical_secondary_holo_dark.9.png Binary files differdeleted file mode 100644 index 5c622d8..0000000 --- a/res/drawable-xhdpi/progress_vertical_secondary_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/scrubber_vertical_primary_holo.9.png b/res/drawable-xhdpi/scrubber_vertical_primary_holo.9.png Binary files differdeleted file mode 100644 index 874dcc8..0000000 --- a/res/drawable-xhdpi/scrubber_vertical_primary_holo.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/scrubber_vertical_secondary_holo.9.png b/res/drawable-xhdpi/scrubber_vertical_secondary_holo.9.png Binary files differdeleted file mode 100644 index a783343..0000000 --- a/res/drawable-xhdpi/scrubber_vertical_secondary_holo.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/scrubber_vertical_track_holo_dark.9.png b/res/drawable-xhdpi/scrubber_vertical_track_holo_dark.9.png Binary files differdeleted file mode 100644 index 5f0fc26..0000000 --- a/res/drawable-xhdpi/scrubber_vertical_track_holo_dark.9.png +++ /dev/null diff --git a/res/drawable-xhdpi/scrubber_vertical_track_holo_light.9.png b/res/drawable-xhdpi/scrubber_vertical_track_holo_light.9.png Binary files differdeleted file mode 100644 index fc89474..0000000 --- a/res/drawable-xhdpi/scrubber_vertical_track_holo_light.9.png +++ /dev/null diff --git a/res/drawable-xxhdpi/ic_action_device_bluetooth.png b/res/drawable-xxhdpi/ic_action_device_bluetooth.png Binary files differnew file mode 100644 index 0000000..8aea295 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_device_bluetooth.png diff --git a/res/drawable-xxhdpi/ic_action_device_headphones.png b/res/drawable-xxhdpi/ic_action_device_headphones.png Binary files differnew file mode 100644 index 0000000..d9c52d1 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_device_headphones.png diff --git a/res/drawable-xxhdpi/ic_action_device_speaker.png b/res/drawable-xxhdpi/ic_action_device_speaker.png Binary files differnew file mode 100644 index 0000000..d3eaaa1 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_device_speaker.png diff --git a/res/drawable-xxhdpi/ic_action_device_usb.png b/res/drawable-xxhdpi/ic_action_device_usb.png Binary files differnew file mode 100644 index 0000000..30708bd --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_device_usb.png diff --git a/res/drawable-xxhdpi/ic_action_dsp_icons_bluetoof.png b/res/drawable-xxhdpi/ic_action_dsp_icons_bluetoof.png Binary files differnew file mode 100644 index 0000000..7bd8dfe --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_dsp_icons_bluetoof.png diff --git a/res/drawable-xxhdpi/ic_action_dsp_icons_headphones.png b/res/drawable-xxhdpi/ic_action_dsp_icons_headphones.png Binary files differnew file mode 100644 index 0000000..b571992 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_dsp_icons_headphones.png diff --git a/res/drawable-xxhdpi/ic_action_dsp_icons_speaker.png b/res/drawable-xxhdpi/ic_action_dsp_icons_speaker.png Binary files differnew file mode 100644 index 0000000..2faaa1c --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_dsp_icons_speaker.png diff --git a/res/drawable-xxhdpi/ic_action_dsp_icons_usb.png b/res/drawable-xxhdpi/ic_action_dsp_icons_usb.png Binary files differnew file mode 100644 index 0000000..df93498 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_dsp_icons_usb.png diff --git a/res/drawable-xxhdpi/ic_action_dsp_icons_wifi.png b/res/drawable-xxhdpi/ic_action_dsp_icons_wifi.png Binary files differnew file mode 100644 index 0000000..eaa3a38 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_dsp_icons_wifi.png diff --git a/res/drawable-xxhdpi/ic_action_lock_open.png b/res/drawable-xxhdpi/ic_action_lock_open.png Binary files differnew file mode 100644 index 0000000..9ed6219 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_lock_open.png diff --git a/res/drawable-xxhdpi/ic_action_lock_outline.png b/res/drawable-xxhdpi/ic_action_lock_outline.png Binary files differnew file mode 100644 index 0000000..cab70f3 --- /dev/null +++ b/res/drawable-xxhdpi/ic_action_lock_outline.png diff --git a/res/drawable-xxhdpi/ic_content_add_circle_outline.png b/res/drawable-xxhdpi/ic_content_add_circle_outline.png Binary files differnew file mode 100644 index 0000000..dabb3be --- /dev/null +++ b/res/drawable-xxhdpi/ic_content_add_circle_outline.png diff --git a/res/drawable-xxhdpi/ic_content_clear.png b/res/drawable-xxhdpi/ic_content_clear.png Binary files differnew file mode 100644 index 0000000..e2d1bec --- /dev/null +++ b/res/drawable-xxhdpi/ic_content_clear.png diff --git a/res/drawable-xxhdpi/ic_content_create.png b/res/drawable-xxhdpi/ic_content_create.png Binary files differnew file mode 100644 index 0000000..98c9332 --- /dev/null +++ b/res/drawable-xxhdpi/ic_content_create.png diff --git a/res/drawable-xxhdpi/knob.png b/res/drawable-xxhdpi/knob.png Binary files differdeleted file mode 100644 index 4cd9670..0000000 --- a/res/drawable-xxhdpi/knob.png +++ /dev/null diff --git a/res/drawable-xxhdpi/toggle_check_off.png b/res/drawable-xxhdpi/toggle_check_off.png Binary files differnew file mode 100755 index 0000000..20e5439 --- /dev/null +++ b/res/drawable-xxhdpi/toggle_check_off.png diff --git a/res/drawable-xxhdpi/toggle_check_on.png b/res/drawable-xxhdpi/toggle_check_on.png Binary files differnew file mode 100755 index 0000000..81f3299 --- /dev/null +++ b/res/drawable-xxhdpi/toggle_check_on.png diff --git a/res/drawable-xxhdpi/toggle_check_on_disabled.png b/res/drawable-xxhdpi/toggle_check_on_disabled.png Binary files differnew file mode 100644 index 0000000..b25911b --- /dev/null +++ b/res/drawable-xxhdpi/toggle_check_on_disabled.png diff --git a/res/drawable/above_shadow.xml b/res/drawable/above_shadow.xml new file mode 100644 index 0000000..cf890b9 --- /dev/null +++ b/res/drawable/above_shadow.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:startColor="#20000000" + android:endColor="@android:color/transparent" + android:angle="90" > + </gradient> +</shape>
\ No newline at end of file diff --git a/res/drawable/below_shadow.xml b/res/drawable/below_shadow.xml new file mode 100644 index 0000000..3255f0e --- /dev/null +++ b/res/drawable/below_shadow.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:startColor="#20000000" + android:endColor="@android:color/transparent" + android:angle="270" > + </gradient> +</shape>
\ No newline at end of file diff --git a/res/drawable/dash.xml b/res/drawable/dash.xml new file mode 100644 index 0000000..dbd88ba --- /dev/null +++ b/res/drawable/dash.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="line"> + + <solid android:color="#ffffffff"/> + <stroke + android:color="#66ffffff" + android:width="1dp" + android:dashWidth="1dp" + android:dashGap="1dp" /> +</shape>
\ No newline at end of file diff --git a/res/drawable/ic_action_dsp_icons_lineout.xml b/res/drawable/ic_action_dsp_icons_lineout.xml new file mode 100644 index 0000000..ab04210 --- /dev/null +++ b/res/drawable/ic_action_dsp_icons_lineout.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2016 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + <path + android:fillColor="#CCFFFFFF" + android:pathData="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1 .9 1.99 2 1.99L17 22c1.1 0 2-.9 +2-2V4c0-1.1-.9-2-2-2zm-5 2c1.1 0 2 .9 2 2s-.9 2-2 2c-1.11 0-2-.9-2-2s.89-2 +2-2zm0 16c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 +1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" /> +</vector>
\ No newline at end of file diff --git a/res/drawable/ic_qs_visualizer_off.xml b/res/drawable/ic_qs_visualizer_off.xml new file mode 100644 index 0000000..87b5df9 --- /dev/null +++ b/res/drawable/ic_qs_visualizer_off.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="64dp" + android:height="64dp" + android:viewportWidth="48" + android:viewportHeight="48"> + + <path + android:fillColor="#a5a5a5" + android:pathData="M14,42H6v-4h8V42Z M28,38h-8v4h8V38z M42,38h-8v4h8V38z M14,30H6v4h8V30z +M28,30h-8v4h8 V30z M42,30h-8v4h8V30z M14,22H6v4h8V22z M28,22h-8v4h8V22z +M42,22h-8v4h8V22z M28,14h-8v4h8V14z M42,14h-8v4h8V14z M28,6h-8v4h8V6z" /> +</vector>
\ No newline at end of file diff --git a/res/drawable/ic_qs_visualizer_on.xml b/res/drawable/ic_qs_visualizer_on.xml new file mode 100644 index 0000000..8ef7991 --- /dev/null +++ b/res/drawable/ic_qs_visualizer_on.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="64dp" + android:height="64dp" + android:viewportWidth="48" + android:viewportHeight="48"> + + <path + android:fillColor="#FFFFFFFF" + android:pathData="M14,42H6v-4h8V42Z M28,38h-8v4h8V38z M42,38h-8v4h8V38z M14,30H6v4h8V30z +M28,30h-8v4h8 V30z M42,30h-8v4h8V30z M14,22H6v4h8V22z M28,22h-8v4h8V22z +M42,22h-8v4h8V22z M28,14h-8v4h8V14z M42,14h-8v4h8V14z M28,6h-8v4h8V6z" /> +</vector>
\ No newline at end of file diff --git a/res/drawable/logo_dts_1c.xml b/res/drawable/logo_dts_1c.xml new file mode 100644 index 0000000..ed81042 --- /dev/null +++ b/res/drawable/logo_dts_1c.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2015 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="60dp" + android:height="24dp" + android:viewportWidth="60" + android:viewportHeight="24"> + + <group> + <path + android:fillColor="#FFFFFF" + android:pathData="M4.5,6.3c0,0-0.1-0.1-0.1-0.1C5.5,4.8,9,3.7,13.2,3.6c5.1-0.1,9.2,1.3,9.4,3.2 +c0,0,0,0.4,0,1.2l0,0c-0.4-1.7-4.3-3.3-9.1-3.6C9.3,4.2,5.7,5,4.5,6.3L4.5,6.3z +M5.6,7.2c1.7-0.8,4.6-1.3,7.9-1.1 +c4.8,0.3,8.7,1.8,9.1,3.6l0,0c0-0.7,0-1.2,0-1.2c-0.1-1.8-4.3-3.3-9.4-3.2C9.9,5.3,7,6,5.3,7C5.3,7,5.5,7.1,5.6,7.2L5.6,7.2z +M7.8,8.1c1.6-0.4,3.6-0.5,5.8-0.4c4.8,0.3,8.7,1.8,9.1,3.6v0c0-0.7,0-1.2,0-1.2c-0.1-1.8-4.3-3.3-9.4-3.2c-2.4,0-4.5,0.4-6.1,0.9 +C7.1,7.8,7.4,8,7.8,8.1L7.8,8.1z +M4,5.2c0.6-1.5,4.6-2.7,9.5-2.4c4.8,0.3,8.7,1.8,9.1,3.6l0-0.1c0-1-0.1-1.3-0.1-1.3 +c0-0.2-0.1-0.3-0.1-0.3c-0.7-1.6-4.5-2.8-9.2-2.7C8.4,2.1,4.4,3.5,4,5.2L4,5.2z" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M22.2,10.5c-1.1,1.4-4.7,2.5-8.9,2.5C8.2,13.1,4.1,11.7,4,9.9c0,0,0-0.4,0-1.2l0,0 +c0.4,1.7,4.3,3.3,9.1,3.6c4.2,0.3,7.8-0.5,9.1-1.9C22.1,10.4,22.2,10.5,22.2,10.5L22.2,10.5z +M21.2,9.7C21.1,9.6,21,9.5,21,9.5 +c-1.7,0.8-4.6,1.3-7.9,1.1C8.2,10.3,4.4,8.8,4,7.1l0,0c0,0.7,0,1.2,0,1.2c0.1,1.8,4.3,3.3,9.4,3.2C16.7,11.4,19.6,10.7,21.2,9.7 +L21.2,9.7z +M19.4,8.8c-0.4-0.1-0.7-0.2-0.7-0.2C17.2,9,15.2,9.1,13,9C8.2,8.7,4.4,7.1,4,5.4l0,0c0,0.7,0,1.2,0,1.2 +c0.1,1.8,4.3,3.3,9.4,3.2C15.7,9.7,17.8,9.4,19.4,8.8L19.4,8.8z +M22.5,11.5C22,13,17.9,14.2,13,13.9c-4.8-0.3-8.7-1.8-9.1-3.6 +l0,0.1c0,1,0.1,1.3,0.1,1.3c0,0.2,0.1,0.3,0.1,0.3c0.7,1.6,4.5,2.8,9.2,2.7C18.2,14.6,22.1,13.2,22.5,11.5L22.5,11.5z" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M37.6,14.5c-2.4,0-4.3,0-4.3,0c-2.3,0.1-3.5,0-3.5,0c-2.5,0-4.5-1.7-4.6-3.9 +c-0.1-2.1,1.7-3.9,4.3-4c0,0,1.2-0.1,3.2,0.2l0-0.2l0-4.3l5,0l0,4.6H40V5.3l4.8-2.1l0,3.6l1.7,0l0,1.3c-0.9,0-1.7,0-1.7,0v2.4 +c0,0.1,0,1.2,0,1.5c0,0,0.1,0.7,0.6,1.1c0,0,0.3,0.3,1,0.3c0,0,0.2,0,0.6,0h1.1l0,0c0.9,0,1.1-0.2,1.1-0.2c0.4-0.2,0.6-0.5,0.6-0.9 +c0-0.3-0.1-0.6-0.3-0.8l0,0l-1.3-1.2C48,10,47.8,9.7,47.7,9.6l0,0c-0.2-0.3-0.2-0.6-0.2-0.9c0-0.6,0.4-1.2,0.9-1.5l0,0 +C48.8,6.9,51.2,6,55.6,7l0,1.1l-0.1,0c0,0-1.2-0.1-1.6,0l0,0c-0.4,0.1-0.6,0.3-0.6,0.7c0,0.2,0.1,0.4,0.2,0.5l0,0l1.7,1.7l0,0 +c0.4,0.4,0.6,0.8,0.6,1.4c0,0.9-0.6,1.6-1.4,1.9c0,0-0.5,0.2-1.3,0.2c0,0-0.4,0-1.5,0h-0.1c-3.9,0-8,0-8,0l-0.1,0 +c-2.6-0.1-3.1-2-3.1-2c-0.1-0.4-0.2-1.3-0.2-1.3l0-3.2l-2.4,0L37.6,14.5 +M29.8,10.7c0,1.3,0.6,2,0.6,2c0.4,0.5,1.2,0.6,1.2,0.6 +c0.4,0.1,1,0,1,0l0-5.3c0,0-0.6-0.1-1,0c0,0-0.7,0-1.2,0.7c0,0-0.6,0.7-0.6,2V10.7z +M27.4,22c-1.1,0-1.5-0.5-1.5-1.5v-0.1h0.6v0.1 +c0,0.6,0.1,1,0.8,1c0.6,0,0.8-0.3,0.8-0.9c0-0.7-0.4-0.9-0.9-1.1c-0.7-0.4-1.3-0.7-1.3-1.7c0-0.9,0.6-1.3,1.4-1.3 +c0.9,0,1.4,0.4,1.4,1.3V18h-0.6v-0.1c0-0.5-0.1-0.8-0.8-0.8c-0.5,0-0.8,0.3-0.8,0.8c0,0.7,0.5,0.9,1.1,1.2c0.6,0.4,1.1,0.6,1.1,1.6 +C28.8,21.5,28.3,22,27.4,22z +M33.9,22c-1.2,0-1.3-0.8-1.3-1.9c0-1.1,0.1-2,1.3-2c1.2,0,1.2,0.8,1.2,1.9C35.2,21.3,35.1,22,33.9,22z +M34.6,19.9c0-0.6,0-1.3-0.7-1.3c-0.7,0-0.7,0.8-0.7,1.3v0.5c0,0.5,0,1.1,0.7,1.1c0.6,0,0.7-0.6,0.7-1.1V19.9z +M41.2,21.9l0-0.5 +C41,21.8,40.7,22,40.3,22c-0.7,0-1-0.5-1-1.1v-2.6h0.6v2.4c0,0.4,0,1,0.6,1c0.6,0,0.7-0.6,0.7-1.1v-2.2h0.6v2.9c0,0.3,0,0.5,0,0.8 +H41.2z +M47.8,22v-2.4c0-0.4,0-1-0.6-1c-0.5,0-0.7,0.6-0.7,1.1V22h-0.6v-2.9c0-0.3,0-0.5,0-0.8h0.6v0.5c0.2-0.3,0.5-0.6,1-0.6 +c0.7,0,1,0.5,1,1.1V22H47.8z +M54.4,21.9l0-0.4C54.2,21.8,54,22,53.6,22c-1.1,0-1.1-1.1-1.1-2c0-0.9,0.1-1.9,1.1-1.9 +c0.4,0,0.7,0.1,0.8,0.4v-2H55v4.6c0,0.2,0,0.5,0,0.7H54.4z +M53.7,18.6c-0.7,0-0.7,0.8-0.7,1.5c0,0.8,0.1,1.5,0.7,1.5 +c0.7,0,0.7-0.8,0.7-1.4C54.4,19.3,54.4,18.6,53.7,18.6z +M55.1,2.1c0.2,0,0.3,0,0.5,0.1c0.1,0.1,0.3,0.2,0.3,0.3 +C56,2.8,56,2.9,56,3.1c0,0.2,0,0.3-0.1,0.5c-0.1,0.1-0.2,0.3-0.3,0.3C55.4,4,55.3,4,55.1,4c-0.2,0-0.3,0-0.5-0.1 +c-0.1-0.1-0.3-0.2-0.3-0.3c-0.1-0.1-0.1-0.3-0.1-0.5c0-0.2,0-0.3,0.1-0.5c0.1-0.1,0.2-0.3,0.3-0.3C54.8,2.2,55,2.1,55.1,2.1z +M55.1,2.3c-0.1,0-0.3,0-0.4,0.1c-0.1,0.1-0.2,0.2-0.3,0.3c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.3,0.1,0.4c0.1,0.1,0.2,0.2,0.3,0.3 +c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.3,0,0.4-0.1c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4 +c-0.1-0.1-0.2-0.2-0.3-0.3C55.4,2.3,55.2,2.3,55.1,2.3z +M54.7,3.6v-1H55c0.1,0,0.2,0,0.3,0c0.1,0,0.1,0.1,0.1,0.1c0,0,0,0.1,0,0.1 +c0,0.1,0,0.1-0.1,0.2c-0.1,0.1-0.1,0.1-0.2,0.1c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.2l0.1,0.2h-0.2l-0.1-0.2 +c-0.1-0.1-0.1-0.2-0.2-0.2c0,0-0.1,0-0.1,0h-0.1v0.4H54.7z +M54.9,3h0.2c0.1,0,0.2,0,0.2,0c0,0,0.1-0.1,0.1-0.1c0,0,0-0.1,0-0.1 +c0,0,0,0-0.1-0.1c0,0-0.1,0-0.2,0h-0.2V3z" /> + </group> +</vector>
\ No newline at end of file diff --git a/res/drawable/logo_dts_fc.xml b/res/drawable/logo_dts_fc.xml new file mode 100644 index 0000000..a9abeb3 --- /dev/null +++ b/res/drawable/logo_dts_fc.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (c) 2015 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="60dp" + android:height="24dp" + android:viewportWidth="60" + android:viewportHeight="24"> + + <group> + <path + android:fillColor="#FDB813" + android:pathData="M4.5,6.3c0,0-0.1-0.1-0.1-0.1C5.5,4.8,9,3.7,13.2,3.6c5.1-0.1,9.2,1.3,9.4,3.2 +c0,0,0,0.4,0,1.2l0,0c-0.4-1.7-4.3-3.3-9.1-3.6C9.3,4.2,5.7,5,4.5,6.3L4.5,6.3z +M5.6,7.2c1.7-0.8,4.6-1.3,7.9-1.1 +c4.8,0.3,8.7,1.8,9.1,3.6l0,0c0-0.7,0-1.2,0-1.2c-0.1-1.8-4.3-3.3-9.4-3.2C9.9,5.3,7,6,5.3,7C5.3,7,5.5,7.1,5.6,7.2L5.6,7.2z +M7.8,8.1c1.6-0.4,3.6-0.5,5.8-0.4c4.8,0.3,8.7,1.8,9.1,3.6v0c0-0.7,0-1.2,0-1.2c-0.1-1.8-4.3-3.3-9.4-3.2c-2.4,0-4.5,0.4-6.1,0.9 +C7.1,7.8,7.4,8,7.8,8.1L7.8,8.1z +M4,5.2c0.6-1.5,4.6-2.7,9.5-2.4c4.8,0.3,8.7,1.8,9.1,3.6l0-0.1c0-1-0.1-1.3-0.1-1.3 +c0-0.2-0.1-0.3-0.1-0.3c-0.7-1.6-4.5-2.8-9.2-2.7C8.4,2.1,4.4,3.5,4,5.2L4,5.2z" /> + <path + android:fillColor="#F16521" + android:pathData="M22.2,10.5c-1.1,1.4-4.7,2.5-8.9,2.5C8.2,13.1,4.1,11.7,4,9.9c0,0,0-0.4,0-1.2l0,0 +c0.4,1.7,4.3,3.3,9.1,3.6c4.2,0.3,7.8-0.5,9.1-1.9C22.1,10.4,22.2,10.5,22.2,10.5L22.2,10.5z +M21.2,9.7C21.1,9.6,21,9.5,21,9.5 +c-1.7,0.8-4.6,1.3-7.9,1.1C8.2,10.3,4.4,8.8,4,7.1l0,0c0,0.7,0,1.2,0,1.2c0.1,1.8,4.3,3.3,9.4,3.2C16.7,11.4,19.6,10.7,21.2,9.7 +L21.2,9.7z +M19.4,8.8c-0.4-0.1-0.7-0.2-0.7-0.2C17.2,9,15.2,9.1,13,9C8.2,8.7,4.4,7.1,4,5.4l0,0c0,0.7,0,1.2,0,1.2 +c0.1,1.8,4.3,3.3,9.4,3.2C15.7,9.7,17.8,9.4,19.4,8.8L19.4,8.8z +M22.5,11.5C22,13,17.9,14.2,13,13.9c-4.8-0.3-8.7-1.8-9.1-3.6 +l0,0.1c0,1,0.1,1.3,0.1,1.3c0,0.2,0.1,0.3,0.1,0.3c0.7,1.6,4.5,2.8,9.2,2.7C18.2,14.6,22.1,13.2,22.5,11.5L22.5,11.5z" /> + <path + android:fillColor="#FFFFFF" + android:pathData="M37.6,14.5c-2.4,0-4.3,0-4.3,0c-2.3,0.1-3.5,0-3.5,0c-2.5,0-4.5-1.7-4.6-3.9 +c-0.1-2.1,1.7-3.9,4.3-4c0,0,1.2-0.1,3.2,0.2l0-0.2l0-4.3l5,0l0,4.6H40V5.3l4.8-2.1l0,3.6l1.7,0l0,1.3c-0.9,0-1.7,0-1.7,0v2.4 +c0,0.1,0,1.2,0,1.5c0,0,0.1,0.7,0.6,1.1c0,0,0.3,0.3,1,0.3c0,0,0.2,0,0.6,0h1.1l0,0c0.9,0,1.1-0.2,1.1-0.2c0.4-0.2,0.6-0.5,0.6-0.9 +c0-0.3-0.1-0.6-0.3-0.8l0,0l-1.3-1.2C48,10,47.8,9.7,47.7,9.6l0,0c-0.2-0.3-0.2-0.6-0.2-0.9c0-0.6,0.4-1.2,0.9-1.5l0,0 +C48.8,6.9,51.2,6,55.6,7l0,1.1l-0.1,0c0,0-1.2-0.1-1.6,0l0,0c-0.4,0.1-0.6,0.3-0.6,0.7c0,0.2,0.1,0.4,0.2,0.5l0,0l1.7,1.7l0,0 +c0.4,0.4,0.6,0.8,0.6,1.4c0,0.9-0.6,1.6-1.4,1.9c0,0-0.5,0.2-1.3,0.2c0,0-0.4,0-1.5,0h-0.1c-3.9,0-8,0-8,0l-0.1,0 +c-2.6-0.1-3.1-2-3.1-2c-0.1-0.4-0.2-1.3-0.2-1.3l0-3.2l-2.4,0L37.6,14.5 +M29.8,10.7c0,1.3,0.6,2,0.6,2c0.4,0.5,1.2,0.6,1.2,0.6 +c0.4,0.1,1,0,1,0l0-5.3c0,0-0.6-0.1-1,0c0,0-0.7,0-1.2,0.7c0,0-0.6,0.7-0.6,2V10.7z +M27.4,22c-1.1,0-1.5-0.5-1.5-1.5v-0.1h0.6v0.1 +c0,0.6,0.1,1,0.8,1c0.6,0,0.8-0.3,0.8-0.9c0-0.7-0.4-0.9-0.9-1.1c-0.7-0.4-1.3-0.7-1.3-1.7c0-0.9,0.6-1.3,1.4-1.3 +c0.9,0,1.4,0.4,1.4,1.3V18h-0.6v-0.1c0-0.5-0.1-0.8-0.8-0.8c-0.5,0-0.8,0.3-0.8,0.8c0,0.7,0.5,0.9,1.1,1.2c0.6,0.4,1.1,0.6,1.1,1.6 +C28.8,21.5,28.3,22,27.4,22z +M33.9,22c-1.2,0-1.3-0.8-1.3-1.9c0-1.1,0.1-2,1.3-2c1.2,0,1.2,0.8,1.2,1.9C35.2,21.3,35.1,22,33.9,22z +M34.6,19.9c0-0.6,0-1.3-0.7-1.3c-0.7,0-0.7,0.8-0.7,1.3v0.5c0,0.5,0,1.1,0.7,1.1c0.6,0,0.7-0.6,0.7-1.1V19.9z +M41.2,21.9l0-0.5 +C41,21.8,40.7,22,40.3,22c-0.7,0-1-0.5-1-1.1v-2.6h0.6v2.4c0,0.4,0,1,0.6,1c0.6,0,0.7-0.6,0.7-1.1v-2.2h0.6v2.9c0,0.3,0,0.5,0,0.8 +H41.2z +M47.8,22v-2.4c0-0.4,0-1-0.6-1c-0.5,0-0.7,0.6-0.7,1.1V22h-0.6v-2.9c0-0.3,0-0.5,0-0.8h0.6v0.5c0.2-0.3,0.5-0.6,1-0.6 +c0.7,0,1,0.5,1,1.1V22H47.8z +M54.4,21.9l0-0.4C54.2,21.8,54,22,53.6,22c-1.1,0-1.1-1.1-1.1-2c0-0.9,0.1-1.9,1.1-1.9 +c0.4,0,0.7,0.1,0.8,0.4v-2H55v4.6c0,0.2,0,0.5,0,0.7H54.4z +M53.7,18.6c-0.7,0-0.7,0.8-0.7,1.5c0,0.8,0.1,1.5,0.7,1.5 +c0.7,0,0.7-0.8,0.7-1.4C54.4,19.3,54.4,18.6,53.7,18.6z +M55.1,2.1c0.2,0,0.3,0,0.5,0.1c0.1,0.1,0.3,0.2,0.3,0.3 +C56,2.8,56,2.9,56,3.1c0,0.2,0,0.3-0.1,0.5c-0.1,0.1-0.2,0.3-0.3,0.3C55.4,4,55.3,4,55.1,4c-0.2,0-0.3,0-0.5-0.1 +c-0.1-0.1-0.3-0.2-0.3-0.3c-0.1-0.1-0.1-0.3-0.1-0.5c0-0.2,0-0.3,0.1-0.5c0.1-0.1,0.2-0.3,0.3-0.3C54.8,2.2,55,2.1,55.1,2.1z +M55.1,2.3c-0.1,0-0.3,0-0.4,0.1c-0.1,0.1-0.2,0.2-0.3,0.3c-0.1,0.1-0.1,0.3-0.1,0.4c0,0.1,0,0.3,0.1,0.4c0.1,0.1,0.2,0.2,0.3,0.3 +c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.3,0,0.4-0.1c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4 +c-0.1-0.1-0.2-0.2-0.3-0.3C55.4,2.3,55.2,2.3,55.1,2.3z +M54.7,3.6v-1H55c0.1,0,0.2,0,0.3,0c0.1,0,0.1,0.1,0.1,0.1c0,0,0,0.1,0,0.1 +c0,0.1,0,0.1-0.1,0.2c-0.1,0.1-0.1,0.1-0.2,0.1c0,0,0.1,0,0.1,0.1c0,0,0.1,0.1,0.1,0.2l0.1,0.2h-0.2l-0.1-0.2 +c-0.1-0.1-0.1-0.2-0.2-0.2c0,0-0.1,0-0.1,0h-0.1v0.4H54.7z +M54.9,3h0.2c0.1,0,0.2,0,0.2,0c0,0,0.1-0.1,0.1-0.1c0,0,0-0.1,0-0.1 +c0,0,0,0-0.1-0.1c0,0-0.1,0-0.2,0h-0.2V3z" /> + </group> +</vector> diff --git a/res/drawable/maxvolume_white.png b/res/drawable/maxvolume_white.png Binary files differnew file mode 100644 index 0000000..1df2aef --- /dev/null +++ b/res/drawable/maxvolume_white.png diff --git a/res/drawable/maxxbass_white.png b/res/drawable/maxxbass_white.png Binary files differnew file mode 100644 index 0000000..118602b --- /dev/null +++ b/res/drawable/maxxbass_white.png diff --git a/res/drawable/maxxreble_white.png b/res/drawable/maxxreble_white.png Binary files differnew file mode 100644 index 0000000..44b8688 --- /dev/null +++ b/res/drawable/maxxreble_white.png diff --git a/res/drawable/maxxspace_white.png b/res/drawable/maxxspace_white.png Binary files differnew file mode 100644 index 0000000..726ac03 --- /dev/null +++ b/res/drawable/maxxspace_white.png diff --git a/res/drawable/progress_vertical_holo_dark.xml b/res/drawable/progress_vertical_holo_dark.xml deleted file mode 100644 index 9eb54b7..0000000 --- a/res/drawable/progress_vertical_holo_dark.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:id="@android:id/background" - android:drawable="@drawable/progress_vertical_bg_holo_dark" /> - - <item android:id="@android:id/secondaryProgress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/progress_vertical_secondary_holo_dark" /> - </item> - - <item android:id="@android:id/progress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/progress_vertical_primary_holo_dark" /> - </item> - -</layer-list> diff --git a/res/drawable/scrubber_control_selector_holo.xml b/res/drawable/scrubber_control_selector_holo.xml deleted file mode 100644 index f53b6b3..0000000 --- a/res/drawable/scrubber_control_selector_holo.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright (c) 2013, The Linux Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false" android:drawable="@drawable/scrubber_control_disabled_holo" /> - <item android:state_pressed="true" android:drawable="@drawable/scrubber_control_pressed_holo" /> - <item android:state_selected="true" android:drawable="@drawable/scrubber_control_focused_holo" /> - <item android:drawable="@drawable/scrubber_control_normal_holo" /> -</selector> diff --git a/res/drawable/scrubber_progress_vertical_holo_dark.xml b/res/drawable/scrubber_progress_vertical_holo_dark.xml deleted file mode 100644 index 0cc56bf..0000000 --- a/res/drawable/scrubber_progress_vertical_holo_dark.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@android:id/background" - android:drawable="@drawable/scrubber_vertical_track_holo_dark" /> - <item android:id="@android:id/secondaryProgress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/scrubber_vertical_secondary_holo" /> - </item> - <item android:id="@android:id/progress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/scrubber_vertical_primary_holo" /> - </item> -</layer-list> diff --git a/res/drawable/scrubber_progress_vertical_holo_light.xml b/res/drawable/scrubber_progress_vertical_holo_light.xml deleted file mode 100644 index 8a54edf..0000000 --- a/res/drawable/scrubber_progress_vertical_holo_light.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2010 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@android:id/background" - android:drawable="@drawable/scrubber_vertical_track_holo_light" /> - <item android:id="@android:id/secondaryProgress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/scrubber_vertical_secondary_holo" /> - </item> - <item android:id="@android:id/progress"> - <scale android:scaleHeight="100%" android:scaleGravity="bottom" - android:drawable="@drawable/scrubber_vertical_primary_holo" /> - </item> -</layer-list> diff --git a/res/drawable/toggle_check.xml b/res/drawable/toggle_check.xml new file mode 100644 index 0000000..a58137c --- /dev/null +++ b/res/drawable/toggle_check.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" android:state_checked="true"> + <bitmap android:src="@drawable/toggle_check_on_disabled" android:gravity="top"/> + </item> + + <item android:state_checked="true"> + <bitmap android:src="@drawable/toggle_check_on" android:gravity="top"/> + </item> + + <item android:state_checked="false"> + <bitmap android:src="@drawable/toggle_check_off" android:gravity="top"/> + </item> +</selector>
\ No newline at end of file diff --git a/res/drawable/toggle_lock.xml b/res/drawable/toggle_lock.xml new file mode 100644 index 0000000..f63c24f --- /dev/null +++ b/res/drawable/toggle_lock.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + + <item android:state_enabled="false" android:state_checked="true" android:drawable="@drawable/ic_action_lock_outline"/> + + <item android:state_checked="true" android:drawable="@drawable/ic_action_lock_outline"/> + + <item android:state_checked="false" android:drawable="@drawable/ic_action_lock_open"/> +</selector>
\ No newline at end of file diff --git a/res/layout-land/activity_main.xml b/res/layout-land/activity_main.xml new file mode 100644 index 0000000..bfae19c --- /dev/null +++ b/res/layout-land/activity_main.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/main_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/res/layout-land/controls_generic.xml b/res/layout-land/controls_generic.xml new file mode 100644 index 0000000..b31ddcb --- /dev/null +++ b/res/layout-land/controls_generic.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.cyngn.audiofx.knobs.KnobContainer + xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@color/knob_container_background" + android:id="@+id/knob_container" + android:layout_width="160dp" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="vertical" + android:animateLayoutChanges="true" + android:elevation="8dp" + android:paddingLeft="20dp" + android:paddingRight="20dp" + android:paddingTop="4dp" + android:paddingBottom="4dp"/> diff --git a/res/layout-land/controls_maxx_audio.xml b/res/layout-land/controls_maxx_audio.xml new file mode 100644 index 0000000..857f718 --- /dev/null +++ b/res/layout-land/controls_maxx_audio.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@color/knob_container_background" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:elevation="8dp" + android:paddingLeft="20dp" + android:padding="4dp"> + + <LinearLayout + android:paddingLeft="30dp" + android:paddingRight="30dp" + android:orientation="horizontal" + android:layout_width="match_parent" + android:minHeight="?android:listPreferredItemHeightSmall" + android:layout_height="wrap_content" + android:gravity="center_horizontal"> + + <ImageView + android:layout_width="83dp" + android:layout_height="22dp" + android:layout_gravity="center_vertical" + android:src="@drawable/maxvolume_white" + android:scaleType="fitCenter"/> + + <Space + android:layout_width="12dp" + android:layout_height="match_parent"/> + + <CheckBox + android:id="@+id/maxx_volume_switch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:button="@drawable/toggle_check" + android:gravity="center_vertical" + android:layout_gravity="center_vertical"/> + + </LinearLayout> + + <com.cyngn.audiofx.knobs.KnobContainer + android:id="@+id/knob_container" + android:layout_width="match_parent" + android:layout_height="0dp" + android:gravity="top|center_horizontal" + android:orientation="vertical" + android:animateLayoutChanges="true" + android:layout_weight="1"> + + <!-- row 1 treble & bass --> + <LinearLayout + android:layout_width="match_parent" + android:layout_weight="1" + android:layout_height="0dp" + android:orientation="horizontal"> + + <FrameLayout + android:id="@+id/treble_knob_container" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_gravity="center_horizontal" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_width="@dimen/knob_width" + android:layout_gravity="top|center_horizontal" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true"/> + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="76dp" + android:layout_height="22dp" + android:src="@drawable/maxxreble_white" + android:scaleType="fitCenter"/> + + </FrameLayout> + + <FrameLayout + android:visibility="gone" + android:id="@+id/virtualizer_knob_container" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="@dimen/knob_width" + android:layout_height="wrap_content"/> + + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="69dp" + android:layout_height="22dp" + android:src="@drawable/maxxspace_white" + android:scaleType="fitCenter"/> + </FrameLayout> + + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_weight="1" + android:layout_height="0dp" + android:orientation="horizontal"> + <FrameLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:layout_weight="1" + android:id="@+id/bass_knob_container" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="@dimen/knob_width" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true"/> + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="68dp" + android:layout_height="22dp" + android:src="@drawable/maxxbass_white" + android:scaleType="fitCenter"/> + + </FrameLayout> + </LinearLayout> + </com.cyngn.audiofx.knobs.KnobContainer> + +</LinearLayout> diff --git a/res/layout-land/fragment_audiofx.xml b/res/layout-land/fragment_audiofx.xml new file mode 100644 index 0000000..76e3e59 --- /dev/null +++ b/res/layout-land/fragment_audiofx.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.cyngn.audiofx.widget.InterceptableLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/interceptable_layout" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- equalizer layout --> + <FrameLayout class="com.cyngn.audiofx.fragment.EqualizerFragment" + android:id="@+id/equalizer" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <!-- knobs --> + <FrameLayout class="com.cyngn.audiofx.fragment.ControlsFragment" + android:id="@+id/controls" + android:layout_weight="0" + android:layout_width="wrap_content" + android:layout_height="match_parent"/> + +</com.cyngn.audiofx.widget.InterceptableLinearLayout> diff --git a/res/layout-land/fragment_audiofx_maxxaudio.xml b/res/layout-land/fragment_audiofx_maxxaudio.xml new file mode 100644 index 0000000..76e3e59 --- /dev/null +++ b/res/layout-land/fragment_audiofx_maxxaudio.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.cyngn.audiofx.widget.InterceptableLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/interceptable_layout" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- equalizer layout --> + <FrameLayout class="com.cyngn.audiofx.fragment.EqualizerFragment" + android:id="@+id/equalizer" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <!-- knobs --> + <FrameLayout class="com.cyngn.audiofx.fragment.ControlsFragment" + android:id="@+id/controls" + android:layout_weight="0" + android:layout_width="wrap_content" + android:layout_height="match_parent"/> + +</com.cyngn.audiofx.widget.InterceptableLinearLayout> diff --git a/res/layout-land/generic_knob_control.xml b/res/layout-land/generic_knob_control.xml new file mode 100644 index 0000000..38883e1 --- /dev/null +++ b/res/layout-land/generic_knob_control.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="0" + android:layout_gravity="center_vertical" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="wrap_content" + android:layout_height="@dimen/knob_width" /> + + <TextView + android:id="@+id/label" + android:layout_height="22dp" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="wrap_content"/> + +</FrameLayout> diff --git a/res/layout/action_bar_custom_components.xml b/res/layout/action_bar_custom_components.xml new file mode 100644 index 0000000..3d65eb3 --- /dev/null +++ b/res/layout/action_bar_custom_components.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + + <ViewStub + android:id="@+id/logo_stub" + android:gravity="center_vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + <CheckBox + android:id="@+id/global_toggle" + android:layout_gravity="center_vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:button="@drawable/toggle_check" + android:paddingRight="@dimen/action_bar_switch_padding" + /> + +</LinearLayout> diff --git a/res/layout/action_bar_dts_logo.xml b/res/layout/action_bar_dts_logo.xml new file mode 100644 index 0000000..89a439c --- /dev/null +++ b/res/layout/action_bar_dts_logo.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingRight="@dimen/action_bar_dts_switch_padding"> + + <TextView + android:text="@string/powered_by" + android:layout_gravity="center_horizontal" + android:textSize="10sp" + android:typeface="sans" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + + <ImageView + android:id="@+id/dts_logo" + android:src="@drawable/logo_dts_fc" + android:layout_weight="1" + android:layout_width="wrap_content" + android:layout_height="0dp" + /> + + +</LinearLayout> diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml new file mode 100644 index 0000000..e6c9f53 --- /dev/null +++ b/res/layout/activity_main.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/main_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent"> + +</FrameLayout> diff --git a/res/layout/controls_generic.xml b/res/layout/controls_generic.xml new file mode 100644 index 0000000..48b8e2f --- /dev/null +++ b/res/layout/controls_generic.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.cyngn.audiofx.knobs.KnobContainer + xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@color/knob_container_background" + android:id="@+id/knob_container" + android:gravity="top|center_horizontal" + android:orientation="horizontal" + android:animateLayoutChanges="true" + android:layout_width="match_parent" + android:layout_height="160dp" + android:elevation="8dp" + android:padding="4dp" + android:layout_weight="1"/> diff --git a/res/layout/controls_maxx_audio.xml b/res/layout/controls_maxx_audio.xml new file mode 100644 index 0000000..9d75cd7 --- /dev/null +++ b/res/layout/controls_maxx_audio.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:background="@color/knob_container_background" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="200dp" + android:elevation="8dp" + android:padding="4dp"> + + <LinearLayout + android:paddingLeft="30dp" + android:paddingRight="30dp" + android:orientation="horizontal" + android:layout_width="match_parent" + android:minHeight="?android:listPreferredItemHeightSmall" + android:layout_height="wrap_content" + android:gravity="center_horizontal"> + + <ImageView + android:layout_width="83dp" + android:layout_height="22dp" + android:layout_gravity="center_vertical" + android:src="@drawable/maxvolume_white" + android:scaleType="fitCenter"/> + + <Space + android:layout_width="12dp" + android:layout_height="match_parent"/> + + <CheckBox + android:id="@+id/maxx_volume_switch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:button="@drawable/toggle_check" + android:gravity="center_vertical" + android:layout_gravity="center_vertical"/> + + </LinearLayout> + + <com.cyngn.audiofx.knobs.KnobContainer + android:id="@+id/knob_container" + android:layout_width="match_parent" + android:layout_height="0dp" + android:gravity="top|center_horizontal" + android:orientation="horizontal" + android:animateLayoutChanges="true" + android:layout_weight="1"> + + <Space + android:layout_weight="0.1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <FrameLayout + android:id="@+id/treble_knob_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="0" + android:layout_gravity="center_horizontal" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_width="@dimen/knob_width" + android:layout_gravity="top|center_horizontal" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true"/> + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="76dp" + android:layout_height="22dp" + android:src="@drawable/maxxreble_white" + android:scaleType="fitCenter"/> + + </FrameLayout> + + <Space + android:layout_weight="0.1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:layout_weight="0" + android:id="@+id/bass_knob_container" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="@dimen/knob_width" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true"/> + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="68dp" + android:layout_height="22dp" + android:src="@drawable/maxxbass_white" + android:scaleType="fitCenter"/> + + </FrameLayout> + + <Space + android:layout_weight="0.1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <FrameLayout + android:visibility="gone" + android:id="@+id/virtualizer_knob_container" + android:layout_width="wrap_content" + android:layout_weight="0" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="@dimen/knob_width" + android:layout_height="wrap_content"/> + + + <ImageView + android:id="@+id/label" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="69dp" + android:layout_height="22dp" + android:src="@drawable/maxxspace_white" + android:scaleType="fitCenter"/> + </FrameLayout> + + <Space + android:visibility="gone" + android:layout_weight="0.1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + </com.cyngn.audiofx.knobs.KnobContainer> + +</LinearLayout> diff --git a/res/layout/eq_container.xml b/res/layout/eq_container.xml new file mode 100644 index 0000000..e41defb --- /dev/null +++ b/res/layout/eq_container.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.cyngn.audiofx.eq.EqContainerView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/eq_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <FrameLayout + android:id="@+id/eq_controls" + android:padding="4dp" + android:alpha="0" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/remove" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_content_clear" + android:paddingRight="12dp" + /> + + <ImageView + android:id="@+id/rename" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_content_create" + android:paddingRight="12dp" + /> + <CheckBox + android:id="@+id/lock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:button="@drawable/toggle_lock" + /> + </LinearLayout> + + <LinearLayout + android:layout_gravity="right" + android:layout_width="wrap_content" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/save" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_content_add_circle_outline" + /> + </LinearLayout> + + </FrameLayout> + + +</com.cyngn.audiofx.eq.EqContainerView> diff --git a/res/layout/equalizer.xml b/res/layout/equalizer.xml index 2ebee34..0f2a613 100644 --- a/res/layout/equalizer.xml +++ b/res/layout/equalizer.xml @@ -1,16 +1,42 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center" - android:background="#cc191919" - > +<com.cyngn.audiofx.eq.EqSwipeController + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/swipe_interceptor" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <include + layout="@layout/eq_container" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="0dp"/> - <org.cyanogenmod.audiofx.widget.EqualizerSurface - android:id="@+id/frequencyResponse" - android:layout_height="match_parent" - android:layout_width="match_parent" /> + <LinearLayout + android:orientation="vertical" + android:id="@+id/preset_container" + android:layout_width="match_parent" + android:layout_height="wrap_content"> -</LinearLayout> + <com.cyngn.audiofx.preset.InfiniteViewPager + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> + + <com.cyngn.audiofx.viewpagerindicator.CirclePageIndicator + android:id="@+id/indicator" + android:padding="10dip" + android:layout_height="wrap_content" + android:layout_width="match_parent" + /> + + <android.support.v4.view.ViewPager + android:layout_height="0dp" + android:layout_width="0dp" + android:visibility="gone" + android:id="@+id/fake_pager" + /> + + </LinearLayout> +</com.cyngn.audiofx.eq.EqSwipeController> diff --git a/res/layout/fragment_audiofx.xml b/res/layout/fragment_audiofx.xml new file mode 100644 index 0000000..c4bb367 --- /dev/null +++ b/res/layout/fragment_audiofx.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.cyngn.audiofx.widget.InterceptableLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/interceptable_layout" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- equalizer layout --> + <FrameLayout class="com.cyngn.audiofx.fragment.EqualizerFragment" + android:id="@+id/equalizer" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="0dp"/> + + <!-- knobs --> + <FrameLayout class="com.cyngn.audiofx.fragment.ControlsFragment" + android:id="@+id/controls" + android:layout_weight="0" + android:layout_width="match_parent" + android:layout_height="160dp"/> + +</com.cyngn.audiofx.widget.InterceptableLinearLayout> diff --git a/res/layout/fragment_audiofx_maxxaudio.xml b/res/layout/fragment_audiofx_maxxaudio.xml new file mode 100644 index 0000000..3f5a022 --- /dev/null +++ b/res/layout/fragment_audiofx_maxxaudio.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<com.cyngn.audiofx.widget.InterceptableLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:custom="http://schemas.android.com/apk/res/com.cyngn.audiofx" + android:id="@+id/interceptable_layout" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <!-- equalizer layout --> + <FrameLayout class="com.cyngn.audiofx.fragment.EqualizerFragment" + android:id="@+id/equalizer" + android:layout_weight="1" + android:layout_width="match_parent" + android:layout_height="0dp"/> + + <!-- knobs --> + <FrameLayout class="com.cyngn.audiofx.fragment.ControlsFragment" + android:id="@+id/controls" + android:layout_weight="0" + android:layout_width="match_parent" + android:layout_height="200dp"/> + +</com.cyngn.audiofx.widget.InterceptableLinearLayout> diff --git a/res/layout/fragment_dts.xml b/res/layout/fragment_dts.xml new file mode 100644 index 0000000..785b996 --- /dev/null +++ b/res/layout/fragment_dts.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:id="@+id/logo" + android:layout_width="120dp" + android:layout_height="48dp" + android:layout_centerInParent="true" + android:src="@drawable/logo_dts_1c" + /> + +</RelativeLayout> diff --git a/res/layout/generic_knob_control.xml b/res/layout/generic_knob_control.xml new file mode 100644 index 0000000..6d9f531 --- /dev/null +++ b/res/layout/generic_knob_control.xml @@ -0,0 +1,20 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + android:layout_weight="0" + android:paddingBottom="@dimen/knob_container_padding_bottom"> + + <com.cyngn.audiofx.knobs.RadialKnob + android:id="@+id/knob" + android:layout_gravity="top|center_horizontal" + android:layout_width="@dimen/knob_width" + android:layout_height="wrap_content"/> + + <TextView + android:id="@+id/label" + android:layout_height="22dp" + android:layout_gravity="bottom|center_horizontal" + android:layout_width="wrap_content"/> + +</FrameLayout> diff --git a/res/layout/knob.xml b/res/layout/knob.xml deleted file mode 100644 index 57030e7..0000000 --- a/res/layout/knob.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright (c) 2013, The Linux Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - tools:context=".ActivityMusic"> - - <ImageView - android:id="@+id/knob_foreground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <ImageView - android:id="@+id/knob_toggle_on" - android:layout_gravity="center_horizontal|center_vertical" - android:layout_width="21px" - android:layout_height="21px" - android:src="@drawable/knob_simple" - android:visibility="gone" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_gravity="center_horizontal" - android:orientation="vertical" - android:gravity="center_horizontal"> - - <TextView - android:id="@+id/knob_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="gone" /> - <TextView - android:id="@+id/knob_label" - android:layout_width="0dp" - android:layout_height="0dp" - android:textAlignment="center" - android:ellipsize="marquee" - android:visibility="gone" /> - </LinearLayout> - -</FrameLayout> diff --git a/res/layout/music_main.xml b/res/layout/music_main.xml deleted file mode 100644 index b797c55..0000000 --- a/res/layout/music_main.xml +++ /dev/null @@ -1,174 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!-- Copyright (C) 2010-2011 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:custom="http://schemas.android.com/apk/res/org.cyanogenmod.audiofx" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <TextView - android:id="@+id/noEffectsTextView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:layout_gravity="center" - android:gravity="center" - android:text="@string/no_effects" - android:layout_weight="1" - android:visibility="gone"/> - - <org.cyanogenmod.audiofx.widget.InterceptableLinearLayout - android:id="@+id/contentSoundEffects" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_weight="1" - android:layout_height="0dp"> - - <include - layout="@layout/equalizer" - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="2"/> - - <View - android:layout_width="match_parent" - android:layout_height="20dp" - android:background="@color/audiofx_background_color" - /> - - <TextView - android:id="@+id/eq_preset_label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textColor="@color/eq_preset_label_text_color" - android:layout_gravity="center" - android:gravity="center" - android:textSize="10sp" - android:background="@color/audiofx_background_color" - android:text="@string/eq_preset" - android:textAllCaps="true"/> - - <org.cyanogenmod.audiofx.widget.Gallery - android:id="@+id/eqPresets" - android:layout_width="match_parent" - android:layout_height="48dip" - android:background="@color/audiofx_background_color" - android:gravity="center_vertical"/> - - - <View - android:layout_width="match_parent" - android:layout_height="20dp" - /> - - <!--<FrameLayout--> - <!--android:layout_width="match_parent"--> - <!--android:layout_height="0dip"--> - <!--android:layout_weight="23">--> - - <!--<com.pheelicks.visualizer.VisualizerView--> - <!--android:id="@+id/visualizerView"--> - <!--android:layout_width="match_parent"--> - <!--android:layout_height="match_parent">--> - <!--</com.pheelicks.visualizer.VisualizerView>--> - - <!--<LinearLayout--> - <!--android:layout_width="match_parent"--> - <!--android:layout_height="0dp"--> - <!--android:orientation="vertical"--> - <!--android:layout_weight="1">--> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center" - android:orientation="horizontal"> - - <Space - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" - /> - - <org.cyanogenmod.audiofx.widget.Knob - android:id="@+id/bBStrengthKnob" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - custom:label="@string/bass_boost_strength" - custom:foreground="@drawable/knob_large"/> - - <Space - android:layout_weight="2" - android:layout_width="0dp" - android:layout_height="match_parent" - /> - - <org.cyanogenmod.audiofx.widget.Knob - android:id="@+id/vIStrengthKnob" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="10dip" - custom:label="@string/virtualizer_strength" - custom:foreground="@drawable/knob_large"/> - - <Space - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" - /> - </LinearLayout> - - - <!--<View--> - <!--android:layout_width="match_parent"--> - <!--android:layout_height="1dp"--> - <!--android:background="@color/disabled"/>--> - - <View - android:layout_width="match_parent" - android:layout_height="20dp" - /> - - <TextView - android:id="@+id/reverb_label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textColor="@color/reverb_label_text_color" - android:layout_gravity="center" - android:gravity="center" - android:background="@color/audiofx_background_color" - android:paddingTop="8dp" - android:textSize="10sp" - android:text="@string/reverb" - android:textAllCaps="true"/> - - - <org.cyanogenmod.audiofx.widget.Gallery - android:id="@+id/reverb_gallery" - android:layout_width="match_parent" - android:layout_height="40dip" - android:background="@color/audiofx_background_color" - android:gravity="center_vertical"/> - - - <!--</FrameLayout>--> - - </org.cyanogenmod.audiofx.widget.InterceptableLinearLayout> -</LinearLayout> diff --git a/res/layout/preset_adapter_row.xml b/res/layout/preset_adapter_row.xml new file mode 100644 index 0000000..f7e6e86 --- /dev/null +++ b/res/layout/preset_adapter_row.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> + + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/text" + + android:layout_gravity="center" + android:fontFamily="sans-serif" + android:textStyle="bold" + android:textSize="@dimen/preset_text_size" + android:gravity="center" + android:layout_margin="@dimen/preset_text_padding" + android:textColor="@color/white" + android:textAllCaps="true" + android:ellipsize="end" + android:maxLines="1" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> diff --git a/res/menu/devices.xml b/res/menu/devices.xml new file mode 100644 index 0000000..ddcf02b --- /dev/null +++ b/res/menu/devices.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:title="@string/devices" + android:id="@+id/devices" + android:icon="@drawable/ic_action_dsp_icons_speaker" + android:showAsAction="ifRoom"> + <menu> + <group android:id="@+id/device_group"> + </group> + </menu> + </item> +</menu> diff --git a/res/mipmap-hdpi/ic_launcher.png b/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..a22b1fc --- /dev/null +++ b/res/mipmap-hdpi/ic_launcher.png diff --git a/res/mipmap-mdpi/ic_launcher.png b/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..cd0f81e --- /dev/null +++ b/res/mipmap-mdpi/ic_launcher.png diff --git a/res/mipmap-xhdpi/ic_launcher.png b/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..0150eb5 --- /dev/null +++ b/res/mipmap-xhdpi/ic_launcher.png diff --git a/res/mipmap-xxhdpi/ic_launcher.png b/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..6cef165 --- /dev/null +++ b/res/mipmap-xxhdpi/ic_launcher.png diff --git a/res/mipmap-xxxhdpi/ic_launcher.png b/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..7e6e264 --- /dev/null +++ b/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/res/values-af/cm_strings.xml b/res/values-af/cm_strings.xml index ae00872..e3015bc 100644 --- a/res/values-af/cm_strings.xml +++ b/res/values-af/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normaal</string> <string name="classical">Klassiek</string> <string name="dance">Dans</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronies</string> + <string name="ci_extreme">Elektronies</string> <string name="small_speakers">Klein luidsprekers</string> - <string name="custom">Persoonlik</string> + <string name="multimedia">Multimedia</string> + <string name="user">Persoonlik</string> + <string name="user_n">Persoonlik <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Geen</string> <string name="smallroom">Klein kamer</string> <string name="mediumroom">Medium kamer</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effek nie beskikbaar vir Luidspreker modus.</string> <string name="device_headset">Oorfone</string> <string name="device_speaker">Luidspreker</string> - <string name="device_lineout">Line out</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Koordloos</string> <string name="reverb">Galm</string> <string name="eq_preset">Voorafbepaalde gelykmaker</string> + <string name="devices">Toestelle</string> + <string name="virtualizer">Virtualiseerder</string> + <string name="treble">Treble</string> + <string name="bass">Bas</string> + <string name="rename">Hernoem</string> + <string name="remove_custom_preset_warning_message">Is jy seker jy wil dit verwyder %1$s?</string> </resources> diff --git a/res/values-ar/cm_strings.xml b/res/values-ar/cm_strings.xml index 6bc6060..3191570 100644 --- a/res/values-ar/cm_strings.xml +++ b/res/values-ar/cm_strings.xml @@ -15,20 +15,22 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <string name="normal">عادي</string> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="normal">عاديّ</string> <string name="classical">كلاسيكي</string> <string name="dance">رقص</string> <string name="flat">معتدل</string> <string name="folk">شعبي</string> <string name="heavy_metal">المعادن الثقيلة</string> <string name="hip_hop">الهيب هوب</string> - <string name="jazz">الجاز</string> + <string name="jazz">موسيقى الجاز</string> <string name="pop">البوب</string> <string name="rock">روك</string> - <string name="electronic">إلكتروني</string> + <string name="ci_extreme">إلكتروني</string> <string name="small_speakers">سماعات صغيرة</string> - <string name="custom">مخصص</string> + <string name="multimedia">وسائط متعددة</string> + <string name="user">مخصص</string> + <string name="user_n">مخصص<xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">بلا</string> <string name="smallroom">غرفة صغيرة</string> <string name="mediumroom">غرفة متوسطة</string> @@ -36,14 +38,19 @@ <string name="mediumhall">قاعة متوسطة</string> <string name="largehall">قاعة كبيرة</string> <string name="plate">لوح</string> - <string name="power_on_prompt">اضغط على زر الطاقة في الجزء الأيمن العلوي.</string> - <string name="effect_unavalable_for_speaker">التأثير غير متاح لوضع السماعة.</string> + <string name="power_on_prompt">اضغط على زر التشغيل في أعلى اليمين.</string> + <string name="effect_unavalable_for_speaker">التأثير غير متاح لوضع السماعات.</string> <string name="device_headset">سماعة الرأس</string> <string name="device_speaker">سماعة</string> - <string name="device_lineout">توصيل</string> <string name="device_usb">USB</string> <string name="device_bluetooth">بلوتوث</string> <string name="device_wireless">لاسلكي</string> <string name="reverb">الصدى</string> <string name="eq_preset">التعيين المسبق للمعادل</string> + <string name="devices">أجهزة</string> + <string name="virtualizer">أداة الظاهرية</string> + <string name="treble">الطنين الثلاثي</string> + <string name="bass">الجهير</string> + <string name="rename">إعادة تسمية</string> + <string name="remove_custom_preset_warning_message">هل تريد بالتأكيد إزالة %1$s؟</string> </resources> diff --git a/res/values-as-rIN/cm_strings.xml b/res/values-as-rIN/cm_strings.xml index d90f79c..a7ce35c 100644 --- a/res/values-as-rIN/cm_strings.xml +++ b/res/values-as-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">সাধাৰণ</string> <string name="classical">ধ্ৰুপদী</string> <string name="dance">নৃত্য</string> @@ -26,9 +26,11 @@ <string name="jazz">জেজ</string> <string name="pop">পপ</string> <string name="rock">ৰক</string> - <string name="electronic">ইলেক্ট্ৰনিক</string> + <string name="ci_extreme">ইলেক্ট্ৰনিক</string> <string name="small_speakers">সৰু স্পিকাৰ</string> - <string name="custom">কাষ্টম</string> + <string name="multimedia">মাল্টিমিডিয়া</string> + <string name="user">কাষ্টম</string> + <string name="user_n">কাষ্টম<xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">হাল্লা</string> <string name="smallroom">সৰু কোঠা</string> <string name="mediumroom">মধ্যমীয়া কোঠা</string> @@ -45,4 +47,10 @@ <string name="device_wireless">বেতাঁৰ</string> <string name="reverb">ৰিভাৰ্ব</string> <string name="eq_preset">ইকুইলাইজাৰ প্ৰিছেট</string> + <string name="devices">ডিভাইচসমূহ</string> + <string name="virtualizer">ভিজুৱেলাইজাৰ</string> + <string name="treble">ট্ৰেবল</string> + <string name="bass">বাছ</string> + <string name="rename">পুনঃনামাকৰণ</string> + <string name="remove_custom_preset_warning_message">আপুনি %1$s আঁতৰাব বিচৰাটো নিশ্চিত নে?</string> </resources> diff --git a/res/values-ast-rES/cm_strings.xml b/res/values-ast-rES/cm_strings.xml index 9c73fcb..57a6b8f 100644 --- a/res/values-ast-rES/cm_strings.xml +++ b/res/values-ast-rES/cm_strings.xml @@ -26,9 +26,9 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> + <string name="ci_extreme">Electronic</string> <string name="small_speakers">Altavoces pequeños</string> - <string name="custom">Custom</string> + <string name="user">Custom</string> <string name="none">Nengún</string> <string name="smallroom">Habitación pequeña</string> <string name="mediumroom">Habitación media</string> @@ -36,14 +36,14 @@ <string name="mediumhall">Sala media</string> <string name="largehall">Sala grande</string> <string name="plate">Estudiu</string> + <string name="eq_custom">Pa personalizar, seleiciona \'Custom\'</string> <string name="power_on_prompt">Calca nel botón d\'apagar na parte superior a mandrecha.</string> <string name="effect_unavalable_for_speaker">L\'efeutu nun ta disponible nel mou altavoz.</string> <string name="device_headset">Auriculares</string> <string name="device_speaker">Altavoz</string> - <string name="device_lineout">Salida de llinia</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Wireless</string> - <string name="reverb">Reverberación</string> <string name="eq_preset">Ecualizador preestablecíu</string> + <string name="reverb">Reverberación</string> </resources> diff --git a/res/values-az-rAZ/cm_strings.xml b/res/values-az-rAZ/cm_strings.xml index f80054c..3afc8ad 100644 --- a/res/values-az-rAZ/cm_strings.xml +++ b/res/values-az-rAZ/cm_strings.xml @@ -19,16 +19,14 @@ <string name="normal">Normal</string> <string name="classical">Klassik</string> <string name="dance">Rәqs</string> - <string name="flat">Düz</string> <string name="folk">Xalq</string> <string name="heavy_metal">Ağır Metal</string> <string name="hip_hop">Hip Hop</string> <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rok</string> - <string name="electronic">Elektronik</string> + <string name="ci_extreme">Elektronik</string> <string name="small_speakers">Kiçik səsucaldanlar</string> - <string name="custom">Özəl</string> <string name="none">Yoxdur</string> <string name="smallroom">Balaca otaq</string> <string name="mediumroom">Orta otaq</string> @@ -42,7 +40,4 @@ <string name="device_speaker">Səsucaldan</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> - <string name="device_wireless">Simsiz</string> - <string name="reverb">Əks-səda</string> - <string name="eq_preset">Ekvalayzer hazır tənzimləmələri</string> </resources> diff --git a/res/values-be/cm_strings.xml b/res/values-be/cm_strings.xml index 3ddabc8..e7be23f 100644 --- a/res/values-be/cm_strings.xml +++ b/res/values-be/cm_strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!--Generated by crowdin.com--> <!-- - Copyright (C) 2014-2015 The CyanogenMod Project + Copyright (C) 2014 The CyanogenMod Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,34 +15,42 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <string name="normal">Звычайная</string> - <string name="classical">Класічная</string> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="normal">Звычайны</string> + <string name="classical">Класіка</string> <string name="dance">Танцы</string> - <string name="flat">Пляската</string> + <string name="flat">Flat</string> <string name="folk">Фолк</string> - <string name="heavy_metal">Хэві-метал</string> + <string name="heavy_metal">Heavy Metal</string> <string name="hip_hop">Хіп-хоп</string> <string name="jazz">Джаз</string> <string name="pop">Поп</string> <string name="rock">Рок</string> - <string name="electronic">Электронная музыка</string> + <string name="ci_extreme">Электронная музыка</string> <string name="small_speakers">Малыя дынамікі</string> - <string name="custom">Карыстальніцкія</string> + <string name="multimedia">Мультымедыя</string> + <string name="user">Уласныя налады</string> + <string name="user_n">Карыстацкія налады <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Няма</string> - <string name="smallroom">Маленькі пакой</string> + <string name="smallroom">Малы пакой</string> <string name="mediumroom">Сярэдні пакой</string> <string name="largeroom">Вялікі пакой</string> <string name="mediumhall">Сярэдняя зала</string> <string name="largehall">Вялікая зала</string> - <string name="plate">Пласцінка</string> - <string name="power_on_prompt">Націсніце вымыкач у правым верхнім куце.</string> - <string name="effect_unavalable_for_speaker">Эфект недаступны для дынамікаў.</string> - <string name="device_headset">Слухаўкі</string> + <string name="plate">Грамафон</string> + <string name="power_on_prompt">Націсніце выключальнік у правым верхнім куце.</string> + <string name="effect_unavalable_for_speaker">Эфект не даступны для дынамікаў.</string> + <string name="device_headset">Гарнітура</string> <string name="device_speaker">Дынамік</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> - <string name="device_wireless">Бесправодныя прылады</string> + <string name="device_wireless">Бесправадная прылада</string> <string name="reverb">Рэверберацыя</string> - <string name="eq_preset">Прадусталёўкі эквалайзера</string> + <string name="eq_preset">Нарыхтоўка эквалайзера</string> + <string name="devices">Прылады</string> + <string name="virtualizer">Віртуалізацыя</string> + <string name="treble">Высокія частоты</string> + <string name="bass">Бас</string> + <string name="rename">Пераназваць</string> + <string name="remove_custom_preset_warning_message">Вы сапраўды хочаце выдаліць %1$s?</string> </resources> diff --git a/res/values-bg/cm_strings.xml b/res/values-bg/cm_strings.xml index e458da6..eb81cd7 100644 --- a/res/values-bg/cm_strings.xml +++ b/res/values-bg/cm_strings.xml @@ -15,8 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <string name="normal">Нормална</string> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="normal">Нормално</string> <string name="classical">Класическа</string> <string name="dance">Танцувална</string> <string name="flat">Плосък</string> @@ -26,12 +26,14 @@ <string name="jazz">Джаз</string> <string name="pop">Поп</string> <string name="rock">Рок</string> - <string name="electronic">Електронна</string> + <string name="ci_extreme">Електронна</string> <string name="small_speakers">Малки говорители</string> - <string name="custom">По избор</string> + <string name="multimedia">Мултимедия</string> + <string name="user">По избор</string> + <string name="user_n">По избор <xliff:g id="preset_num">%1$d </xliff:g></string> <string name="none">Без</string> <string name="smallroom">Малка стая</string> - <string name="mediumroom">Среднa стая</string> + <string name="mediumroom">Средна стая</string> <string name="largeroom">Голяма стая</string> <string name="mediumhall">Средна зала</string> <string name="largehall">Голяма зала</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Ефекта не е достъпен за високоговорителя.</string> <string name="device_headset">Слушалки</string> <string name="device_speaker">Говорител</string> - <string name="device_lineout">Линеен изход</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Безжичен</string> <string name="reverb">Ехо</string> <string name="eq_preset">Настройки на еквалайзера</string> + <string name="devices">Устройства</string> + <string name="virtualizer">Виртуализатор</string> + <string name="treble">Троен</string> + <string name="bass">Бас</string> + <string name="rename">Преименуване</string> + <string name="remove_custom_preset_warning_message">Сигурни ли сте, че искате да премахнете %1$s?</string> </resources> diff --git a/res/values-bn-rBD/cm_strings.xml b/res/values-bn-rBD/cm_strings.xml index 0888203..4f3a763 100644 --- a/res/values-bn-rBD/cm_strings.xml +++ b/res/values-bn-rBD/cm_strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <!--Generated by crowdin.com--> <!-- - Copyright (C) 2014-2015 The CyanogenMod Project + Copyright (C) 2014 The CyanogenMod Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">স্বাভাবিক</string> <string name="classical">ধ্রুপদী</string> <string name="dance">নৃত্য</string> @@ -26,9 +26,9 @@ <string name="jazz">জ্যাজ</string> <string name="pop">পপ</string> <string name="rock">রক</string> - <string name="electronic">ইলেকট্রনিক</string> + <string name="ci_extreme">ইলেকট্রনিক</string> <string name="small_speakers">ছোট স্পিকার</string> - <string name="custom">পছন্দসই</string> + <string name="user">পছন্দসই</string> <string name="none">কোনটি না</string> <string name="smallroom">ছোট ঘর</string> <string name="mediumroom">মধ্যম ঘর</string> diff --git a/res/values-bn/cm_strings.xml b/res/values-bn/cm_strings.xml new file mode 100644 index 0000000..6e62161 --- /dev/null +++ b/res/values-bn/cm_strings.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Generated by crowdin.com--> +<!-- + Copyright (C) 2014 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="normal">স্বাভাবিক</string> + <string name="classical">ধ্রুপদী</string> + <string name="dance">নৃত্য</string> + <string name="flat">ফ্ল্যাট</string> + <string name="folk">লোকগীতি</string> + <string name="heavy_metal">হেভি মেটাল</string> + <string name="hip_hop">হিপ-হপ</string> + <string name="jazz">জ্যাজ</string> + <string name="pop">পপ</string> + <string name="rock">রক</string> + <string name="ci_extreme">ইলেকট্রনিক</string> + <string name="small_speakers">ছোট স্পিকার</string> + <string name="user">পছন্দসই</string> + <string name="none">কোনটি না</string> + <string name="smallroom">ছোট ঘর</string> + <string name="mediumroom">মধ্যম ঘর</string> + <string name="largeroom">বড় ঘর</string> + <string name="mediumhall">মধ্যম হল</string> + <string name="largehall">বড় হল</string> + <string name="plate">প্লেট</string> + <string name="eq_custom">পছন্দসই-বিন্যাস করতে, \'পছন্দসই\' নির্বাচন করুন</string> + <string name="power_on_prompt">উপরের-ডানে পাওয়ার বোতাম চাপুন।</string> + <string name="effect_unavalable_for_speaker">স্পীকারের জন্য প্রভাব উপলব্ধ নয়।</string> + <string name="device_headset">হেডসেট</string> + <string name="device_speaker">স্পীকার</string> + <string name="device_usb">ইউএসবি</string> + <string name="device_bluetooth">ব্লুটুথ</string> + <string name="device_wireless">ওয়্যারলেস</string> + <string name="eq_preset">ইকুয়ালাইজার বিন্যাস-পূর্বাবস্থা</string> + <string name="reverb">প্রতিধ্বনি</string> +</resources> diff --git a/res/values-ca/cm_strings.xml b/res/values-ca/cm_strings.xml index a9d614e..c87ca5d 100644 --- a/res/values-ca/cm_strings.xml +++ b/res/values-ca/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clàssica</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electrònica</string> + <string name="ci_extreme">Electrònica</string> <string name="small_speakers">Altaveus petits</string> - <string name="custom">Personalitzat</string> + <string name="multimedia">Multimèdia</string> + <string name="user">Personalitzat</string> + <string name="user_n">Personalitzat <xliff:g id="preset_num">%1$d </xliff:g></string> <string name="none">Cap</string> <string name="smallroom">Habitació petita</string> <string name="mediumroom">Habitació mitjana</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">L\'efecte no està disponible en el mode Altaveu.</string> <string name="device_headset">Auriculars</string> <string name="device_speaker">Altaveu</string> - <string name="device_lineout">Sortida de línia</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Sense fils</string> <string name="reverb">Reverberació</string> <string name="eq_preset">Presintonia de l\'equalitzador</string> + <string name="devices">Dispositius</string> + <string name="virtualizer">Virtualitzador</string> + <string name="treble">Aguts</string> + <string name="bass">Greus</string> + <string name="rename">Reanomena</string> + <string name="remove_custom_preset_warning_message">Estàs segur que vols esborrar %1$s?</string> </resources> diff --git a/res/values-cs/cm_strings.xml b/res/values-cs/cm_strings.xml index 957874a..b0e1e8c 100644 --- a/res/values-cs/cm_strings.xml +++ b/res/values-cs/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normalní</string> <string name="classical">Klasická hudba</string> <string name="dance">Taneční</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronická</string> + <string name="ci_extreme">Elektronická</string> <string name="small_speakers">Malé reproduktory</string> - <string name="custom">Vlastní</string> + <string name="multimedia">Multimédia</string> + <string name="user">Vlastní</string> + <string name="user_n">Vlastní <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Žádný</string> <string name="smallroom">Malá místnost</string> <string name="mediumroom">Střední místnost</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efekt není pro režim reproduktoru dostupný.</string> <string name="device_headset">Sluchátka</string> <string name="device_speaker">Reproduktor</string> - <string name="device_lineout">Linkový výstup</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Bezdrátové</string> <string name="reverb">Dozvuku</string> <string name="eq_preset">Předvolba ekvalizéru</string> + <string name="devices">Zařízení</string> + <string name="virtualizer">Virtualizér</string> + <string name="treble">Výšky</string> + <string name="bass">Basy</string> + <string name="rename">Přejmenovat</string> + <string name="remove_custom_preset_warning_message">Opravdu chcete odstranit %1$s?</string> </resources> diff --git a/res/values-da/cm_strings.xml b/res/values-da/cm_strings.xml index 3cc09e5..1b731a8 100644 --- a/res/values-da/cm_strings.xml +++ b/res/values-da/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klassisk</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronisk</string> + <string name="ci_extreme">Elektronisk</string> <string name="small_speakers">Små højttalere</string> - <string name="custom">Brugerdefineret</string> + <string name="multimedia">Multimedier</string> + <string name="user">Brugerdefineret</string> + <string name="user_n">Tilpasset <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Ingen</string> <string name="smallroom">Lille værelse</string> <string name="mediumroom">Mellemstort værelse</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effekt ikke tilgængelig i Højttalertilstand.</string> <string name="device_headset">Håndfri</string> <string name="device_speaker">Højttaler</string> - <string name="device_lineout">Linjeudgang</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Trådløs</string> <string name="reverb">Rumklang</string> <string name="eq_preset">Equalizer-forudindstilling</string> + <string name="devices">Enheder</string> + <string name="virtualizer">Virtualisator</string> + <string name="treble">Diskant</string> + <string name="bass">Bas</string> + <string name="rename">Omdøb</string> + <string name="remove_custom_preset_warning_message">Er du sikker på du vil fjerne %1$s?</string> </resources> diff --git a/res/values-de/cm_strings.xml b/res/values-de/cm_strings.xml index e515b2b..b8199e0 100644 --- a/res/values-de/cm_strings.xml +++ b/res/values-de/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klassik</string> <string name="dance">Dance</string> @@ -26,10 +26,12 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronisch</string> + <string name="ci_extreme">Elektronisch</string> <string name="small_speakers">Kleine Lautsprecher</string> - <string name="custom">Benutzerdefiniert</string> - <string name="none">Keiner</string> + <string name="multimedia">Multimedia</string> + <string name="user">Benutzerdefiniert</string> + <string name="user_n">Benutzerdefiniert <xliff:g id="preset_num">%1$d </xliff:g></string> + <string name="none">Keine</string> <string name="smallroom">Kleiner Raum</string> <string name="mediumroom">Mittlerer Raum</string> <string name="largeroom">Großer Raum</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effekt für Lautsprecher-Modus nicht verfügbar.</string> <string name="device_headset">Kopfhörer</string> <string name="device_speaker">Lautsprecher</string> - <string name="device_lineout">Line-Ausgang</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Drahtlos</string> <string name="reverb">Halleffekt</string> <string name="eq_preset">Equalizer-Voreinstellung</string> + <string name="devices">Geräte</string> + <string name="virtualizer">Virtualisierung</string> + <string name="treble">Höhen</string> + <string name="bass">Bass</string> + <string name="rename">Umbenennen</string> + <string name="remove_custom_preset_warning_message">Sind Sie sicher, dass Sie %1$s entfernen möchten?</string> </resources> diff --git a/res/values-el/cm_strings.xml b/res/values-el/cm_strings.xml index 26fa04d..0030f99 100644 --- a/res/values-el/cm_strings.xml +++ b/res/values-el/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Κανονικό</string> <string name="classical">Κλασική</string> <string name="dance">Χορευτική</string> @@ -26,9 +26,11 @@ <string name="jazz">Τζαζ</string> <string name="pop">Ποπ</string> <string name="rock">Ροκ</string> - <string name="electronic">Ηλεκτρονική</string> + <string name="ci_extreme">Ηλεκτρονική</string> <string name="small_speakers">Μικρά ηχεία</string> - <string name="custom">Προσαρμοσμένο</string> + <string name="multimedia">Πολυμέσα</string> + <string name="user">Προσαρμοσμένο</string> + <string name="user_n">Προσαρμοσμένο <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Κανένα</string> <string name="smallroom">Μικρό δωμάτιο</string> <string name="mediumroom">Μεσαίο δωμάτιο</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Το εφέ δεν είναι διαθέσιμο κατά την αναπαραγωγή από το ηχείο.</string> <string name="device_headset">Ακουστικά</string> <string name="device_speaker">Ήχειο</string> - <string name="device_lineout">Έξοδος</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Ασύρματα</string> <string name="reverb">Αντήχηση</string> <string name="eq_preset">Προκαθορισμένη ρύθμιση ισοσταθμιστή</string> + <string name="devices">Συσκευές</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Πρίμα</string> + <string name="bass">Μπάσα</string> + <string name="rename">Μετονομασία</string> + <string name="remove_custom_preset_warning_message">Είστε βέβαιοι ότι θέλετε να διαγράψετε το %1$s;</string> </resources> diff --git a/res/values-en-rAU/cm_strings.xml b/res/values-en-rAU/cm_strings.xml index 11ef81a..c7c1f08 100644 --- a/res/values-en-rAU/cm_strings.xml +++ b/res/values-en-rAU/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Classical</string> <string name="dance">Dance</string> @@ -26,9 +26,9 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> + <string name="ci_extreme">Electronic</string> <string name="small_speakers">Small speakers</string> - <string name="custom">Custom</string> + <string name="user">Custom</string> <string name="none">None</string> <string name="smallroom">Small room</string> <string name="mediumroom">Medium room</string> diff --git a/res/values-en-rIN/cm_strings.xml b/res/values-en-rIN/cm_strings.xml index 41abf06..43a3277 100644 --- a/res/values-en-rIN/cm_strings.xml +++ b/res/values-en-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Classical</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> + <string name="ci_extreme">Electronic</string> <string name="small_speakers">Small speakers</string> - <string name="custom">Custom</string> + <string name="multimedia">Multimedia</string> + <string name="user">Custom</string> + <string name="user_n">Custom <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">None</string> <string name="smallroom">Small room</string> <string name="mediumroom">Medium room</string> @@ -45,4 +47,10 @@ <string name="device_wireless">Wireless</string> <string name="reverb">Reverb</string> <string name="eq_preset">Equalizer preset</string> + <string name="devices">Devices</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Treble</string> + <string name="bass">Bass</string> + <string name="rename">Rename</string> + <string name="remove_custom_preset_warning_message">Are you sure you want to remove %1$s?</string> </resources> diff --git a/res/values-es-rUS/cm_strings.xml b/res/values-es-rUS/cm_strings.xml index 2540a88..befca2c 100644 --- a/res/values-es-rUS/cm_strings.xml +++ b/res/values-es-rUS/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clásica</string> <string name="dance">Dance</string> @@ -26,24 +26,31 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electrónica</string> + <string name="ci_extreme">Electrónica</string> <string name="small_speakers">Altavoces pequeños</string> - <string name="custom">Personalizado</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personalizado</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> personalizado</string> <string name="none">Ninguno</string> <string name="smallroom">Habitación pequeña</string> <string name="mediumroom">Habitación mediana</string> - <string name="largeroom">Habitación grande</string> + <string name="largeroom">Gran habitación</string> <string name="mediumhall">Sala mediana</string> - <string name="largehall">Sala grande</string> + <string name="largehall">Gran sala</string> <string name="plate">Vinilo</string> <string name="power_on_prompt">Pulsar el botón de encendido en la parte superior derecha.</string> <string name="effect_unavalable_for_speaker">Efecto no disponible para el modo altavoz.</string> <string name="device_headset">Auriculares</string> <string name="device_speaker">Altavoz</string> - <string name="device_lineout">Salida de línea</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Inalámbrico</string> <string name="reverb">Reverberación</string> <string name="eq_preset">Ecualizador preestablecido</string> + <string name="devices">Dispositivos</string> + <string name="virtualizer">Virtualizador</string> + <string name="treble">Agudos</string> + <string name="bass">Bajos</string> + <string name="rename">Cambiar nombre</string> + <string name="remove_custom_preset_warning_message">¿Estás seguro de que deseas quitar %1$s?</string> </resources> diff --git a/res/values-es-rXA/strings.xml b/res/values-es-rXA/strings.xml new file mode 100644 index 0000000..0c5dbe6 --- /dev/null +++ b/res/values-es-rXA/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Generated by crowdin.com--> +<!-- Copyright (C) 2010-2011 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="app_name">AudioFX</string> + <string name="no_effects">Efeutos non disponibles.</string> + <string name="main_toggle_effects_title">Efeutos d\'audiu</string> + <string name="eq_dialog_title">AudioFX</string> + <string name="headset_plug">Coneuta los auriculares pa estos efectos.</string> + <string name="bass_boost_strength">Bass boost</string> + <string name="virtualizer_strength">Soníu envolvente</string> + <string name="pr_title">Reverberación:</string> + <string name="pr_summary">Efeutos de reverberación de sala adicionales</string> + <string name="pr_dialog_title">Preaxuste de reverberación</string> + <string name="setup">Configuración</string> + <string name="picker_title">Panel de control d\'efeutos de música\"</string> +</resources> diff --git a/res/values-es/cm_strings.xml b/res/values-es/cm_strings.xml index d4926cc..2d2113b 100644 --- a/res/values-es/cm_strings.xml +++ b/res/values-es/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clásica</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electrónica</string> + <string name="ci_extreme">Electrónica</string> <string name="small_speakers">Altavoces pequeños</string> - <string name="custom">Personalizado</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personalizado</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> personalizado</string> <string name="none">Ninguno</string> <string name="smallroom">Habitación pequeña</string> <string name="mediumroom">Habitación mediana</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efecto no disponible para el modo altavoz.</string> <string name="device_headset">Auriculares</string> <string name="device_speaker">Altavoz</string> - <string name="device_lineout">Salida de línea</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Inalámbrico</string> <string name="reverb">Reverberación</string> <string name="eq_preset">Ecualizador preestablecido</string> + <string name="devices">Dispositivos</string> + <string name="virtualizer">Virtualizador</string> + <string name="treble">Agudos</string> + <string name="bass">Bajos</string> + <string name="rename">Cambiar el nombre</string> + <string name="remove_custom_preset_warning_message">¿Estás seguro que deseas eliminar %1$s?</string> </resources> diff --git a/res/values-et-rEE/cm_strings.xml b/res/values-et-rEE/cm_strings.xml index 6dd71f1..02e026f 100644 --- a/res/values-et-rEE/cm_strings.xml +++ b/res/values-et-rEE/cm_strings.xml @@ -26,9 +26,9 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rokk</string> - <string name="electronic">Elektrooniline</string> + <string name="ci_extreme">Elektrooniline</string> <string name="small_speakers">Väiksed kõlarid</string> - <string name="custom">Kohandatud</string> + <string name="user">Kohandatud</string> <string name="none">Puudub</string> <string name="smallroom">Väike tuba</string> <string name="mediumroom">Keskmine tuba</string> @@ -36,6 +36,7 @@ <string name="mediumhall">Keskmine hall</string> <string name="largehall">Suur hall</string> <string name="plate">Plate</string> + <string name="eq_custom">Kohandamiseks vali \'Kohandatud\'</string> <string name="power_on_prompt">Vajutage power nuppu üleval paremal.</string> <string name="effect_unavalable_for_speaker">Efekt puudub Kõlari-režiimis.</string> <string name="device_headset">Peakomplekt</string> @@ -43,6 +44,6 @@ <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Juhtmeta</string> - <string name="reverb">Peegeldus</string> <string name="eq_preset">Ekvalaiseri eelsäte</string> + <string name="reverb">Peegeldus</string> </resources> diff --git a/res/values-fa/cm_strings.xml b/res/values-fa/cm_strings.xml index 4991163..074d513 100644 --- a/res/values-fa/cm_strings.xml +++ b/res/values-fa/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">عادی</string> <string name="classical">کلاسیک</string> <string name="dance">رقص</string> @@ -26,9 +26,11 @@ <string name="jazz">جاز</string> <string name="pop">پاپ</string> <string name="rock">راک</string> - <string name="electronic">الکترونیک</string> + <string name="ci_extreme">الکترونیک</string> <string name="small_speakers">بلندگوهای کوچک</string> - <string name="custom">سفارشی</string> + <string name="multimedia">چند رسانهای</string> + <string name="user">سفارشی</string> + <string name="user_n">سفارشی <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">هیچکدام</string> <string name="smallroom">اتاق کوچک</string> <string name="mediumroom">اتاق متوسط</string> @@ -45,4 +47,10 @@ <string name="device_wireless">بیسیم</string> <string name="reverb">طنین</string> <string name="eq_preset">اکولایزر تنظیم شده</string> + <string name="devices">دستگاهها</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">صدای زیر</string> + <string name="bass">صدای بم</string> + <string name="rename">تغییرنام</string> + <string name="remove_custom_preset_warning_message">از حذف %1$s اطمینان دارید؟</string> </resources> diff --git a/res/values-fi/cm_strings.xml b/res/values-fi/cm_strings.xml index 4bac476..79e2a9a 100644 --- a/res/values-fi/cm_strings.xml +++ b/res/values-fi/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normaali</string> <string name="classical">Klassinen</string> <string name="dance">Tanssi</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektroninen</string> + <string name="ci_extreme">Elektroninen</string> <string name="small_speakers">Pienet kaiuttimet</string> - <string name="custom">Muokattu</string> + <string name="multimedia">Multimedia</string> + <string name="user">Muokattu</string> + <string name="user_n">Mukautettu <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Ei mitään</string> <string name="smallroom">Pieni huone</string> <string name="mediumroom">Keskikokoinen huone</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efekti ei käytettävissä kaiutintilassa.</string> <string name="device_headset">Kuulokkeet</string> <string name="device_speaker">Kaiutin</string> - <string name="device_lineout">Ulostulo</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Langaton</string> <string name="reverb">Kaikuefekti</string> <string name="eq_preset">Taajuuskorjaimen esiasetus</string> + <string name="devices">Laitteet</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Diskantti</string> + <string name="bass">Basso</string> + <string name="rename">Nimeä uudelleen</string> + <string name="remove_custom_preset_warning_message">Haluatko varmasti poistaa asetusen %1$s?</string> </resources> diff --git a/res/values-fr-rCA/cm_strings.xml b/res/values-fr-rCA/cm_strings.xml new file mode 100644 index 0000000..d1a2578 --- /dev/null +++ b/res/values-fr-rCA/cm_strings.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Generated by crowdin.com--> +<!-- + Copyright (C) 2014 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="normal">Normal</string> + <string name="classical">Classique</string> + <string name="dance">Dance</string> + <string name="flat">Plat</string> + <string name="folk">Folk</string> + <string name="heavy_metal">Heavy Metal</string> + <string name="hip_hop">Hip Hop</string> + <string name="jazz">Jazz</string> + <string name="pop">Pop</string> + <string name="rock">Rock</string> + <string name="ci_extreme">Électronique</string> + <string name="small_speakers">Petits haut-parleurs</string> + <string name="user">Personnalisé</string> + <string name="none">Aucun</string> + <string name="smallroom">Petite pièce</string> + <string name="mediumroom">Pièce moyenne</string> + <string name="largeroom">Grande pièce</string> + <string name="mediumhall">Salle moyenne</string> + <string name="largehall">Grande salle</string> + <string name="plate">Plat</string> + <string name="power_on_prompt">Appuyez sur le bouton d\'activation en haut à droite.</string> + <string name="effect_unavalable_for_speaker">Effet non disponible pour le mode haut-parleur.</string> + <string name="device_headset">Casque d\'écoute</string> + <string name="device_speaker">Haut-parleur</string> + <string name="device_usb">USB</string> + <string name="device_bluetooth">Périphérique Bluetooth</string> + <string name="device_wireless">Sans fil</string> + <string name="reverb">Réverbération</string> + <string name="eq_preset">Égaliseur prédéfini</string> +</resources> diff --git a/res/values-fr/cm_strings.xml b/res/values-fr/cm_strings.xml index 8896d7e..13394c9 100644 --- a/res/values-fr/cm_strings.xml +++ b/res/values-fr/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Classique</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Électronique</string> + <string name="ci_extreme">Électronique</string> <string name="small_speakers">Petits haut-parleurs</string> - <string name="custom">Personnalisé</string> + <string name="multimedia">Multimédia</string> + <string name="user">Personnalisé</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> personnalisé</string> <string name="none">Aucun</string> <string name="smallroom">Petite salle</string> <string name="mediumroom">Salle moyenne</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effet non disponible pour le mode haut-parleur.</string> <string name="device_headset">Casque</string> <string name="device_speaker">Haut-parleur</string> - <string name="device_lineout">Sortie ligne</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Sans fil</string> <string name="reverb">Réverbération</string> <string name="eq_preset">Égaliseur prédéfini</string> + <string name="devices">Appareils</string> + <string name="virtualizer">Virtualiseur</string> + <string name="treble">Aiguës</string> + <string name="bass">Basses</string> + <string name="rename">Renommer</string> + <string name="remove_custom_preset_warning_message">Êtes-vous sûr de vouloir supprimer %1$s ?</string> </resources> diff --git a/res/values-fy-rNL/cm_strings.xml b/res/values-fy-rNL/cm_strings.xml new file mode 100644 index 0000000..ca92ab6 --- /dev/null +++ b/res/values-fy-rNL/cm_strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Generated by crowdin.com--> +<!-- + Copyright (C) 2014 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="none">Gjin</string> +</resources> diff --git a/res/values-gl-rES/cm_strings.xml b/res/values-gl-rES/cm_strings.xml index 582d739..2c9ea9c 100644 --- a/res/values-gl-rES/cm_strings.xml +++ b/res/values-gl-rES/cm_strings.xml @@ -15,34 +15,30 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clásica</string> <string name="dance">Dance</string> <string name="flat">Plano</string> - <string name="folk">Folk</string> <string name="heavy_metal">Heavy Metal</string> <string name="hip_hop">Hip Hop</string> <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electrónica</string> - <string name="small_speakers">Altofalantes pequenos</string> - <string name="custom">Personalizado</string> + <string name="ci_extreme">Electrónica</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personalizado</string> <string name="none">Ningunha</string> <string name="smallroom">Cuarto pequeno</string> <string name="mediumroom">Cuarto mediano</string> <string name="largeroom">Cuarto grande</string> <string name="mediumhall">Sala mediana</string> <string name="largehall">Sala grande</string> - <string name="plate">Disco</string> - <string name="power_on_prompt">Prema o botón de acendido na esquina dereita.</string> - <string name="effect_unavalable_for_speaker">O efecto non está dispoñíbel no modo altavoz.</string> - <string name="device_headset">Auriculares</string> <string name="device_speaker">Altofalante</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Sen fíos</string> - <string name="reverb">Reverberar</string> - <string name="eq_preset">Ecualizador predefinido</string> + <string name="devices">Dispositivos</string> + <string name="treble">Agudos</string> + <string name="bass">Baixos</string> </resources> diff --git a/res/values-gl/cm_strings.xml b/res/values-gl/cm_strings.xml new file mode 100644 index 0000000..161f0ff --- /dev/null +++ b/res/values-gl/cm_strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Generated by crowdin.com--> +<!-- + Copyright (C) 2014 The CyanogenMod Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="normal">Normal</string> + <string name="classical">Clásica</string> + <string name="dance">Dance</string> + <string name="flat">Plano</string> + <string name="jazz">Jazz</string> + <string name="rock">Rock</string> + <string name="ci_extreme">Electrónica</string> + <string name="user">Personalizado</string> + <string name="none">Ningunha</string> + <string name="smallroom">Cuarto pequeno</string> + <string name="mediumroom">Cuarto mediano</string> + <string name="largeroom">Cuarto grande</string> + <string name="mediumhall">Sala mediana</string> + <string name="largehall">Sala grande</string> + <string name="eq_custom">Para personalizar, seleccione «Personalizado»</string> + <string name="device_speaker">Altofalante</string> + <string name="device_usb">USB</string> + <string name="device_bluetooth">Bluetooth</string> +</resources> diff --git a/res/values-gu-rIN/cm_strings.xml b/res/values-gu-rIN/cm_strings.xml index cad9bf9..30aa7f3 100644 --- a/res/values-gu-rIN/cm_strings.xml +++ b/res/values-gu-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">સામાન્ય</string> <string name="classical">શાસ્ત્રીય</string> <string name="dance">નૃત્ય</string> @@ -26,9 +26,11 @@ <string name="jazz">જેઝ</string> <string name="pop">પોપ</string> <string name="rock">ઝુમવુ</string> - <string name="electronic">ઇલેક્ટ્રોનિક</string> + <string name="ci_extreme">ઇલેક્ટ્રોનિક</string> <string name="small_speakers">નાના સ્પીકરો</string> - <string name="custom">કસ્ટમ</string> + <string name="multimedia">મલ્ટિમિડિયા</string> + <string name="user">કસ્ટમ</string> + <string name="user_n">કસ્ટમ <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">કોઈ નહીં</string> <string name="smallroom">નાનો ઓરડો</string> <string name="mediumroom">મધ્યમ ઓરડો</string> @@ -44,5 +46,11 @@ <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">વાયરલેસ</string> <string name="reverb">રિવર્બ</string> - <string name="eq_preset">ઇક્વિલાઇઝર પ્રિસેટ</string> + <string name="eq_preset">ઈક્વલાઇઝર પ્રિસેટ</string> + <string name="devices">ઉપકરણો</string> + <string name="virtualizer">વર્ચુઅલાઇઝર</string> + <string name="treble">ટ્રેબલ</string> + <string name="bass">બાસ</string> + <string name="rename">નામ બદલો</string> + <string name="remove_custom_preset_warning_message">શું તમને ખાતરી છે કે તમારે %1$s દૂર કરવું છે?</string> </resources> diff --git a/res/layout/music_eq.xml b/res/values-hdpi/knobs.xml index a7a5f36..216f472 100644 --- a/res/layout/music_eq.xml +++ b/res/values-hdpi/knobs.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8" ?> +<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2010-2011 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,12 +14,7 @@ limitations under the License. --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/eqcontainer" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" - android:gravity="center_horizontal"> - -</LinearLayout> +<resources> + <dimen name="radial_rect_padding">8dp</dimen> + <dimen name="radial_knob_stroke">15dp</dimen> +</resources> diff --git a/res/values-hr/cm_strings.xml b/res/values-hr/cm_strings.xml index c512b7f..320f216 100644 --- a/res/values-hr/cm_strings.xml +++ b/res/values-hr/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normalno</string> <string name="classical">Klasika</string> <string name="dance">Ples</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronska</string> + <string name="ci_extreme">Elektronska</string> <string name="small_speakers">Mali zvučnici</string> - <string name="custom">Prilagođeno</string> + <string name="multimedia">Multimedija</string> + <string name="user">Prilagođeno</string> + <string name="user_n">Prilagođeni <xliff:g id="preset_num">%1$d </xliff:g></string> <string name="none">Ništa</string> <string name="smallroom">Mala soba</string> <string name="mediumroom">Srednja soba</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efekt nije dostupan za način Zvučnika.</string> <string name="device_headset">Slušalice</string> <string name="device_speaker">Zvučnik</string> - <string name="device_lineout">Izlaz za slušalice</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Bežično</string> <string name="reverb">Odjek</string> <string name="eq_preset">Predlošci ekvilajzera</string> + <string name="devices">Uređaji</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Visoki tonovi</string> + <string name="bass">Bas gitara</string> + <string name="rename">Preimenuj</string> + <string name="remove_custom_preset_warning_message">Jeste li sigurni da želite ukloniti %1$s?</string> </resources> diff --git a/res/values-hu/cm_strings.xml b/res/values-hu/cm_strings.xml index 35b5fb3..f480aaa 100644 --- a/res/values-hu/cm_strings.xml +++ b/res/values-hu/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normál</string> <string name="classical">Klasszikus</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronikus</string> + <string name="ci_extreme">Elektronikus</string> <string name="small_speakers">Kis hangszórók</string> - <string name="custom">Egyéni</string> + <string name="multimedia">Multimédia</string> + <string name="user">Egyéni</string> + <string name="user_n">Egyéni <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Nincs</string> <string name="smallroom">Kis szoba</string> <string name="mediumroom">Közepes szoba</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Hangszórót használva az effekt nem elérhető.</string> <string name="device_headset">Fejhallgató</string> <string name="device_speaker">Hangszóró</string> - <string name="device_lineout">Vonal kimenet</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Vezeték nélküli</string> <string name="reverb">Zengetés</string> <string name="eq_preset">Hangszín minták</string> + <string name="devices">Eszközök</string> + <string name="virtualizer">Virtualizáló</string> + <string name="treble">Magas hang</string> + <string name="bass">Mélyhang</string> + <string name="rename">Átnevezés</string> + <string name="remove_custom_preset_warning_message">Biztosan el szeretné távolítani: %1$s?</string> </resources> diff --git a/res/values-in/cm_strings.xml b/res/values-in/cm_strings.xml index e671d90..1c7c5dc 100644 --- a/res/values-in/cm_strings.xml +++ b/res/values-in/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klasik</string> <string name="dance">Dansa</string> @@ -26,24 +26,31 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronik</string> + <string name="ci_extreme">Elektronik</string> <string name="small_speakers">Speaker kecil</string> - <string name="custom">Khusus</string> + <string name="multimedia">Multimedia</string> + <string name="user">Khusus</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> khusus</string> <string name="none">Tidak Ada</string> <string name="smallroom">Ruang kecil</string> <string name="mediumroom">Ruang sedang</string> <string name="largeroom">Ruang besar</string> <string name="mediumhall">Aula sedang</string> <string name="largehall">Aula besar</string> - <string name="plate">Pelat</string> + <string name="plate">Plate</string> <string name="power_on_prompt">Tekan tombol power pada bagian kanan atas.</string> <string name="effect_unavalable_for_speaker">Efek tidak tersedia untuk modus Speaker.</string> <string name="device_headset">Set kepala</string> <string name="device_speaker">Speaker</string> - <string name="device_lineout">Sambungan keluar</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Nirkabel</string> <string name="reverb">Reverb</string> <string name="eq_preset">Preset equalizer</string> + <string name="devices">Perangkat</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Treble</string> + <string name="bass">Bass</string> + <string name="rename">Ubah Nama</string> + <string name="remove_custom_preset_warning_message">Apakah Anda yakin Anda ingin menghapus %1$s?</string> </resources> diff --git a/res/values-it/cm_strings.xml b/res/values-it/cm_strings.xml index 2bbae8d..30aee98 100644 --- a/res/values-it/cm_strings.xml +++ b/res/values-it/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normale</string> <string name="classical">Classica</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elettronica</string> + <string name="ci_extreme">Elettronica</string> <string name="small_speakers">Altoparlanti piccoli</string> - <string name="custom">Personalizzato</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personalizzato</string> + <string name="user_n">Personalizzato <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Nessuno</string> <string name="smallroom">Stanza piccola</string> <string name="mediumroom">Stanza media</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effetto non disponibile per la modalità vivavoce.</string> <string name="device_headset">Cuffie</string> <string name="device_speaker">Altoparlante</string> - <string name="device_lineout">Line out</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> - <string name="device_wireless">Wireless</string> + <string name="device_wireless">Senza fili</string> <string name="reverb">Riverbero</string> <string name="eq_preset">Equalizzatore predefinito</string> + <string name="devices">Dispositivi</string> + <string name="virtualizer">Virtualizzatore</string> + <string name="treble">Alti</string> + <string name="bass">Bassi</string> + <string name="rename">Rinomina</string> + <string name="remove_custom_preset_warning_message">Sei sicuro di voler rimuovere %1$s?</string> </resources> diff --git a/res/values-iw/cm_strings.xml b/res/values-iw/cm_strings.xml index 4686d1e..5d9278e 100644 --- a/res/values-iw/cm_strings.xml +++ b/res/values-iw/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">רגיל</string> <string name="classical">קלאסי</string> <string name="dance">דאנס</string> @@ -26,9 +26,11 @@ <string name="jazz">ג\'אז</string> <string name="pop">פופ</string> <string name="rock">רוק</string> - <string name="electronic">אלקטרוני</string> + <string name="ci_extreme">אלקטרוני</string> <string name="small_speakers">רמקולים קטנים</string> - <string name="custom">מותאם אישית</string> + <string name="multimedia">מולטימדיה</string> + <string name="user">מותאם אישית</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> מותאם אישית</string> <string name="none">ללא</string> <string name="smallroom">חדר קטן</string> <string name="mediumroom">חדר בינוני</string> @@ -38,12 +40,17 @@ <string name="plate">צלחת</string> <string name="power_on_prompt">לחץ על לחצן ההפעלה למעלה מימין.</string> <string name="effect_unavalable_for_speaker">האפקט אינו זמין במצב רמקול.</string> - <string name="device_headset">אזניות</string> + <string name="device_headset">אוזניה</string> <string name="device_speaker">רמקול</string> - <string name="device_lineout">יציאת שמע</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">אלחוטי</string> <string name="reverb">תהודה</string> <string name="eq_preset">אקולייזר קבוע מראש</string> + <string name="devices">מכשירים</string> + <string name="virtualizer">מדמה וירטואלי</string> + <string name="treble">טרבל</string> + <string name="bass">בס</string> + <string name="rename">שנה שם</string> + <string name="remove_custom_preset_warning_message">בטוח שברצונך להסיר את %1$s?</string> </resources> diff --git a/res/values-ja/cm_strings.xml b/res/values-ja/cm_strings.xml index 96ad24b..730080a 100644 --- a/res/values-ja/cm_strings.xml +++ b/res/values-ja/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Classical</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> - <string name="small_speakers">Small speakers</string> - <string name="custom">カスタム</string> + <string name="ci_extreme">Electronic</string> + <string name="small_speakers">小さいスピーカー</string> + <string name="multimedia">マルチメディア</string> + <string name="user">カスタム</string> + <string name="user_n">カスタム<xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">なし</string> <string name="smallroom">小さい部屋</string> <string name="mediumroom">中くらいの部屋</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">スピーカーモードでは効果は利用できません。</string> <string name="device_headset">ヘッドセット</string> <string name="device_speaker">スピーカー</string> - <string name="device_lineout">ライン出力</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">ワイヤレス</string> <string name="reverb">リバーブ</string> <string name="eq_preset">イコライザープリセット</string> + <string name="devices">端末</string> + <string name="virtualizer">バーチャライザ</string> + <string name="treble">Treble</string> + <string name="bass">Bass</string> + <string name="rename">名前の変更</string> + <string name="remove_custom_preset_warning_message">%1$sを削除してもよろしいですか?</string> </resources> diff --git a/res/values-kn-rIN/cm_strings.xml b/res/values-kn-rIN/cm_strings.xml index 4370b11..1f5fb22 100644 --- a/res/values-kn-rIN/cm_strings.xml +++ b/res/values-kn-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">ಸಹಜ</string> <string name="classical">ಶಾಸ್ತ್ರೀಯ</string> <string name="dance">ನೃತ್ಯ</string> @@ -26,9 +26,11 @@ <string name="jazz">ಜಾಝ್</string> <string name="pop">ಪಾಪ್</string> <string name="rock">ರಾಕ್</string> - <string name="electronic">ಎಲೆಕ್ಟ್ರಾನಿಕ್</string> + <string name="ci_extreme">ಎಲೆಕ್ಟ್ರಾನಿಕ್</string> <string name="small_speakers">ಚಿಕ್ಕ ಸ್ಪೀಕರ್ಗಳು</string> - <string name="custom">ಕಸ್ಟಮ್</string> + <string name="multimedia">ಬಹುಮಾಧ್ಯಮ</string> + <string name="user">ಕಸ್ಟಮ್</string> + <string name="user_n">ಕಸ್ಟಮ್ <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">ಶೂನ್ಯ</string> <string name="smallroom">ಸಣ್ಣ ಕೋಣೆ</string> <string name="mediumroom">ಮಧ್ಯಮ ಕೊಣೆ</string> @@ -37,7 +39,7 @@ <string name="largehall">ದೊಡ್ಡ ಹಾಲ್</string> <string name="plate">ಪ್ಲೇಟ್</string> <string name="power_on_prompt">ಬಲ-ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಪವರ್ ಬಟನ್ ಒತ್ತಿ.</string> - <string name="effect_unavalable_for_speaker">ಸ್ಪೀಕರ್ ಮೋಡ್ ಪರಿಣಾಮಗಳು ಲಭ್ಯವಿಲ್ಲ.</string> + <string name="effect_unavalable_for_speaker">ಪರಿಣಾಮಗಳು ಸ್ಪೀಕರ್ ಮೋಡ್ಗೆ ಲಭ್ಯವಿಲ್ಲ.</string> <string name="device_headset">ಹೆಡ್ಸೆಟ್</string> <string name="device_speaker">ಸ್ಪೀಕರ್</string> <string name="device_usb">ಯುಎಸ್ಬಿ</string> @@ -45,4 +47,10 @@ <string name="device_wireless">ನಿಸ್ತಂತು</string> <string name="reverb">ಪ್ರತಿಫಲನ</string> <string name="eq_preset">ಸಮಕಾರಕ ಪೂರ್ವನಿಗದಿ</string> + <string name="devices">ಸಾಧನಗಳು</string> + <string name="virtualizer">ವಾಸ್ತವೀಕರಣಕಾರಕ</string> + <string name="treble">ಟ್ರೆಬಲ್</string> + <string name="bass">ಬಾಸ್</string> + <string name="rename">ಮರುಹೆಸರಿಸಿ</string> + <string name="remove_custom_preset_warning_message">ನೀವು ಖಚಿತವಾಗಿ %1$s ತೆಗೆಯಲು ಬಯಸುವಿರಾ?</string> </resources> diff --git a/res/values-ko/cm_strings.xml b/res/values-ko/cm_strings.xml index 27144cb..d480a53 100644 --- a/res/values-ko/cm_strings.xml +++ b/res/values-ko/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">일반</string> <string name="classical">클래식</string> <string name="dance">댄스</string> @@ -26,9 +26,11 @@ <string name="jazz">재즈</string> <string name="pop">팝</string> <string name="rock">락</string> - <string name="electronic">일렉트로닉</string> + <string name="ci_extreme">일렉트로닉</string> <string name="small_speakers">작은 스피커</string> - <string name="custom">사용자 정의</string> + <string name="multimedia">멀티미디어</string> + <string name="user">사용자 정의</string> + <string name="user_n">사용자 지정 <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">없음</string> <string name="smallroom">작은 방</string> <string name="mediumroom">중간 방</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">스피커로는 이 음향효과를 사용할 수 없습니다.</string> <string name="device_headset">헤드셋</string> <string name="device_speaker">스피커</string> - <string name="device_lineout">출력</string> <string name="device_usb">USB</string> <string name="device_bluetooth">블루투스</string> <string name="device_wireless">무선</string> <string name="reverb">리버브</string> <string name="eq_preset">이퀄라이저 프리셋</string> + <string name="devices">기기</string> + <string name="virtualizer">버추얼라이저</string> + <string name="treble">고음</string> + <string name="bass">베이스</string> + <string name="rename">이름 바꾸기</string> + <string name="remove_custom_preset_warning_message">%1$s을(를) 삭제하시겠습니까?</string> </resources> diff --git a/res/values-ku/cm_strings.xml b/res/values-ku/cm_strings.xml index 0cbb16a..cc3ec9f 100644 --- a/res/values-ku/cm_strings.xml +++ b/res/values-ku/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">ئاسایی</string> <string name="classical">كلاسیكی</string> <string name="dance">سهما</string> @@ -26,9 +26,9 @@ <string name="jazz">جاز</string> <string name="pop">پۆپ</string> <string name="rock">ڕۆک</string> - <string name="electronic">ئەلیکترۆنیك</string> + <string name="ci_extreme">ئەلیکترۆنیك</string> <string name="small_speakers">دەنگدەری بچووک</string> - <string name="custom">داواکراو</string> + <string name="user">داواکراو</string> <string name="none">هیچ</string> <string name="smallroom">ژوری بچوک</string> <string name="mediumroom">ژوری ئاسایی</string> diff --git a/res/values-ku/strings.xml b/res/values-ku/strings.xml index 22cee23..be57ad1 100644 --- a/res/values-ku/strings.xml +++ b/res/values-ku/strings.xml @@ -15,12 +15,16 @@ limitations under the License. --> <resources> - <!-- ControlPanelMusic strings --> + <string name="app_name">دەنگڕێخەر</string> <string name="no_effects">کاریگەر لەکارەنیە.</string> + <string name="main_toggle_effects_title">کاریگهری دهنگ</string> + <string name="eq_dialog_title">دەنگڕێخەر</string> <string name="headset_plug">تکایه بیستۆکی پێوهبکه بۆ کاریگهری.</string> <string name="bass_boost_strength">زیادکردنی دەنگی دور</string> <string name="virtualizer_strength">دەوردانی دەنگ</string> + <string name="pr_title">هەمیشەی:</string> + <string name="pr_summary">زیادکردنی کاریگەری دەنگ</string> + <string name="pr_dialog_title">هەمیشە ڕێخستن</string> <string name="setup">دامهزراندن</string> - <!-- title of control panel picker dialog [CHAR LIMIT=30] --> <string name="picker_title">پەڕەی ڕێکخستنی کاریگەری دەنگ\"</string> </resources> diff --git a/res/values-lb/cm_strings.xml b/res/values-lb/cm_strings.xml index 2bef31f..cf7f852 100644 --- a/res/values-lb/cm_strings.xml +++ b/res/values-lb/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klassesch</string> <string name="dance">Danzmusek</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronesch</string> + <string name="ci_extreme">Elektronesch</string> <string name="small_speakers">Kleng Boxen</string> - <string name="custom">Personaliséiert</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personaliséiert</string> + <string name="user_n">Personaliséiert <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Keen</string> <string name="smallroom">Klenge Raum</string> <string name="mediumroom">Mëttlere Raum</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effekt am Lautsprecher-Modus net disponibel.</string> <string name="device_headset">Kopfhörer</string> <string name="device_speaker">Lautsprecher</string> - <string name="device_lineout">Line-Ausgank</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Ouni Kabel</string> <string name="reverb">Halleffekt</string> <string name="eq_preset">Equalizer-Virastellung</string> + <string name="devices">Apparater</string> + <string name="virtualizer">Virtualiséierung</string> + <string name="treble">Héichten</string> + <string name="bass">Bass</string> + <string name="rename">Ëmbenennen</string> + <string name="remove_custom_preset_warning_message">Bass du sécher, datt s du %1$s ewechhuele wëlls?</string> </resources> diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml index 6e63ce9..8d82431 100644 --- a/res/values-lb/strings.xml +++ b/res/values-lb/strings.xml @@ -15,16 +15,16 @@ limitations under the License. --> <resources> - <string name="app_name">MusicFX</string> - <!-- ControlPanelMusic strings --> + <string name="app_name">TounFX</string> <string name="no_effects">Effekter net disponibel.</string> - <string name="eq_dialog_title">Equalizer</string> + <string name="main_toggle_effects_title">Touneffekter</string> + <string name="eq_dialog_title">TounFX</string> <string name="headset_plug">Schléiss Kopfhörer u fir dës Effekter.</string> <string name="bass_boost_strength">Bassunhiewung</string> <string name="virtualizer_strength">Surround-Toun</string> + <string name="pr_title">Halleffekt:</string> + <string name="pr_summary">Zousätzlech Raum-Halleffekter</string> + <string name="pr_dialog_title">Virastellunge fir den Hall</string> <string name="setup">Astellen</string> - <string name="ci_extreme">FX-Booster</string> - <string name="user">Benotzer</string> - <!-- title of control panel picker dialog [CHAR LIMIT=30] --> <string name="picker_title">Steierelementer fir Touneffekter</string> </resources> diff --git a/res/values-lt/cm_strings.xml b/res/values-lt/cm_strings.xml index 2af6752..a351c58 100644 --- a/res/values-lt/cm_strings.xml +++ b/res/values-lt/cm_strings.xml @@ -15,34 +15,42 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normalus</string> <string name="classical">Klasikinis</string> <string name="dance">Šokių</string> <string name="flat">Lygus</string> <string name="folk">Liaudies</string> <string name="heavy_metal">Sunkusis metalas</string> - <string name="hip_hop">Hiphopas</string> + <string name="hip_hop">Hip Hop</string> <string name="jazz">Džiazas</string> - <string name="pop">Popsas</string> + <string name="pop">Pop</string> <string name="rock">Rokas</string> - <string name="electronic">Elektronika</string> + <string name="ci_extreme">Elektroninis</string> <string name="small_speakers">Maži garsiakalbiai</string> - <string name="custom">Priskirtas</string> + <string name="multimedia">Multimedija</string> + <string name="user">Priskirtas</string> + <string name="user_n">Priskirtas <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Joks</string> <string name="smallroom">Mažas kambarys</string> <string name="mediumroom">Vidutinio dydžio kambarys</string> <string name="largeroom">Didelis kambarys</string> <string name="mediumhall">Vidutinio dydžio salė</string> <string name="largehall">Didelė salė</string> - <string name="plate">Platforma</string> + <string name="plate">Plokštė</string> <string name="power_on_prompt">Paspauskite maitinimo mygtuką viršuje dešinėje.</string> <string name="effect_unavalable_for_speaker">Efektas negalimas garsiakalbio režime.</string> <string name="device_headset">Laisvų rankų įranga</string> <string name="device_speaker">Garsiakalbis</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> - <string name="device_wireless">Belaidis</string> + <string name="device_wireless">Bevielis</string> <string name="reverb">Aidas</string> - <string name="eq_preset">Vienodintuvo išankstiniai nustatymai</string> + <string name="eq_preset">Ekvalaizerio išankstiniai nustatymai</string> + <string name="devices">Įrenginiai</string> + <string name="virtualizer">Virtualizacija</string> + <string name="treble">Aukšti dažniai</string> + <string name="bass">Žemi dažniai</string> + <string name="rename">Pervadinti</string> + <string name="remove_custom_preset_warning_message">Ar tikrai norite pašalinti %1$s?</string> </resources> diff --git a/res/values-lv/cm_strings.xml b/res/values-lv/cm_strings.xml index 4cc059d..75b8872 100644 --- a/res/values-lv/cm_strings.xml +++ b/res/values-lv/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normāls</string> <string name="classical">Klasika</string> <string name="dance">Dejas</string> @@ -26,9 +26,9 @@ <string name="jazz">Džezs</string> <string name="pop">Pops</string> <string name="rock">Roks</string> - <string name="electronic">Elektronika</string> + <string name="ci_extreme">Elektronika</string> <string name="small_speakers">Mazi skaļruņi</string> - <string name="custom">Pielāgots</string> + <string name="user">Pielāgots</string> <string name="none">Nekas</string> <string name="smallroom">Maza istaba</string> <string name="mediumroom">Vidēja istaba</string> diff --git a/res/values-ml-rIN/cm_strings.xml b/res/values-ml-rIN/cm_strings.xml index 362e5f1..69bc8be 100644 --- a/res/values-ml-rIN/cm_strings.xml +++ b/res/values-ml-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">സാധാരണ</string> <string name="classical">ക്ലാസിക്കൽ</string> <string name="dance">ഡാൻസ്</string> @@ -26,9 +26,11 @@ <string name="jazz">ജാസ്സ്</string> <string name="pop">പോപ്പ്</string> <string name="rock">റോക്ക്</string> - <string name="electronic">ഇലക്ട്രോണിക്</string> + <string name="ci_extreme">ഇലക്ട്രോണിക്</string> <string name="small_speakers">ചെറിയ സ്പീക്കറുകൾ</string> - <string name="custom">ഇഷ്ടാനുസൃതം</string> + <string name="multimedia">മൾട്ടിമീഡിയ</string> + <string name="user">ഇഷ്ടാനുസൃതം</string> + <string name="user_n">ഇഷ്ടാനുസൃത <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">ആരിൽ നിന്നും വേണ്ട</string> <string name="smallroom">ചെറിയ റൂം</string> <string name="mediumroom">ഇടത്തരം റൂം</string> @@ -45,4 +47,10 @@ <string name="device_wireless">വയർലെസ്</string> <string name="reverb">പ്രതിധ്വനി</string> <string name="eq_preset">ഈക്വലൈസര് പ്രീസെറ്റ്</string> + <string name="devices">ഉപകരണങ്ങൾ</string> + <string name="virtualizer">വര്ച്വലൈസര്</string> + <string name="treble">ട്രെബിൾ</string> + <string name="bass">ബാസ്സ്</string> + <string name="rename">പേരുമാറ്റുക</string> + <string name="remove_custom_preset_warning_message">%1$s നീക്കം ചെയ്യണമെന്ന് നിങ്ങള്ക്ക് ഉറപ്പാണോ?</string> </resources> diff --git a/res/values-mr-rIN/cm_strings.xml b/res/values-mr-rIN/cm_strings.xml index ac03835..8c3a7c7 100644 --- a/res/values-mr-rIN/cm_strings.xml +++ b/res/values-mr-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">सामान्य</string> <string name="classical">शास्त्रीय</string> <string name="dance">नृत्य</string> @@ -26,9 +26,11 @@ <string name="jazz">जॅझ</string> <string name="pop">पॉप</string> <string name="rock">रॉक</string> - <string name="electronic">इलेक्ट्रॉनिक</string> + <string name="ci_extreme">इलेक्ट्रॉनिक</string> <string name="small_speakers">लहान स्पीकर्स</string> - <string name="custom">सानुकूल</string> + <string name="multimedia">मल्टिमिडिया</string> + <string name="user">सानुकूल</string> + <string name="user_n">सानुकूल करा <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">काहीही नाही</string> <string name="smallroom">लहान खोली</string> <string name="mediumroom">मध्यम खोली</string> @@ -36,7 +38,7 @@ <string name="mediumhall">मध्यम हॉल</string> <string name="largehall">मोठा हॉल</string> <string name="plate">प्लेट</string> - <string name="power_on_prompt">शीर्षाशी-उजवीकडे पॉवर बटण दाबा.</string> + <string name="power_on_prompt">शीर्षाच्या-उजवीकडे पॉवर बटण दाबा.</string> <string name="effect_unavalable_for_speaker">स्पीकर मोडसाठी प्रभाव उपलब्ध नाही.</string> <string name="device_headset">हेडसेट</string> <string name="device_speaker">स्पीकर</string> @@ -45,4 +47,10 @@ <string name="device_wireless">बिनतारी</string> <string name="reverb">रिव्हर्ब</string> <string name="eq_preset">इक्वेलायझर प्रीसेट</string> + <string name="devices">डिव्हाइसेस</string> + <string name="virtualizer">व्हर्च्युअलाझर</string> + <string name="treble">ट्रेबल</string> + <string name="bass">बास</string> + <string name="rename">पुनर्नामित करा</string> + <string name="remove_custom_preset_warning_message">तुम्हाला नक्की %1$s काढायचे आहे का?</string> </resources> diff --git a/res/values-nb/cm_strings.xml b/res/values-nb/cm_strings.xml index dc47ad4..eb4cc01 100644 --- a/res/values-nb/cm_strings.xml +++ b/res/values-nb/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klassisk</string> <string name="dance">Dans</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronisk</string> + <string name="ci_extreme">Elektronisk</string> <string name="small_speakers">Små høyttalere</string> - <string name="custom">Egendefinert</string> + <string name="multimedia">Multimedia</string> + <string name="user">Egendefinert</string> + <string name="user_n">Egendefinert <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Ingen</string> <string name="smallroom">Lite rom</string> <string name="mediumroom">Medium rom</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effekten er ikke tilgjengelig for høyttaler-modus.</string> <string name="device_headset">Hodetelefon</string> <string name="device_speaker">Høyttaler</string> - <string name="device_lineout">Linje ut</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Trådløs</string> <string name="reverb">Romklang</string> <string name="eq_preset">Equalizer forhåndsinnstilling</string> + <string name="devices">Enheter</string> + <string name="virtualizer">Virtualizer</string> + <string name="treble">Diskant</string> + <string name="bass">Bass</string> + <string name="rename">Gi nytt navn</string> + <string name="remove_custom_preset_warning_message">Er du sikker på at du vil fjerne %1$s?</string> </resources> diff --git a/res/values-nl/cm_strings.xml b/res/values-nl/cm_strings.xml index db0897c..04fa891 100644 --- a/res/values-nl/cm_strings.xml +++ b/res/values-nl/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normaal</string> <string name="classical">Klassiek</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronisch</string> + <string name="ci_extreme">Elektronisch</string> <string name="small_speakers">Kleine luidsprekers</string> - <string name="custom">Aangepast</string> + <string name="multimedia">Multimedia</string> + <string name="user">Aangepast</string> + <string name="user_n">Aangepaste <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Geen</string> <string name="smallroom">Kleine ruimte</string> <string name="mediumroom">Middelgrote ruimte</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effect niet beschikbaar voor luidsprekermodus.</string> <string name="device_headset">Koptelefoon</string> <string name="device_speaker">Luidspreker</string> - <string name="device_lineout">Lijnuitgang</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Draadloos</string> <string name="reverb">Galm</string> <string name="eq_preset">Equalizer-preset</string> + <string name="devices">Apparaten</string> + <string name="virtualizer">Virtualiseerder</string> + <string name="treble">Hoge tonen</string> + <string name="bass">Bas</string> + <string name="rename">Hernoemen</string> + <string name="remove_custom_preset_warning_message">Weet u zeker dat u %1$s wilt verwijderen?</string> </resources> diff --git a/res/values-or-rIN/cm_strings.xml b/res/values-or-rIN/cm_strings.xml index b4cf9e5..a2f43dd 100644 --- a/res/values-or-rIN/cm_strings.xml +++ b/res/values-or-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">ସାଧାରଣ</string> <string name="classical">କ୍ଲାସିକାଲ୍</string> <string name="dance">ନୃତ୍ୟ</string> @@ -26,9 +26,11 @@ <string name="jazz">ଜାଜ୍</string> <string name="pop">ପପ୍</string> <string name="rock">ରକ୍</string> - <string name="electronic">ବୈଦ୍ୟୁତିକ</string> + <string name="ci_extreme">ବୈଦ୍ୟୁତିକ</string> <string name="small_speakers">ଛୋଟ ସ୍ପିକର୍ଗୁଡିକ</string> - <string name="custom">କଷ୍ଟମ୍</string> + <string name="multimedia">ମଲ୍ଟିମିଡିଆ</string> + <string name="user">କଷ୍ଟମ୍</string> + <string name="user_n">କଷ୍ଟମ୍ <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">କିଛି ନୁହେଁ</string> <string name="smallroom">ଛୋଟ ରୁମ୍</string> <string name="mediumroom">ମଧ୍ୟମ ରୁମ୍</string> @@ -45,4 +47,10 @@ <string name="device_wireless">ଓୟାର୍ଲେସ୍</string> <string name="reverb">ର୍ରିଭର୍ବ:</string> <string name="eq_preset">ଇକ୍ୱାଲାଇଜର୍ ପ୍ରିସେଟ୍</string> + <string name="devices">ଡିଭାଇସଗୁଡିକ</string> + <string name="virtualizer">ଭର୍ଚୁଆଲାଇଜର୍</string> + <string name="treble">ଟ୍ରେବେଲ୍</string> + <string name="bass">ବାସ୍</string> + <string name="rename">ପୁନଃନାମ</string> + <string name="remove_custom_preset_warning_message">ଆପଣ %1$s ଅପସାରଣ କରିବାକୁ ଚାହୁଁଛନ୍ତି, ଏଥିରେ ଆପଣ ସୁନି୍ଶ୍ଚିତ କି?</string> </resources> diff --git a/res/values-pl/cm_strings.xml b/res/values-pl/cm_strings.xml index fd9f99a..4df7f01 100644 --- a/res/values-pl/cm_strings.xml +++ b/res/values-pl/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normalna</string> <string name="classical">Klasyczna</string> <string name="dance">Taneczna</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektroniczna</string> + <string name="ci_extreme">Elektroniczna</string> <string name="small_speakers">Małe głośniki</string> - <string name="custom">Własne</string> + <string name="multimedia">Multimedia</string> + <string name="user">Własne</string> + <string name="user_n">Niestandardowy <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Brak</string> <string name="smallroom">Mały pokój</string> <string name="mediumroom">Średni pokój</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efekt nie jest dostępny dla Głośnika urządzenia.</string> <string name="device_headset">Słuchawki</string> <string name="device_speaker">Głośnik</string> - <string name="device_lineout">Wyjście analogowe</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Bezprzewodowe</string> <string name="reverb">Pogłos</string> <string name="eq_preset">Ustawienia korektora</string> + <string name="devices">Urządzenia</string> + <string name="virtualizer">Wirtualizacja</string> + <string name="treble">Sopran</string> + <string name="bass">Bass</string> + <string name="rename">Zmień nazwę</string> + <string name="remove_custom_preset_warning_message">Jesteś pewny, że chcesz usunąć %1$s?</string> </resources> diff --git a/res/values-pt-rBR/cm_strings.xml b/res/values-pt-rBR/cm_strings.xml index 9f65d05..3f77695 100644 --- a/res/values-pt-rBR/cm_strings.xml +++ b/res/values-pt-rBR/cm_strings.xml @@ -15,20 +15,22 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clássica</string> <string name="dance">Dance</string> <string name="flat">Plano</string> <string name="folk">Folk</string> - <string name="heavy_metal">Heavy metal</string> - <string name="hip_hop">Hip-hop</string> + <string name="heavy_metal">Heavy Metal</string> + <string name="hip_hop">Hip-Hop</string> <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Eletrônica</string> - <string name="small_speakers">Falantes pequenos</string> - <string name="custom">Personalizado</string> + <string name="ci_extreme">Eletrônica</string> + <string name="small_speakers">Alto-falantes pequenos</string> + <string name="multimedia">Multimídia</string> + <string name="user">Personalizado</string> + <string name="user_n">Personalizado <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Nenhum</string> <string name="smallroom">Sala pequena</string> <string name="mediumroom">Sala média</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efeito não disponível no modo de alto-falante.</string> <string name="device_headset">Fone de ouvido</string> <string name="device_speaker">Alto-falante</string> - <string name="device_lineout">Saída de linha</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Sem fio</string> <string name="reverb">Reverb</string> <string name="eq_preset">Predefinição do equalizador</string> + <string name="devices">Dispositivos</string> + <string name="virtualizer">Virtualizador</string> + <string name="treble">Agudo</string> + <string name="bass">Grave</string> + <string name="rename">Renomear</string> + <string name="remove_custom_preset_warning_message">Tem certeza que deseja remover %1$s?</string> </resources> diff --git a/res/values-pt-rPT/cm_strings.xml b/res/values-pt-rPT/cm_strings.xml index 760485a..8fc7a09 100644 --- a/res/values-pt-rPT/cm_strings.xml +++ b/res/values-pt-rPT/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clássica</string> <string name="dance">Dança</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Eletrónica</string> + <string name="ci_extreme">Eletrónica</string> <string name="small_speakers">Altifalantes pequenos</string> - <string name="custom">Personalizado</string> + <string name="multimedia">Multimédia</string> + <string name="user">Personalizado</string> + <string name="user_n">Personalizado <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Nenhum</string> <string name="smallroom">Quarto pequeno</string> <string name="mediumroom">Quarto médio</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efeito não disponível no modo de altifalante.</string> <string name="device_headset">Auricular</string> <string name="device_speaker">Altifalante</string> - <string name="device_lineout">Saída de linha</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Sem fios</string> - <string name="reverb">Reverb</string> + <string name="reverb">Ressonância</string> <string name="eq_preset">Predefinição do equalizador</string> + <string name="devices">Dispositivos</string> + <string name="virtualizer">Virtualizador</string> + <string name="treble">Agudos</string> + <string name="bass">Graves</string> + <string name="rename">Mudar o nome</string> + <string name="remove_custom_preset_warning_message">Tem certeza que deseja remover %1$s?</string> </resources> diff --git a/res/values-ro/cm_strings.xml b/res/values-ro/cm_strings.xml index e73caf8..6f85f2b 100644 --- a/res/values-ro/cm_strings.xml +++ b/res/values-ro/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Clasică</string> <string name="dance">Dance</string> @@ -26,9 +26,10 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronică</string> + <string name="ci_extreme">Electronică</string> <string name="small_speakers">Difuzoare Mici</string> - <string name="custom">Personalizat</string> + <string name="multimedia">Multimedia</string> + <string name="user">Personalizat</string> <string name="none">Niciunul</string> <string name="smallroom">Cameră mică</string> <string name="mediumroom">Cameră medie</string> @@ -45,4 +46,8 @@ <string name="device_wireless">Wireless</string> <string name="reverb">Reverberație</string> <string name="eq_preset">Preset egalizator</string> + <string name="devices">Dispozitive</string> + <string name="treble">Înalte</string> + <string name="bass">Joase</string> + <string name="rename">Redenumire</string> </resources> diff --git a/res/values-ru/cm_strings.xml b/res/values-ru/cm_strings.xml index 46b7efd..2869dd4 100644 --- a/res/values-ru/cm_strings.xml +++ b/res/values-ru/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Стандартный</string> <string name="classical">Классика</string> <string name="dance">Танцевальная музыка</string> @@ -26,9 +26,11 @@ <string name="jazz">Джаз</string> <string name="pop">Поп</string> <string name="rock">Рок</string> - <string name="electronic">Электронная музыка</string> + <string name="ci_extreme">Электронная музыка</string> <string name="small_speakers">Маленькие динамики</string> - <string name="custom">Пользовательские настр.</string> + <string name="multimedia">Мультимедиа</string> + <string name="user">Пользовательские</string> + <string name="user_n">Пользоват. <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Нет</string> <string name="smallroom">Маленькая комната</string> <string name="mediumroom">Средняя комната</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Эффект недоступен для динамиков.</string> <string name="device_headset">Наушники</string> <string name="device_speaker">Динамик</string> - <string name="device_lineout">Линейный выход</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Беспров. устр.</string> <string name="reverb">Реверберация</string> - <string name="eq_preset">Предуст. эквалайзера</string> + <string name="eq_preset">Предустановки</string> + <string name="devices">Устройства</string> + <string name="virtualizer">Виртуализатор</string> + <string name="treble">Высокие</string> + <string name="bass">НЧ</string> + <string name="rename">Переименовать</string> + <string name="remove_custom_preset_warning_message">Действительно удалить «%1$s»?</string> </resources> diff --git a/res/values-si-rLK/cm_strings.xml b/res/values-si-rLK/cm_strings.xml index 0ed1eec..34e55cc 100644 --- a/res/values-si-rLK/cm_strings.xml +++ b/res/values-si-rLK/cm_strings.xml @@ -26,9 +26,9 @@ <string name="jazz">ජෑස්</string> <string name="pop">පොප්</string> <string name="rock">රොක්</string> - <string name="electronic">ඉලෙක්ට්රොනික</string> + <string name="ci_extreme">ඉලෙක්ට්රොනික</string> <string name="small_speakers">කුඩා ස්පීකර</string> - <string name="custom">රිසිවූ</string> + <string name="user">රිසිවූ</string> <string name="none">කිසිවක් නැත</string> <string name="smallroom">කුඩා කාමරය</string> <string name="mediumroom">මධ්යම කාමරය</string> @@ -36,6 +36,7 @@ <string name="mediumhall">මධ්යම ශාලාව</string> <string name="largehall">විශාල ශාලාව</string> <string name="plate">තහඩුව</string> + <string name="eq_custom">රිසිකරණය සඳහා, \'රිසිවූ\' තෝරන්න</string> <string name="power_on_prompt">ඉහළින් දකුණේ ඇති බල බොත්තම ඔබන්න.</string> <string name="effect_unavalable_for_speaker">ස්පීකර ප්රකාරය සඳහා බලපෑම ලබාගත නොහැක.</string> <string name="device_headset">කන්යොමුව</string> @@ -43,6 +44,6 @@ <string name="device_usb">USB</string> <string name="device_bluetooth">බ්ලූටූත්</string> <string name="device_wireless">රැහැන් රහිත</string> - <string name="reverb">ආඛ්යාතය</string> <string name="eq_preset">සමකරකයේ පෙරසැකසීම</string> + <string name="reverb">ආඛ්යාතය</string> </resources> diff --git a/res/values-sk/cm_strings.xml b/res/values-sk/cm_strings.xml index 8cd4758..ca48bbb 100644 --- a/res/values-sk/cm_strings.xml +++ b/res/values-sk/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normálne</string> <string name="classical">Klasická hudba</string> <string name="dance">Tanec</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronická hudba</string> + <string name="ci_extreme">Elektronická hudba</string> <string name="small_speakers">Malé reproduktory</string> - <string name="custom">Vlastné</string> + <string name="multimedia">Multimédiá</string> + <string name="user">Vlastné</string> + <string name="user_n">Vlastné <xliff:g id="preset_num">%1$d </xliff:g></string> <string name="none">Žiadny</string> <string name="smallroom">Malá miestnosť</string> <string name="mediumroom">Stredná miestnosť</string> @@ -45,4 +47,10 @@ <string name="device_wireless">Bezdrôtové</string> <string name="reverb">Reverb</string> <string name="eq_preset">Predvoľba ekvalizéra</string> + <string name="devices">Zariadenia</string> + <string name="virtualizer">Virtualizér</string> + <string name="treble">Výšky</string> + <string name="bass">Basy</string> + <string name="rename">Premenovať</string> + <string name="remove_custom_preset_warning_message">Naozaj chcete odstrániť %1$s?</string> </resources> diff --git a/res/values-sl/cm_strings.xml b/res/values-sl/cm_strings.xml index c848ded..a6b9601 100644 --- a/res/values-sl/cm_strings.xml +++ b/res/values-sl/cm_strings.xml @@ -15,8 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <string name="normal">Običajna</string> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="normal">Običajno</string> <string name="classical">Klasična</string> <string name="dance">Plesna</string> <string name="flat">Ploska</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronska</string> + <string name="ci_extreme">Elektronska</string> <string name="small_speakers">Majhni zvočniki</string> - <string name="custom">Po meri</string> + <string name="multimedia">Večpredstavnost</string> + <string name="user">Po meri</string> + <string name="user_n"><xliff:g id="preset_num">%1$d</xliff:g> po meri</string> <string name="none">Brez</string> <string name="smallroom">Majhna soba</string> <string name="mediumroom">Srednja soba</string> @@ -36,14 +38,19 @@ <string name="mediumhall">Srednje velika dvorana</string> <string name="largehall">Velika dvorana</string> <string name="plate">Plošča</string> - <string name="power_on_prompt">Pritisnite gumb za vklop na zgornji desni strani.</string> + <string name="power_on_prompt">Pritisnite na gumb za vklop na zgornji desni strani.</string> <string name="effect_unavalable_for_speaker">Učinek ni na voljo v načinu Zvočnika.</string> <string name="device_headset">Slušalke</string> <string name="device_speaker">Zvočnik</string> - <string name="device_lineout">Izhod za slušalke</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Brezžično</string> <string name="reverb">Odmev</string> <string name="eq_preset">Prednastavitev izenačevalnika</string> + <string name="devices">Naprave</string> + <string name="virtualizer">Navidezovalnik</string> + <string name="treble">Visoki toni</string> + <string name="bass">Nizki toni</string> + <string name="rename">Preimenuj</string> + <string name="remove_custom_preset_warning_message">Ali res želite odstraniti %1$s?</string> </resources> diff --git a/res/values-sr/cm_strings.xml b/res/values-sr/cm_strings.xml index a3d2e05..e1bb743 100644 --- a/res/values-sr/cm_strings.xml +++ b/res/values-sr/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Нормално</string> <string name="classical">Класика</string> <string name="dance">Денс</string> @@ -26,9 +26,11 @@ <string name="jazz">Џез</string> <string name="pop">Поп</string> <string name="rock">Рок</string> - <string name="electronic">Електроника</string> + <string name="ci_extreme">Електроника</string> <string name="small_speakers">Мали звучници</string> - <string name="custom">Прилагођено</string> + <string name="multimedia">Мултимедија</string> + <string name="user">Прилагођено</string> + <string name="user_n">Прилагођено <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Ниједно</string> <string name="smallroom">Мала соба</string> <string name="mediumroom">Средња соба</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Ефекат није доступан за режим Звучника.</string> <string name="device_headset">Слушалице</string> <string name="device_speaker">Звучник</string> - <string name="device_lineout">Излаз</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Wireless</string> <string name="reverb">Reverb</string> <string name="eq_preset">Еквилајзер унапред постављен</string> + <string name="devices">Уређаји</string> + <string name="virtualizer">Визуелно</string> + <string name="treble">Високи</string> + <string name="bass">Бас</string> + <string name="rename">Преименуј</string> + <string name="remove_custom_preset_warning_message">Да ли сигурно желиш уклонити %1$s?</string> </resources> diff --git a/res/values-sv/cm_strings.xml b/res/values-sv/cm_strings.xml index d437200..10c5b21 100644 --- a/res/values-sv/cm_strings.xml +++ b/res/values-sv/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klassisk</string> <string name="dance">Dance</string> @@ -26,9 +26,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronisk</string> + <string name="ci_extreme">Elektronisk</string> <string name="small_speakers">Små högtalare</string> - <string name="custom">Anpassad</string> + <string name="multimedia">Multimedia</string> + <string name="user">Anpassad</string> + <string name="user_n">Anpassade <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Ingen</string> <string name="smallroom">Litet rum</string> <string name="mediumroom">Medelstort rum</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Effekten finns inte tillgänglig för högtalarläge.</string> <string name="device_headset">Hörlurar</string> <string name="device_speaker">Högtalare</string> - <string name="device_lineout">Linjeutgång</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Trådlös</string> <string name="reverb">Rumsklang</string> <string name="eq_preset">Equalizerförval</string> + <string name="devices">Enheter</string> + <string name="virtualizer">Virtualiserare</string> + <string name="treble">Diskant</string> + <string name="bass">Bas</string> + <string name="rename">Byt namn</string> + <string name="remove_custom_preset_warning_message">Är du säker du vill ta bort %1$s?</string> </resources> diff --git a/res/values-ta-rIN/cm_strings.xml b/res/values-ta-rIN/cm_strings.xml index af60552..c2fbb6b 100644 --- a/res/values-ta-rIN/cm_strings.xml +++ b/res/values-ta-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">இயல்பு</string> <string name="classical">கிளாசிக்கல்</string> <string name="dance">டான்ஸ்</string> @@ -26,9 +26,11 @@ <string name="jazz">ஜாஸ்</string> <string name="pop">பாப்</string> <string name="rock">ராக்</string> - <string name="electronic">மின்னணு</string> + <string name="ci_extreme">மின்னணு</string> <string name="small_speakers">சிறிய ஒலிபெருக்கிகள்</string> - <string name="custom">தனிப்பயன்</string> + <string name="multimedia">மல்டிமீடியா</string> + <string name="user">தனிப்பயன்</string> + <string name="user_n">தனிப்பயன் <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">ஏதுமில்லை</string> <string name="smallroom">சிறிய அறை</string> <string name="mediumroom">நடுத்தர அறை</string> @@ -45,4 +47,10 @@ <string name="device_wireless">கம்பியில்லா</string> <string name="reverb">எதிர்முழக்கம்</string> <string name="eq_preset">சமனாக்கி முன்னமை</string> + <string name="devices">சாதனங்கள்</string> + <string name="virtualizer">தோற்றநிலையாக்கி</string> + <string name="treble">டிரெபில்</string> + <string name="bass">பேஸ்</string> + <string name="rename">மறுபெயரிடு</string> + <string name="remove_custom_preset_warning_message">%1$sஐ நீக்க நீங்கள் நிச்சயமாக விரும்புகிறீர்களா?</string> </resources> diff --git a/res/values-te-rIN/cm_strings.xml b/res/values-te-rIN/cm_strings.xml index aa52869..2c8344b 100644 --- a/res/values-te-rIN/cm_strings.xml +++ b/res/values-te-rIN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">సాధారణం</string> <string name="classical">క్లాసికల్</string> <string name="dance">నృత్యం</string> @@ -26,15 +26,17 @@ <string name="jazz">జాజ్</string> <string name="pop">పాప్</string> <string name="rock">రాక్</string> - <string name="electronic">ఎలక్ట్రానిక్</string> + <string name="ci_extreme">ఎలక్ట్రానిక్</string> <string name="small_speakers">చిన్న స్పీకర్లు</string> - <string name="custom">అనుకూలం</string> + <string name="multimedia">మల్టీమీడియా</string> + <string name="user">అనుకూలం</string> + <string name="user_n">అనుకూల <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">ఏదీ లేదు</string> <string name="smallroom">చిన్న గది</string> - <string name="mediumroom">మధ్యస్థం గది</string> + <string name="mediumroom">మధ్యస్థ గది</string> <string name="largeroom">పెద్ద గది</string> - <string name="mediumhall">మధ్యస్థం హాల్</string> - <string name="largehall">పెద్ద గది</string> + <string name="mediumhall">మధ్యస్థ హాల్</string> + <string name="largehall">పెద్ద హాల్</string> <string name="plate">ప్లేటు</string> <string name="power_on_prompt">పైన కుడి వైపున ఉన్న పవర్ బటన్ నొక్కండి.</string> <string name="effect_unavalable_for_speaker">స్పీకర్ రీతి కొరకు ప్రభావం అందుబాటులో లేదు.</string> @@ -45,4 +47,10 @@ <string name="device_wireless">నిస్తంత్రి</string> <string name="reverb">ప్రతిధ్వని</string> <string name="eq_preset">ముందుగా అమర్చిన ఈక్విలైజర్</string> + <string name="devices">పరికరంలు</string> + <string name="virtualizer">వర్చువలైజర్</string> + <string name="treble">ట్రెబెల్</string> + <string name="bass">బాస్</string> + <string name="rename">పేరు మార్చు</string> + <string name="remove_custom_preset_warning_message">మీరు %1$s ను తొలిగించాలని నిర్ధారించుకున్నారా?</string> </resources> diff --git a/res/values-th/cm_strings.xml b/res/values-th/cm_strings.xml index 0d7d1f5..70983d5 100644 --- a/res/values-th/cm_strings.xml +++ b/res/values-th/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">ปกติ</string> <string name="classical">คลาสสิก</string> <string name="dance">แดนซ์</string> @@ -26,9 +26,11 @@ <string name="jazz">แจ๊ส</string> <string name="pop">ป๊อป</string> <string name="rock">ร็อค</string> - <string name="electronic">อิเล็กทรอนิก</string> + <string name="ci_extreme">อิเล็กทรอนิก</string> <string name="small_speakers">ลำโพงเล็ก</string> - <string name="custom">กำหนดเอง</string> + <string name="multimedia">มัลติมีเดีย</string> + <string name="user">กำหนดเอง</string> + <string name="user_n">กำหนดเอง <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">ไม่มี</string> <string name="smallroom">ห้องขนาดเล็ก</string> <string name="mediumroom">ห้องขนาดกลาง</string> @@ -36,14 +38,19 @@ <string name="mediumhall">โถงขนาดกลาง</string> <string name="largehall">โถงขนาดใหญ่</string> <string name="plate">เวที</string> - <string name="power_on_prompt">กดปุ่มเพาเวอร์ด้านบนขวา</string> - <string name="effect_unavalable_for_speaker">เอฟเฟกต์นี้ไม่สามารถใช้ได้กับรูปแบบลำโพง</string> + <string name="power_on_prompt">กดปุ่มพาวเวอร์ด้านบนขวา</string> + <string name="effect_unavalable_for_speaker">ใช้เอฟเฟกต์ไม่ได้กับรูปแบบลำโพง</string> <string name="device_headset">หูฟัง</string> <string name="device_speaker">ลำโพง</string> - <string name="device_lineout">สายออก</string> <string name="device_usb">USB</string> <string name="device_bluetooth">บลูทูธ</string> <string name="device_wireless">แบบไร้สาย</string> <string name="reverb">เสียงก้อง</string> <string name="eq_preset">ชุดรูปแบบปรับแต่งเสียง</string> + <string name="devices">อุปกรณ์</string> + <string name="virtualizer">บริการเอฟเฟกต์เสมือนจริง</string> + <string name="treble">Treble</string> + <string name="bass">Bass</string> + <string name="rename">เปลี่ยนชื่อ</string> + <string name="remove_custom_preset_warning_message">แน่ใจหรือไม่ว่าต้องการลบ %1$s</string> </resources> diff --git a/res/values-tr/cm_strings.xml b/res/values-tr/cm_strings.xml index 605d772..b6aa029 100644 --- a/res/values-tr/cm_strings.xml +++ b/res/values-tr/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Normal</string> <string name="classical">Klasik</string> <string name="dance">Dans</string> @@ -26,9 +26,11 @@ <string name="jazz">Caz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Elektronik</string> + <string name="ci_extreme">Elektronik</string> <string name="small_speakers">Küçük hoparlörler</string> - <string name="custom">Özel</string> + <string name="multimedia">Multimedya</string> + <string name="user">Özel</string> + <string name="user_n">Özel <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">Yok</string> <string name="smallroom">Küçük oda</string> <string name="mediumroom">Orta boy oda</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">Efekt Hoparlör modunda kullanılamıyor.</string> <string name="device_headset">Kulaklık</string> <string name="device_speaker">Hoparlör</string> - <string name="device_lineout">Hat çıkışı</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> - <string name="device_wireless">Kablosuz</string> + <string name="device_wireless">Kablosuz cihaz</string> <string name="reverb">Yankı</string> <string name="eq_preset">Equalizer önayarı</string> + <string name="devices">Cihazlar</string> + <string name="virtualizer">Görselleştirici</string> + <string name="treble">Tiz</string> + <string name="bass">Bass</string> + <string name="rename">Yeniden adlandır</string> + <string name="remove_custom_preset_warning_message">%1$s önayarını kaldırmak istediğinizden emin misiniz?</string> </resources> diff --git a/res/values-ug/cm_strings.xml b/res/values-ug/cm_strings.xml index 9422ddf..174e330 100644 --- a/res/values-ug/cm_strings.xml +++ b/res/values-ug/cm_strings.xml @@ -15,20 +15,22 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">ئادەتتىكى</string> <string name="classical">كىلاسسىك</string> <string name="dance">ئۇسسۇل</string> - <string name="flat">تەكشىلىك</string> + <string name="flat">يېنىك</string> <string name="folk">خەلق ناخشىسى</string> <string name="heavy_metal">ئېغىر مېتال</string> <string name="hip_hop">Hip Hop</string> <string name="jazz">جاز</string> <string name="pop">مودا نەغمە</string> <string name="rock">تولغىما ئۇسسۇل</string> - <string name="electronic">ئېلېكتىرونلۇق</string> + <string name="ci_extreme">ئېلېكتىرونلۇق</string> <string name="small_speakers">كىچىك ياڭراتقۇلار</string> - <string name="custom">ئىختىيارىي</string> + <string name="multimedia">كۆپ ۋاستە</string> + <string name="user">ئىختىيارى</string> + <string name="user_n">خاسلاشتۇرۇش <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">يوق</string> <string name="smallroom">كىچىك ئۆي</string> <string name="mediumroom">ئوتتۇرىھال ئۆي</string> @@ -45,4 +47,10 @@ <string name="device_wireless">سىمسىز</string> <string name="reverb">ئارىلاش ئاۋاز</string> <string name="eq_preset">تەڭشىگۈچنى ئالدىن تەڭشەش</string> + <string name="devices">ئۈسكىنە</string> + <string name="virtualizer">مەۋھۇم تەڭشىگۈچ</string> + <string name="treble">ئۈچ ھەسسە</string> + <string name="bass">تۆۋەن ئاۋاز</string> + <string name="rename">قايتا ناملاش</string> + <string name="remove_custom_preset_warning_message">%1$s نى راستلا ئۆچۈرۋېتەمسىز؟</string> </resources> diff --git a/res/values-uk/cm_strings.xml b/res/values-uk/cm_strings.xml index 8de6491..7620f51 100644 --- a/res/values-uk/cm_strings.xml +++ b/res/values-uk/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Нормальний</string> <string name="classical">Класика</string> <string name="dance">Танцювальна</string> @@ -26,9 +26,11 @@ <string name="jazz">Джаз</string> <string name="pop">Поп</string> <string name="rock">Рок</string> - <string name="electronic">Електронна</string> + <string name="ci_extreme">Електронна</string> <string name="small_speakers">Маленькі динаміки</string> - <string name="custom">Користувач</string> + <string name="multimedia">Мультимедіа</string> + <string name="user">Користувач</string> + <string name="user_n">Користувацькі <xliff:g id="preset_num">%1$d </xliff:g></string> <string name="none">Немає</string> <string name="smallroom">Мала кімната</string> <string name="mediumroom">Середня кімната</string> @@ -45,4 +47,10 @@ <string name="device_wireless">Бездротові</string> <string name="reverb">Реверберація</string> <string name="eq_preset">Налаштування еквалайзера</string> + <string name="devices">Пристрої</string> + <string name="virtualizer">Віртуалізатор</string> + <string name="treble">Високі</string> + <string name="bass">Бас</string> + <string name="rename">Перейменувати</string> + <string name="remove_custom_preset_warning_message">Ви дійсно бажаєте видалити %1$s?</string> </resources> diff --git a/res/values-vi/cm_strings.xml b/res/values-vi/cm_strings.xml index 6ee978d..cab13d8 100644 --- a/res/values-vi/cm_strings.xml +++ b/res/values-vi/cm_strings.xml @@ -15,35 +15,42 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">Bình thường</string> <string name="classical">Cổ điển</string> <string name="dance">Dance</string> <string name="flat">Flat</string> - <string name="folk">Dân ca</string> + <string name="folk">Dân gian</string> <string name="heavy_metal">Heavy Metal</string> <string name="hip_hop">Hip Hop</string> <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> + <string name="ci_extreme">Electronic</string> <string name="small_speakers">Loa nhỏ</string> - <string name="custom">Tùy chỉnh</string> - <string name="none">Không</string> + <string name="multimedia">Đa phương tiện</string> + <string name="user">Tuỳ chỉnh</string> + <string name="user_n">Tuỳ chỉnh <xliff:g id="preset_num">%1$d </xliff:g></string> + <string name="none">Không có</string> <string name="smallroom">Phòng nhỏ</string> <string name="mediumroom">Phòng trung bình</string> <string name="largeroom">Phòng lớn</string> - <string name="mediumhall">Hội trường trung bình</string> + <string name="mediumhall">Hộ trường trung bình</string> <string name="largehall">Hội trường lớn</string> - <string name="plate">Đĩa</string> + <string name="plate">Plate</string> <string name="power_on_prompt">Nhấn nút nguồn điện ở trên cùng bên phải.</string> <string name="effect_unavalable_for_speaker">Hiệu ứng không khả dụng với chế độ Loa ngoài.</string> <string name="device_headset">Tai nghe</string> <string name="device_speaker">Loa ngoài</string> - <string name="device_lineout">Đường tín hiệu cho đầu ra</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Không dây</string> <string name="reverb">Âm vang</string> - <string name="eq_preset">Cài sẵn bộ chỉnh âm</string> + <string name="eq_preset">Thiết lập trước bộ chỉnh âm</string> + <string name="devices">Thiết bị</string> + <string name="virtualizer">Trực quan hóa</string> + <string name="treble">Gấp ba</string> + <string name="bass">Bass</string> + <string name="rename">Đổi tên</string> + <string name="remove_custom_preset_warning_message">Bạn có chắc là bạn muốn gỡ bỏ %1$s?</string> </resources> diff --git a/res/values-zh-rCN/cm_strings.xml b/res/values-zh-rCN/cm_strings.xml index 025fe9f..00e2159 100644 --- a/res/values-zh-rCN/cm_strings.xml +++ b/res/values-zh-rCN/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">正常</string> <string name="classical">古典音乐</string> <string name="dance">舞曲</string> @@ -26,9 +26,11 @@ <string name="jazz">爵士</string> <string name="pop">流行</string> <string name="rock">摇滚</string> - <string name="electronic">电子</string> + <string name="ci_extreme">电子</string> <string name="small_speakers">小型喇叭</string> - <string name="custom">自定义</string> + <string name="multimedia">多媒体</string> + <string name="user">自定义</string> + <string name="user_n">自定义 <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">无</string> <string name="smallroom">小房间</string> <string name="mediumroom">中等房间</string> @@ -40,10 +42,15 @@ <string name="effect_unavalable_for_speaker">效果不适用于扬声器模式。</string> <string name="device_headset">耳机</string> <string name="device_speaker">扬声器</string> - <string name="device_lineout">线路输出</string> <string name="device_usb">USB</string> <string name="device_bluetooth">蓝牙</string> <string name="device_wireless">无线</string> <string name="reverb">混响</string> <string name="eq_preset">均衡器预设</string> + <string name="devices">设备</string> + <string name="virtualizer">虚拟均衡器</string> + <string name="treble">三倍</string> + <string name="bass">低音</string> + <string name="rename">重命名</string> + <string name="remove_custom_preset_warning_message">您确定想要移除 %1$s ?</string> </resources> diff --git a/res/values-zh-rHK/cm_strings.xml b/res/values-zh-rHK/cm_strings.xml index 249acde..4725c99 100644 --- a/res/values-zh-rHK/cm_strings.xml +++ b/res/values-zh-rHK/cm_strings.xml @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">正常</string> <string name="classical">古典音樂</string> <string name="dance">舞曲</string> @@ -26,9 +26,11 @@ <string name="jazz">爵士樂</string> <string name="pop">流行音樂</string> <string name="rock">搖滾</string> - <string name="electronic">電子音樂</string> + <string name="ci_extreme">電子音樂</string> <string name="small_speakers">小型喇叭</string> - <string name="custom">自訂</string> + <string name="multimedia">多媒體</string> + <string name="user">自訂</string> + <string name="user_n">自訂 <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">無</string> <string name="smallroom">小型房間</string> <string name="mediumroom">中型房間</string> @@ -45,4 +47,10 @@ <string name="device_wireless">無綫</string> <string name="reverb">混響效果</string> <string name="eq_preset">等化器預設</string> + <string name="devices">裝置</string> + <string name="virtualizer">虛擬器</string> + <string name="treble">高音</string> + <string name="bass">低音</string> + <string name="rename">重新命名</string> + <string name="remove_custom_preset_warning_message">您確定想要移除 %1$s?</string> </resources> diff --git a/res/values-zh-rTW/cm_strings.xml b/res/values-zh-rTW/cm_strings.xml index 13bea9b..368b2f9 100644 --- a/res/values-zh-rTW/cm_strings.xml +++ b/res/values-zh-rTW/cm_strings.xml @@ -15,20 +15,20 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="normal">正常</string> - <string name="classical">古典</string> + <string name="classical">古典音樂</string> <string name="dance">舞曲</string> <string name="flat">平淡</string> <string name="folk">民謠</string> <string name="heavy_metal">重金屬</string> <string name="hip_hop">嘻哈</string> <string name="jazz">爵士樂</string> - <string name="pop">流行</string> + <string name="pop">流行音樂</string> <string name="rock">搖滾</string> - <string name="electronic">電子</string> - <string name="small_speakers">小型揚聲器</string> - <string name="custom">自訂</string> + <string name="ci_extreme">電子</string> + <string name="small_speakers">小型喇叭</string> + <string name="user">自訂</string> <string name="none">無</string> <string name="smallroom">小型房間</string> <string name="mediumroom">中型房間</string> @@ -39,11 +39,13 @@ <string name="power_on_prompt">按下右上方的電源鍵。</string> <string name="effect_unavalable_for_speaker">效果不適用於擴音器模式。</string> <string name="device_headset">耳機</string> - <string name="device_speaker">揚聲器</string> - <string name="device_lineout">線路輸出</string> + <string name="device_speaker">擴音器</string> <string name="device_usb">USB</string> <string name="device_bluetooth">藍牙</string> <string name="device_wireless">無線裝置</string> <string name="reverb">混響</string> - <string name="eq_preset">預設等化器</string> + <string name="eq_preset">等化器預設</string> + <string name="devices">裝置</string> + <string name="rename">重新命名</string> + <string name="remove_custom_preset_warning_message">你確定要刪除 %1$s?</string> </resources> diff --git a/res/values/attrs.xml b/res/values/attrs.xml deleted file mode 100644 index 2da318f..0000000 --- a/res/values/attrs.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright (c) 2013, The Linux Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---> - -<resources> - <declare-styleable name="Knob"> - <attr name="label" format="string" /> - <attr name="background" format="integer" /> - <attr name="foreground" format="integer" /> - </declare-styleable> -</resources> diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml index 16d24ff..bea5314 100644 --- a/res/values/cm_strings.xml +++ b/res/values/cm_strings.xml @@ -14,8 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources> - <string name="app_name_cm" translatable="false">AudioFX</string> +<resources xmlns:xliff="rn:oasis:names:tc:xliff:document:1.2"> + <string name="snack_bar_not_default">AudioFX is not your default effects control panel.</string> + <string name="snack_bar_not_default_set">Set default</string> + <string name="snack_bar_not_default_not_now">Not now</string> + + <string name="qs_tile_content_description">Toggle AudioFX on/off for the current device</string> + <string name="qs_tile_label">AudioFX (%1$s)</string> <string name="normal">Normal</string> <string name="classical">Classical</string> @@ -27,9 +32,11 @@ <string name="jazz">Jazz</string> <string name="pop">Pop</string> <string name="rock">Rock</string> - <string name="electronic">Electronic</string> + <string name="ci_extreme">Electronic</string> <string name="small_speakers">Small speakers</string> - <string name="custom">Custom</string> + <string name="multimedia">Multimedia</string> + <string name="user">Custom</string> + <string name="user_n">Custom <xliff:g id="preset_num">%1$d</xliff:g></string> <string name="none">None</string> <string name="smallroom">Small room</string> @@ -41,13 +48,32 @@ <string name="power_on_prompt">Press the power button on the top-right.</string> <string name="effect_unavalable_for_speaker">Effect not available for Speaker mode.</string> - + <string name="device_headset">Headset</string> <string name="device_speaker">Speaker</string> - <string name="device_lineout">Line out</string> <string name="device_usb">USB</string> <string name="device_bluetooth">Bluetooth</string> <string name="device_wireless">Wireless</string> + <string name="device_line_out">Line out</string> + <string name="reverb">Reverb</string> <string name="eq_preset">Equalizer preset</string> + + <string name="devices">Devices</string> + + <string name="virtualizer">Virtualizer</string> + <string name="treble">Treble</string> + <string name="bass">Bass</string> + + <string name="rename">Rename</string> + <string name="remove_custom_preset_warning_message">Are you sure you want to remove %1$s?</string> + + <string name="app_name" translatable="false">AudioFX</string> + <string name="app_title" translatable="false">@string/app_name</string> + + <string name="powered_by_maxx_audio" translatable="false">Powered by MaxxAudio</string> + <string name="mode_dts" translatable="false">with DTS Sound™</string> + <string name="powered_by">powered by</string> + + </resources> diff --git a/res/values/color.xml b/res/values/colors.xml index 2814058..2a88cd4 100644 --- a/res/values/color.xml +++ b/res/values/colors.xml @@ -35,30 +35,50 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <color name="reverb_label_text_color">#ff808080</color> <color name="highlight">#cc8f8f8f</color> - <color name="lowlight">#ccf4fcff</color> - <color name="grey">#9e9e9e</color> + <color name="lowlight">#558f8f8f</color> + <color name="grey">#80e7e7e7</color> <color name="disabled">#80525252</color> <color name="disabled_gallery">#888888</color> <color name="disabled_knob">#575757</color> - <color name="primary">#607D8B</color> - <color name="primary_dark">#455A64</color> - <color name="accent">#78909C</color> - - <color name="white">#fafafa</color> - <color name="black">#212121</color> + <color name="white">#ffffffff</color> + <color name="color_grey">#90dedede</color> + <color name="black">#ff000000</color> <color name="grid_lines">#22ffffff</color> <color name="cb">#88ffffff</color> <color name="freq_hl">#2000ddff</color> <color name="freq_hl2">#4033b5e5</color> - <color name="eq_red">#f44336</color> - <color name="eq_green">#8bc34a</color> - <color name="eq_yellow">#ff9800</color> + <color name="eq_red">#80ff0000</color> + <color name="eq_green">#80aaeaaa</color> + <color name="eq_yellow">#80f0ff00</color> <color name="eq_holo_bright">#8000ddff</color> <color name="eq_holo_blue">#8033b5e5</color> <color name="eq_holo_dark">#800099cc</color> <color name="cb_shader">#ffffffff</color> <color name="cb_shader_alpha">#22ffffff</color> - <color name="window_background">@color/black</color> + <color name="window_background">#ff000000</color> + + <color name="band_freq_label">#ffffffff</color> + <color name="band_bar_color_1">#FFCCCCCC</color> + <color name="band_bar_color_2">#FFDDDDDD</color> + + <color name="preset_custom">#CE5B4C</color> + <color name="preset_normal">#CB6139</color> + <color name="preset_classical">#BE963F</color> + <color name="preset_dance">#BEBD3F</color> + <color name="preset_flat">#84B74E</color> + <color name="preset_folk">#80B74F</color> + <color name="preset_metal">#65B759</color> + <color name="preset_hiphop">#5CB797</color> + <color name="preset_jazz">#5B82B7</color> + <color name="preset_pop">#585DCA</color> + <color name="preset_rock">#9A62CB</color> + <color name="preset_electronic">#C862C5</color> + <color name="preset_small_speakers">#CB629A</color> + <color name="band_bar_color_selected">#ffffffff</color> + <color name="knob_container_background">#323437</color> + <color name="disabled_eq">#ff6c6c6c</color> + <color name="radial_knob_arc_bg">#4e5052</color> + <color name="radial_knob_arc_bg_disabled">#6c6c6c</color> </resources> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index ab9f0b8..93019de 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -17,12 +17,41 @@ <resources> <dimen name="eq_slider_margin">5dip</dimen> <dimen name="eq_slider_height">180dip</dimen> - <dimen name="eq_text_height">20dip</dimen> + + <!-- size of frequency labels below bars --> + <dimen name="eq_label_text_size">9sp</dimen> + + <!-- size of selected frequency/db text sizes [both right now ] --> + <dimen name="eq_selected_box_height">15sp</dimen> + + <!-- extra room at the bottom of the eq bar to never shrink below --> + <dimen name="eq_bar_bottom_grab_space">20dp</dimen> + + <!-- extra space above floating selected bar db --> + <dimen name="eq_bar_top_padding">10dp</dimen> + + <dimen name="preset_text_size">30sp</dimen> + <dimen name="preset_text_padding">4dp</dimen> <dimen name="action_bar_switch_padding">16dip</dimen> <dimen name="action_bar_button_width">96dip</dimen> <dimen name="action_bar_button_height">34dip</dimen> - <dimen name="eq_bar_width">20dp</dimen> - <dimen name="eq_label_text_size">9dp</dimen> + + <dimen name="eq_bar_width">24dp</dimen> + <dimen name="separator_width">4dp</dimen> + + <dimen name="knob_height">150dp</dimen> + <dimen name="knob_width">100dp</dimen> + <dimen name="knob_container_height_small">180dp</dimen> + <dimen name="knob_container_height_expanded">200dp</dimen> + <dimen name="drag_area_max">200dp</dimen> + <dimen name="knob_container_padding_bottom">8dp</dimen> + + <dimen name="radial_rect_padding">11dp</dimen> + <dimen name="radial_text_size">18sp</dimen> + <dimen name="radial_knob_stroke">22dp</dimen> + + <dimen name="action_bar_dts_switch_padding">48dp</dimen> + </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 143efa5..08a22fb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -15,17 +15,17 @@ --> <resources> - <string name="app_name">MusicFX</string> - <!-- ControlPanelMusic strings --> <string name="no_effects">Effects not available.</string> - <string name="eq_dialog_title">Equalizer</string> + <string name="main_toggle_effects_title">Audio effects</string> + <string name="eq_dialog_title">AudioFX</string> <string name="headset_plug">Plug in headphones for these effects.</string> <string name="bass_boost_strength">Bass boost</string> <string name="virtualizer_strength">Surround sound</string> + <string name="pr_title">Reverb:</string> + <string name="pr_summary">Additional room reverberation effects</string> + <string name="pr_dialog_title">Reverb preset</string> <string name="setup">Setup</string> - <string name="ci_extreme">FX booster</string> - <string name="user">User</string> <!-- title of control panel picker dialog [CHAR LIMIT=30] --> <string name="picker_title">Music effects control panel"</string> diff --git a/res/values/styles.xml b/res/values/styles.xml index 35b8f31..319ed7f 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -16,26 +16,28 @@ <resources> <style name="AppTheme" parent="android:Theme.Material"> - <item name="android:colorPrimary">@color/primary</item> - <item name="android:colorPrimaryDark">@color/primary_dark</item> - <item name="android:colorAccent">@color/accent</item> <item name="android:windowBackground">@color/window_background</item> </style> - <style name="Widget.Material.SeekBar.Vertical" parent="@android:Widget.Holo.SeekBar"> - <item name="android:indeterminateOnly">false</item> - <item name="android:progressDrawable">@drawable/scrubber_progress_vertical_holo_dark</item> - <item name="android:colorPrimary">@color/primary</item> - <item name="android:colorPrimaryDark">@color/primary_dark</item> - <item name="android:colorAccent">@color/accent</item> -<!-- not public <item name="android:indeterminateDrawable">@android:drawable/scrubber_progress_horizontal_holo_dark</item> --> - <item name="android:minWidth">33dip</item> - <item name="android:maxWidth">33dip</item> - <item name="android:thumb">@drawable/scrubber_control_selector_holo</item> - <item name="android:thumbOffset">16dip</item> - <item name="android:focusable">true</item> - <item name="android:paddingLeft">0dip</item> - <item name="android:paddingRight">0dip</item> - <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">16dip</item> + + <style name="AppThemeV2" parent="android:Theme.Material"> + <item name="android:actionBarStyle">@style/ActionBar</item> + </style> + + <style name="ActionBar" parent="android:Widget.Material.ActionBar.Solid"> + <item name="android:titleTextStyle">@style/AppThemeV2.ActionBar.TitleTextStyle</item> + <item name="android:subtitleTextStyle">@style/AppThemeV2.ActionBar.SubtitleTextStyle</item> + </style> + + <style name="AppThemeV2.ActionBar.TitleTextStyle" parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title"> + <item name="android:textColor">@color/white</item> + <item name="android:textSize">18sp</item> + <item name="android:fontFamily">sans-serif</item> + <item name="android:textStyle">bold</item> + </style> + + <style name="AppThemeV2.ActionBar.SubtitleTextStyle" parent="@android:style/TextAppearance.Material.Widget.ActionBar.Subtitle"> + <item name="android:textSize">10sp</item> + <item name="android:fontFamily">sans-serif</item> </style> + </resources> diff --git a/res/values/vpi__attrs.xml b/res/values/vpi__attrs.xml new file mode 100644 index 0000000..3115872 --- /dev/null +++ b/res/values/vpi__attrs.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 Jake Wharton + Copyright (C) 2011 Patrik Åkerfeldt + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <declare-styleable name="ViewPagerIndicator"> + <!-- Style of the circle indicator. --> + <attr name="vpiCirclePageIndicatorStyle" format="reference"/> + <!-- Style of the icon indicator's views. --> + <attr name="vpiIconPageIndicatorStyle" format="reference"/> + <!-- Style of the line indicator. --> + <attr name="vpiLinePageIndicatorStyle" format="reference"/> + <!-- Style of the title indicator. --> + <attr name="vpiTitlePageIndicatorStyle" format="reference"/> + <!-- Style of the tab indicator's tabs. --> + <attr name="vpiTabPageIndicatorStyle" format="reference"/> + <!-- Style of the underline indicator. --> + <attr name="vpiUnderlinePageIndicatorStyle" format="reference"/> + </declare-styleable> + + <attr name="centered" format="boolean" /> + <attr name="selectedColor" format="color" /> + <attr name="strokeWidth" format="dimension" /> + <attr name="unselectedColor" format="color" /> + + <declare-styleable name="CirclePageIndicator"> + <!-- Whether or not the indicators should be centered. --> + <attr name="centered" /> + <!-- Color of the filled circle that represents the current page. --> + <attr name="fillColor" format="color" /> + <!-- Color of the filled circles that represents pages. --> + <attr name="pageColor" format="color" /> + <!-- Orientation of the indicator. --> + <attr name="android:orientation"/> + <!-- Radius of the circles. This is also the spacing between circles. --> + <attr name="radius" format="dimension" /> + <!-- Whether or not the selected indicator snaps to the circles. --> + <attr name="snap" format="boolean" /> + <!-- Color of the open circles. --> + <attr name="strokeColor" format="color" /> + <!-- Width of the stroke used to draw the circles. --> + <attr name="strokeWidth" /> + <!-- View background --> + <attr name="android:background"/> + </declare-styleable> + + <declare-styleable name="LinePageIndicator"> + <!-- Whether or not the indicators should be centered. --> + <attr name="centered" /> + <!-- Color of the unselected lines that represent the pages. --> + <attr name="unselectedColor" /> + <!-- Color of the selected line that represents the current page. --> + <attr name="selectedColor" /> + <!-- Width of each indicator line. --> + <attr name="lineWidth" format="dimension" /> + <!-- Width of each indicator line's stroke. --> + <attr name="strokeWidth" /> + <!-- Width of the gap between each indicator line. --> + <attr name="gapWidth" format="dimension" /> + <!-- View background --> + <attr name="android:background"/> + </declare-styleable> + + <declare-styleable name="TitlePageIndicator"> + <!-- Screen edge padding. --> + <attr name="clipPadding" format="dimension" /> + <!-- Color of the footer line and indicator. --> + <attr name="footerColor" format="color" /> + <!-- Height of the footer line. --> + <attr name="footerLineHeight" format="dimension" /> + <!-- Style of the indicator. Default is triangle. --> + <attr name="footerIndicatorStyle"> + <enum name="none" value="0" /> + <enum name="triangle" value="1" /> + <enum name="underline" value="2" /> + </attr> + <!-- Height of the indicator above the footer line. --> + <attr name="footerIndicatorHeight" format="dimension" /> + <!-- Left and right padding of the underline indicator. --> + <attr name="footerIndicatorUnderlinePadding" format="dimension" /> + <!-- Padding between the bottom of the title and the footer. --> + <attr name="footerPadding" format="dimension" /> + <!-- Position of the line. --> + <attr name="linePosition"> + <enum name="bottom" value="0"/> + <enum name="top" value="1"/> + </attr> + <!-- Color of the selected title. --> + <attr name="selectedColor" /> + <!-- Whether or not the selected item is displayed as bold. --> + <attr name="selectedBold" format="boolean" /> + <!-- Color of regular titles. --> + <attr name="android:textColor" /> + <!-- Size of title text. --> + <attr name="android:textSize" /> + <!-- Padding between titles when bumping into each other. --> + <attr name="titlePadding" format="dimension" /> + <!-- Padding between titles and the top of the View. --> + <attr name="topPadding" format="dimension" /> + <!-- View background --> + <attr name="android:background"/> + </declare-styleable> + + <declare-styleable name="UnderlinePageIndicator"> + <!-- Whether or not the selected indicator fades. --> + <attr name="fades" format="boolean" /> + <!-- Length of the delay to fade the indicator. --> + <attr name="fadeDelay" format="integer" /> + <!-- Length of the indicator fade to transparent. --> + <attr name="fadeLength" format="integer" /> + <!-- Color of the selected line that represents the current page. --> + <attr name="selectedColor" /> + <!-- View background --> + <attr name="android:background"/> + </declare-styleable> +</resources>
\ No newline at end of file diff --git a/res/values/vpi__colors.xml b/res/values/vpi__colors.xml new file mode 100644 index 0000000..a61b75f --- /dev/null +++ b/res/values/vpi__colors.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 Jake Wharton + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <color name="vpi__background_holo_dark">#ff000000</color> + <color name="vpi__background_holo_light">#fff3f3f3</color> + <color name="vpi__bright_foreground_holo_dark">@color/vpi__background_holo_light</color> + <color name="vpi__bright_foreground_holo_light">@color/vpi__background_holo_dark</color> + <color name="vpi__bright_foreground_disabled_holo_dark">#ff4c4c4c</color> + <color name="vpi__bright_foreground_disabled_holo_light">#ffb2b2b2</color> + <color name="vpi__bright_foreground_inverse_holo_dark">@color/vpi__bright_foreground_holo_light</color> + <color name="vpi__bright_foreground_inverse_holo_light">@color/vpi__bright_foreground_holo_dark</color> +</resources>
\ No newline at end of file diff --git a/res/values/vpi__defaults.xml b/res/values/vpi__defaults.xml new file mode 100644 index 0000000..1a52917 --- /dev/null +++ b/res/values/vpi__defaults.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2012 Jake Wharton + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <bool name="default_circle_indicator_centered">true</bool> + <color name="default_circle_indicator_fill_color">#ffFFFFFF</color> + <color name="default_circle_indicator_page_color">#ffcccccc</color> + <integer name="default_circle_indicator_orientation">0</integer> + <dimen name="default_circle_indicator_radius">3dp</dimen> + <bool name="default_circle_indicator_snap">false</bool> + <color name="default_circle_indicator_stroke_color">#ffcccccc</color> + <dimen name="default_circle_indicator_stroke_width">0dp</dimen> + + <dimen name="default_line_indicator_line_width">12dp</dimen> + <dimen name="default_line_indicator_gap_width">4dp</dimen> + <dimen name="default_line_indicator_stroke_width">1dp</dimen> + <color name="default_line_indicator_selected_color">#FF33B5E5</color> + <color name="default_line_indicator_unselected_color">#FFBBBBBB</color> + <bool name="default_line_indicator_centered">true</bool> + + <dimen name="default_title_indicator_clip_padding">4dp</dimen> + <color name="default_title_indicator_footer_color">#FF33B5E5</color> + <dimen name="default_title_indicator_footer_line_height">2dp</dimen> + <integer name="default_title_indicator_footer_indicator_style">2</integer> + <dimen name="default_title_indicator_footer_indicator_height">4dp</dimen> + <dimen name="default_title_indicator_footer_indicator_underline_padding">20dp</dimen> + <dimen name="default_title_indicator_footer_padding">7dp</dimen> + <integer name="default_title_indicator_line_position">0</integer> + <color name="default_title_indicator_selected_color">#FFFFFFFF</color> + <bool name="default_title_indicator_selected_bold">true</bool> + <color name="default_title_indicator_text_color">#BBFFFFFF</color> + <dimen name="default_title_indicator_text_size">15dp</dimen> + <dimen name="default_title_indicator_title_padding">5dp</dimen> + <dimen name="default_title_indicator_top_padding">7dp</dimen> + + <bool name="default_underline_indicator_fades">true</bool> + <integer name="default_underline_indicator_fade_delay">300</integer> + <integer name="default_underline_indicator_fade_length">400</integer> + <color name="default_underline_indicator_selected_color">#FF33B5E5</color> +</resources>
\ No newline at end of file diff --git a/src/org/cyanogenmod/audiofx/ActivityMusic.java b/src/org/cyanogenmod/audiofx/ActivityMusic.java deleted file mode 100644 index 883b127..0000000 --- a/src/org/cyanogenmod/audiofx/ActivityMusic.java +++ /dev/null @@ -1,789 +0,0 @@ -/* - * Copyright (C) 2014 The CyanogenMod Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.cyanogenmod.audiofx; - -import android.app.ActionBar; -import android.app.Activity; -import android.content.*; -import android.graphics.drawable.ColorDrawable; -import android.media.audiofx.AudioEffect; -import android.media.audiofx.AudioEffect.Descriptor; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.*; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.CompoundButton.OnCheckedChangeListener; -import org.cyanogenmod.audiofx.widget.EqualizerSurface; -import org.cyanogenmod.audiofx.widget.Gallery; -import org.cyanogenmod.audiofx.widget.InterceptableLinearLayout; -import org.cyanogenmod.audiofx.widget.Knob; -import org.cyanogenmod.audiofx.widget.Knob.OnKnobChangeListener; - -import java.util.UUID; - -/** - * - */ -public class ActivityMusic extends Activity { - - private final static String TAG = "AudioFXActivityMusic"; - private final static boolean DEBUG = false; - - /** - * Max number of EQ bands supported - */ - private final static int EQUALIZER_MAX_BANDS = 32; - - /** - * Indicates if Virtualizer effect is supported. - */ - private boolean mVirtualizerSupported; - private boolean mVirtualizerIsHeadphoneOnly; - /** - * Indicates if BassBoost effect is supported. - */ - private boolean mBassBoostSupported; - /** - * Indicates if Equalizer effect is supported. - */ - private boolean mEqualizerSupported; - /** - * Indicates if Preset Reverb effect is supported. - */ - private boolean mPresetReverbSupported; - private ServiceConnection mServiceConnection; - - // Equalizer fields - private int mNumberEqualizerBands; - private int mEQCustomPresetPosition = 1; - private int mEQPreset; - private String[] mEQPresetNames; - private String[] mReverbPresetNames; - - private int mPRPreset; - - private boolean mEQAnimatingToUserPos = false; - - private ViewGroup mContentEffectsViewGroup; - private EqualizerSurface mEqualizerSurface; - private Gallery mEqGallery; - private Gallery mReverbGallery; - private Knob mVirtualizerKnob; - private Knob mBassKnob; - - private boolean mKnobsAvailable = false; - private Switch mToggleSwitch; - - private boolean mStandalone = false; - private boolean mStateChangeUpdate = false; - - private Toast mCurrentToast; - - HeadsetService mService; - - private String mCurrentDevice = "speaker"; // the sensible default - - private static final int[] mReverbPresetRSids = { - R.string.none, R.string.smallroom, R.string.mediumroom, R.string.largeroom, - R.string.mediumhall, R.string.largehall, R.string.plate - }; - - private Context mContext; - - private int mAudioSession = AudioEffect.ERROR_BAD_VALUE; - - private static final int MSG_UPDATE_EQ = 1; - private static final int MSG_UPDATE_SERVICE = 2; - private static final int MSG_UPDATE_EQ_ANIMATE = 3; - Handler mHandler = new Handler() { - - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - switch (msg.what) { - case MSG_UPDATE_EQ: - equalizerUpdateDisplayInternal(false); - break; - case MSG_UPDATE_SERVICE: - if (mService != null) { - mService.update(); - } - break; - case MSG_UPDATE_EQ_ANIMATE: - equalizerUpdateDisplayInternal(true); - break; - } - } - }; - - // Broadcast receiver to handle wired and Bluetooth A2dp headset events - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - final String action = intent.getAction(); - - if (action.equals(HeadsetService.ACTION_UPDATE_PREFERENCES)) { - if (mCurrentDeviceOverride == false) { // the user has selected a device, don't interrupt them. - if (mService != null) { - mCurrentDevice = mService.getAudioOutputRouting(); - } - } - - updateUI(true); - mStateChangeUpdate = true; - getActionBar().setSelectedNavigationItem(getCurrentDeviceIndex()); - equalizerSetPreset(mEQPreset); - equalizerUpdateDisplay(true); - } - } - }; - private ArrayAdapter<String> mNavBarDeviceAdapter; - - private boolean mCurrentDeviceOverride = false; - - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - startService(new Intent(this, HeadsetService.class)); - - // Init context to be used in listeners - mContext = this; - // Receive intent - // get calling intent - final Intent intent = getIntent(); - mAudioSession = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, - AudioEffect.ERROR_BAD_VALUE); - Log.v(TAG, "audio session: " + mAudioSession); - - // check for errors - if (getCallingPackage() == null) { - mStandalone = true; - } else { - mStandalone = false; - } - setResult(RESULT_OK); - - // query available effects - final Descriptor[] effects = AudioEffect.queryEffects(); - - // Determine available/supported effects - if (DEBUG) Log.v(TAG, "Available effects:"); - for (final Descriptor effect : effects) { - if (DEBUG) Log.v(TAG, effect.name.toString() + ", type: " + effect.type.toString()); - - if (effect.type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) { - mVirtualizerSupported = true; - if (effect.uuid.equals(UUID.fromString("1d4033c0-8557-11df-9f2d-0002a5d5c51b")) - || effect.uuid.equals(UUID.fromString("e6c98a16-22a3-11e2-b87b-f23c91aec05e")) - || effect.uuid.equals(UUID.fromString("d3467faa-acc7-4d34-acaf-0002a5d5c51b"))) { - mVirtualizerIsHeadphoneOnly = true; - } - } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) { - mBassBoostSupported = true; - } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) { - mEqualizerSupported = true; - } else if (effect.type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) { - mPresetReverbSupported = true; - } - } - - setContentView(R.layout.music_main); - - mContentEffectsViewGroup = (ViewGroup) findViewById(R.id.contentSoundEffects); - - // fix up labels - TextView reverbLabel = (TextView) findViewById(R.id.reverb_label); - reverbLabel.setText("- " + reverbLabel.getText() + " -"); - - TextView eqPresetLabel = (TextView) findViewById(R.id.eq_preset_label); - eqPresetLabel.setText("- " + eqPresetLabel.getText() + " -"); - - // setup actionbar on off switch - mToggleSwitch = new Switch(this); - mToggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() { - @Override - public void onCheckedChanged(final CompoundButton buttonView, - final boolean isChecked) { - // set parameter and state - getPrefs().edit().putBoolean("audiofx.global.enable", isChecked).apply(); - - updateUI(true); - setInterception(isChecked); - updateService(); - } - }); - - // setup action bar - String[] navigationBarDevices = new String[HeadsetService.DEFAULT_AUDIO_DEVICES.length]; - for (int i = 0; i < navigationBarDevices.length; i++) { - navigationBarDevices[i] = localizeDevice(HeadsetService.DEFAULT_AUDIO_DEVICES[i]); - } - - mNavBarDeviceAdapter = new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_spinner_dropdown_item, - navigationBarDevices); - ActionBar.OnNavigationListener navigationListener = new ActionBar.OnNavigationListener() { - @Override - public boolean onNavigationItemSelected(int itemPosition, long itemId) { - if (mStateChangeUpdate) { - mStateChangeUpdate = false; - } else { - mCurrentDeviceOverride = true; - mCurrentDevice = HeadsetService.DEFAULT_AUDIO_DEVICES[itemPosition]; - } - updateUI(true); - equalizerSetPreset(mEQPreset); - equalizerUpdateDisplay(true); - mBassKnob.setValue(Integer.valueOf(getPrefs().getString("audiofx.bass.strength", "0"))); - mVirtualizerKnob.setValue(Integer.valueOf(getPrefs().getString("audiofx.virtualizer.strength", "0"))); - return true; - } - }; - - ActionBar ab = getActionBar(); - final ActionBar.LayoutParams params = new ActionBar.LayoutParams( - ActionBar.LayoutParams.WRAP_CONTENT, - ActionBar.LayoutParams.WRAP_CONTENT, - Gravity.CENTER_VERTICAL | Gravity.END); - - ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); - ab.setListNavigationCallbacks(mNavBarDeviceAdapter, navigationListener); - ab.setBackgroundDrawable(new ColorDrawable(getResources() - .getColor(R.color.action_bar_background))); - mStateChangeUpdate = true; - ab.setSelectedNavigationItem(getCurrentDeviceIndex()); - - ab.setCustomView(mToggleSwitch, params); - ab.setDisplayShowTitleEnabled(false); - ab.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); - - // initialize views - mEqualizerSurface = (EqualizerSurface) findViewById(R.id.frequencyResponse); - mEqGallery = (Gallery) findViewById(R.id.eqPresets); - mReverbGallery = (Gallery) findViewById(R.id.reverb_gallery); - mVirtualizerKnob = (Knob) findViewById(R.id.vIStrengthKnob); - mBassKnob = (Knob) findViewById(R.id.bBStrengthKnob); - - // setup equalizer presets - final int numPresets = Integer.parseInt(getSharedPreferences("global", 0) - .getString("equalizer.number_of_presets", "0")); - mEQPresetNames = new String[numPresets + 3]; - - String[] presetNames = getSharedPreferences("global", 0).getString("equalizer.preset_names", "").split("\\|"); - for (short i = 0; i < numPresets; i++) { - mEQPresetNames[i] = localizePresetName(presetNames[i]); - } - mEQPresetNames[numPresets] = getString(R.string.electronic); - mEQPresetNames[numPresets + 1] = getString(R.string.small_speakers); - mEQPresetNames[numPresets + 2] = getString(R.string.custom); - mEQCustomPresetPosition = numPresets + 2; - - ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.equalizer_presets, - mEQPresetNames); - - mEqGallery.setAdapter(adapter); - mEqGallery.setSelection(mEQPreset); - mEqGallery.setOnItemSelectedListener(new Gallery.OnItemSelectedListener() { - @Override - public void onItemSelected(int position) { - mEQPreset = position; - if (!mEQAnimatingToUserPos) { - equalizerSetPreset(position); - } else if (mEQAnimatingToUserPos && mEQPreset == mEQCustomPresetPosition) { - mEQAnimatingToUserPos = false; - } - } - }); - - // setup equalizer - mNumberEqualizerBands = Integer.parseInt(getSharedPreferences("global", 0) - .getString("equalizer.number_of_bands", "5")); - final int[] centerFreqs = getCenterFreqs(); - final int[] bandLevelRange = getBandLevelRange(); - float[] centerFreqsKHz = new float[centerFreqs.length]; - for (int i = 0; i < centerFreqs.length; i++) { - centerFreqsKHz[i] = (float) centerFreqs[i] / 1000.0f; - } - mEqualizerSurface.setCenterFreqs(centerFreqsKHz); - mEqualizerSurface.setBandLevelRange(bandLevelRange[0] / 100, bandLevelRange[1] / 100); - final EqualizerSurface.BandUpdatedListener listener = new EqualizerSurface.BandUpdatedListener() { - - @Override - public void onBandUpdated(int band, float dB) { - if (mEQPreset != mEQCustomPresetPosition && !mEQAnimatingToUserPos) { - equalizerCopyToCustom(); - mEQAnimatingToUserPos = true; - mEqGallery.setAnimationDuration(1000); - mEqGallery.setSelection(mEQCustomPresetPosition, true); - } else { - equalizerBandUpdate(band, (int) (dB * 100)); - } - } - - float[] animatingLevels; - - @Override - public void onBandAnimating(int band, float dB) { - if (animatingLevels == null) { - animatingLevels = mEqualizerSurface.softCopyLevels(); - } - animatingLevels[band] = dB; - if (mService != null) { - mService.setEqualizerLevels(animatingLevels); - } - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - - @Override - public void onBandAnimationCompleted() { - if (mService != null) { - mService.setEqualizerLevels(animatingLevels = null); - } - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - }; - mEqualizerSurface.registerBandUpdatedListener(listener); - - // setup virtualizer knob - mVirtualizerKnob.setMax(OpenSLESConstants.VIRTUALIZER_MAX_STRENGTH - - OpenSLESConstants.VIRTUALIZER_MIN_STRENGTH); - mVirtualizerKnob.setOnKnobChangeListener(new OnKnobChangeListener() { - // Update the parameters while Knob changes and set the - // effect parameter. - @Override - public void onValueChanged(final Knob knob, final int value, - final boolean fromUser) { - if (fromUser) { - // set parameter and state - getPrefs().edit().putBoolean("audiofx.virtualizer.enable", true).apply(); - getPrefs().edit().putString("audiofx.virtualizer.strength", String.valueOf(value)).apply(); - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - } - - @Override - public boolean onSwitchChanged(final Knob knob, boolean on) { - if (!mKnobsAvailable) { - showHeadsetMsg(); - return false; - } -// knob.setOn(getPrefs().getBoolean("audiofx.virtualizer.enable", true)); -// knob.setOn(on); - return true; - } - - @Override - public void onAnimationFinished(boolean endValue) { - getPrefs().edit().putBoolean("audiofx.virtualizer.enable", endValue).apply(); -// updateService(); - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - }); - - // setup bass knob - mBassKnob.setMax(OpenSLESConstants.BASSBOOST_MAX_STRENGTH - - OpenSLESConstants.BASSBOOST_MIN_STRENGTH); - mBassKnob.setOnKnobChangeListener(new OnKnobChangeListener() { - // Update the parameters while SeekBar changes and set the - // effect parameter. - @Override - public void onValueChanged(final Knob knob, final int value, - final boolean fromUser) { - if (fromUser) { - // set parameter and state - getPrefs().edit().putBoolean("audiofx.bass.enable", true).apply(); - getPrefs().edit().putString("audiofx.bass.strength", String.valueOf(value)).apply(); - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - } - - @Override - public boolean onSwitchChanged(final Knob knob, boolean on) { - if (!mKnobsAvailable) { - showHeadsetMsg(); - return false; - } -// knob.setOn(on); - return true; - } - - @Override - public void onAnimationFinished(boolean endValue) { - getPrefs().edit().putBoolean("audiofx.bass.enable", endValue).apply(); -// updateService(); - mHandler.sendEmptyMessage(MSG_UPDATE_SERVICE); - } - }); - - - // setup reverb presets - mReverbPresetNames = new String[mReverbPresetRSids.length]; - for (short i = 0; i < mReverbPresetRSids.length; ++i) { - mReverbPresetNames[i] = getString(mReverbPresetRSids[i]); - } - - ArrayAdapter<String> reverbAdapter = new ArrayAdapter<String>(this, - R.layout.equalizer_presets, mReverbPresetNames); - mReverbGallery.setAdapter(reverbAdapter); - mReverbGallery.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - if (position != mPRPreset) { - presetReverbSetPreset(position); - } - mPRPreset = position; - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - } - }); - mReverbGallery.setSelection(mPRPreset); - - - } - - private SharedPreferences getPrefs() { - return getSharedPreferences(mCurrentDevice, 0); - } - - private final String localizePresetName(final String name) { - final String[] names = { - "Normal", "Classical", "Dance", "Flat", "Folk", - "Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock" - }; - final int[] ids = { - R.string.normal, R.string.classical, R.string.dance, R.string.flat, R.string.folk, - R.string.heavy_metal, R.string.hip_hop, R.string.jazz, R.string.pop, R.string.rock - }; - - for (int i = names.length - 1; i >= 0; --i) { - if (names[i].equals(name)) { - return getString(ids[i]); - } - } - return name; - } - - private final String localizeDevice(String device) { - return getString(mContext.getResources().getIdentifier("device_" + device, "string", getPackageName())); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - updateUI(false); - } - - @Override - protected void onResume() { - super.onResume(); - - if (mServiceConnection == null) { - mServiceConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder binder) { - mService = ((HeadsetService.LocalBinder) binder).getService(); - if (!mCurrentDeviceOverride) { - mCurrentDevice = mService.getAudioOutputRouting(); - } - updateUI(true); - - mStateChangeUpdate = true; - getActionBar().setSelectedNavigationItem(getCurrentDeviceIndex()); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mService = null; - } - }; - } - Intent serviceIntent = new Intent(this, HeadsetService.class); - bindService(serviceIntent, mServiceConnection, 0); - - final IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(HeadsetService.ACTION_UPDATE_PREFERENCES); - registerReceiver(mReceiver, intentFilter); - - equalizerUpdateDisplay(true); - } - - @Override - protected void onPause() { - super.onPause(); - - // clear the toast - if (mCurrentToast != null) { - mCurrentToast.cancel(); - mCurrentToast = null; - } - - unbindService(mServiceConnection); - - // Unregister for broadcast intents. (These affect the visible UI, - // so we only care about them while we're in the foreground.) - unregisterReceiver(mReceiver); - } - - private void updateUI(boolean fromNavbar) { - if (!fromNavbar) { - mStateChangeUpdate = true; - getActionBar().setSelectedNavigationItem(getCurrentDeviceIndex()); - } - - boolean isSpeaker = mCurrentDevice.equals("speaker"); - - final boolean isEnabled = getPrefs().getBoolean("audiofx.global.enable", isSpeaker); - mKnobsAvailable = !isSpeaker; - - mToggleSwitch.setChecked(isEnabled); - - if (mVirtualizerSupported) { - mVirtualizerKnob.setOn(getPrefs().getBoolean("audiofx.virtualizer.enable", false), false); - mVirtualizerKnob.setEnabled(isEnabled && mKnobsAvailable); - } else { - mVirtualizerKnob.setVisibility(View.GONE); - } - if (mBassBoostSupported) { - mBassKnob.setOn(getPrefs().getBoolean("audiofx.bass.enable", true), false); - mBassKnob.setEnabled(isEnabled && mKnobsAvailable); - } else { - mBassKnob.setVisibility(View.GONE); - } - if (mEqualizerSupported) { - String preset; - if (isSpeaker) { - preset = String.valueOf(mNumberEqualizerBands + 2); - } else { - preset = "3"; - } - mEQPreset = Integer.valueOf(getPrefs().getString("audiofx.eq.preset", preset)); - mEqGallery.setEnabled(isEnabled); - mEqGallery.setSelection(mEQPreset); - } - if (mPresetReverbSupported) { - mPRPreset = Integer.valueOf(getPrefs().getString("audiofx.reverb.preset", "0")); - mReverbGallery.setSelection(mPRPreset, true); - mReverbGallery.setEnabled(isEnabled); - } - - setInterception(isEnabled); - } - - private void updateUI() { - updateUI(false); - } - - private int getCurrentDeviceIndex() { - for (int i = 0; i < HeadsetService.DEFAULT_AUDIO_DEVICES.length; i++) { - if (HeadsetService.DEFAULT_AUDIO_DEVICES[i].equals(mCurrentDevice)) { - return i; - } - } - return 0; - } - - private void setInterception(boolean isEnabled) { - final InterceptableLinearLayout ill = - (InterceptableLinearLayout) findViewById(R.id.contentSoundEffects); - ill.setInterception(!isEnabled); - if (isEnabled) { - ill.setOnClickListener(null); - } else { - ill.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // clear the toast - if (mCurrentToast != null) { - mCurrentToast.cancel(); - mCurrentToast = null; - } - mCurrentToast = Toast.makeText(mContext, - getString(R.string.power_on_prompt), Toast.LENGTH_SHORT); - mCurrentToast.setGravity(Gravity.CENTER, 0, 0); - mCurrentToast.show(); - } - }); - } - } - - private int[] getBandLevelRange() { - String savedCenterFreqs = getSharedPreferences("global", 0).getString("equalizer.band_level_range", null); - if (savedCenterFreqs == null || savedCenterFreqs.isEmpty()) { - return new int[]{-1500, 1500}; - } else { - String[] split = savedCenterFreqs.split(";"); - int[] freqs = new int[split.length]; - for (int i = 0; i < split.length; i++) { - freqs[i] = Integer.valueOf(split[i]); - } - return freqs; - } - } - - private int[] getCenterFreqs() { - String savedCenterFreqs = getSharedPreferences("global", 0).getString("equalizer.center_freqs", - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)); - String[] split = savedCenterFreqs.split(";"); - int[] freqs = new int[split.length]; - for (int i = 0; i < split.length; i++) { - freqs[i] = Integer.valueOf(split[i]); - } - return freqs; - } - - /** - * Updates the EQ by getting the parameters. - */ - private void equalizerUpdateDisplay(boolean animate) { - mHandler.removeMessages(animate ? MSG_UPDATE_EQ_ANIMATE : MSG_UPDATE_EQ); - mHandler.sendEmptyMessageDelayed(animate ? MSG_UPDATE_EQ_ANIMATE : MSG_UPDATE_EQ, 100); - } - - private void equalizerUpdateDisplayInternal(boolean animate) { - String levelsString = null; - float[] floats; - - if (mEQPreset == mEQCustomPresetPosition) { - // load custom preset for current device - // here mEQValues needs to be pre-populated with the user's preset values. - String[] customEq = getPrefs().getString("audiofx.eq.bandlevels.custom", - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)).split(";"); - floats = new float[mNumberEqualizerBands]; - for (int band = 0; band < floats.length; band++) { - final float level = Float.parseFloat(customEq[band]); - floats[band] = level / 100.0f; - } - if (animate) { - mEqualizerSurface.setBands(floats); - } else { - for (int band = 0; band < mNumberEqualizerBands; band++) { - mEqualizerSurface.setBand(band, (float) floats[band] / 100.0f); - } - } - } else { - // try to load preset - levelsString = getSharedPreferences("global", 0).getString("equalizer.preset." + mEQPreset, - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)); - String[] bandLevels = levelsString.split(";"); - floats = new float[bandLevels.length]; - for (int band = 0; band < bandLevels.length; band++) { - final float level = Float.parseFloat(bandLevels[band]); - floats[band] = level / 100.0f; - if (!animate) { - mEqualizerSurface.setBand(band, (float) level / 100.0f); - } - } - if (animate) { - mEqualizerSurface.setBands(floats); - } - } - } - - /** - * Called when user starts touch eq on a preset - */ - private void equalizerCopyToCustom() { - if (DEBUG) Log.d(TAG, "equalizerCopyToCustom()"); - StringBuilder bandLevels = new StringBuilder(); - for (int band = 0; band < mNumberEqualizerBands; band++) { - final float level = mEqualizerSurface.getBand(band); - bandLevels.append(level * 100); - bandLevels.append(";"); - } - // remove trailing ";" - bandLevels.deleteCharAt(bandLevels.length() - 1); - getPrefs().edit().putString("audiofx.eq.bandlevels.custom", bandLevels.toString()).apply(); - getPrefs().edit().putString("audiofx.eq.preset", String.valueOf(mEQCustomPresetPosition)).apply(); - } - - private void equalizerBandUpdate(final int band, final int level) { - if (DEBUG) Log.d(TAG, "equalizerBandUpdate(band: " + band + ", level: " + level + ")"); - - String[] currentCustomLevels = getPrefs().getString("audiofx.eq.bandlevels.custom", - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)).split(";"); - - currentCustomLevels[band] = String.valueOf(level); - // save - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < mNumberEqualizerBands; i++) { - builder.append(currentCustomLevels[i]); - builder.append(";"); - } - builder.deleteCharAt(builder.length() - 1); - getPrefs().edit().putString("audiofx.eq.bandlevels", builder.toString()).apply(); - getPrefs().edit().putString("audiofx.eq.bandlevels.custom", builder.toString()).apply(); - - updateService(); - } - - private void updateService() { - mHandler.removeMessages(MSG_UPDATE_SERVICE); - mHandler.sendEmptyMessageDelayed(MSG_UPDATE_SERVICE, 100); - } - - private void equalizerSetPreset(final int preset) { - if (DEBUG) Log.d(TAG, "equalizerSetPreset(" + preset + ")"); - - mEQPreset = preset; - getPrefs().edit().putString("audiofx.eq.preset", String.valueOf(preset)).apply(); - - String newLevels = null; - if (preset == mEQCustomPresetPosition) { - // load custom if possible - newLevels = getPrefs().getString("audiofx.eq.bandlevels.custom", - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)); - } else { - newLevels = getSharedPreferences("global", 0).getString("equalizer.preset." + preset, - HeadsetService.getZeroedBandsString(mNumberEqualizerBands)); - } - getPrefs().edit().putString("audiofx.eq.bandlevels", newLevels).apply(); - equalizerUpdateDisplay(true); - - updateService(); - } - - - private void presetReverbSetPreset(final int preset) { - getPrefs().edit().putString("audiofx.reverb.preset", String.valueOf(preset)).apply(); - updateService(); - } - - private void showHeadsetMsg() { - // clear the toast - if (mCurrentToast != null) { - mCurrentToast.cancel(); - mCurrentToast = null; - } - - final Context context = getApplicationContext(); - final int duration = Toast.LENGTH_SHORT; - - mCurrentToast = Toast.makeText(context, getString(R.string.effect_unavalable_for_speaker), duration); - mCurrentToast.setGravity(Gravity.CENTER, mCurrentToast.getXOffset() / 2, mCurrentToast.getYOffset() / 2); - mCurrentToast.show(); - } - -} diff --git a/src/org/cyanogenmod/audiofx/BootReceiver.java b/src/org/cyanogenmod/audiofx/BootReceiver.java deleted file mode 100644 index b032310..0000000 --- a/src/org/cyanogenmod/audiofx/BootReceiver.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.cyanogenmod.audiofx; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -/** - * Created by roman on 5/12/14. - */ -public class BootReceiver extends BroadcastReceiver { - public void onReceive(Context context, Intent intent) { - context.startService(new Intent(context, HeadsetService.class)); - } -} diff --git a/src/org/cyanogenmod/audiofx/HeadsetService.java b/src/org/cyanogenmod/audiofx/HeadsetService.java deleted file mode 100644 index 98bf20e..0000000 --- a/src/org/cyanogenmod/audiofx/HeadsetService.java +++ /dev/null @@ -1,708 +0,0 @@ -/* - * Copyright (C) 2014 The CyanogenMod Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.cyanogenmod.audiofx; - -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.media.AudioManager; -import android.media.AudioPatch; -import android.media.AudioPort; -import android.media.AudioSystem; -import android.media.audiofx.AudioEffect; -import android.media.audiofx.BassBoost; -import android.media.audiofx.Equalizer; -import android.media.audiofx.PresetReverb; -import android.media.audiofx.Virtualizer; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; -import android.util.SparseArray; - -import java.util.Arrays; - -import cyanogenmod.media.AudioSessionInfo; -import cyanogenmod.media.CMAudioManager; - -/** - * <p>This calls listen to events that affect DSP function and responds to them.</p> - * <ol> - * <li>new audio session declarations</li> - * <li>headset plug / unplug events</li> - * <li>preference update events.</li> - * </ol> - * - * @author alankila - */ -public class HeadsetService extends Service { - - public static final String ACTION_UPDATE_PREFERENCES = "org.cyanogenmod.audiofx.UPDATE_PREFS"; - public static final String[] DEFAULT_AUDIO_DEVICES = new String[]{ - "headset", "speaker", "usb", "bluetooth", "wireless", "lineout" - }; - - static String getZeroedBandsString(int length) { - StringBuffer buff = new StringBuffer(); - for (int i = 0; i < length; i++) { - buff.append("0;"); - } - buff.deleteCharAt(buff.length() - 1); - return buff.toString(); - } - - /** - * Helper class representing the full complement of effects attached to one - * audio session. - * - * @author alankila - */ - private static class EffectSet { - /** - * Session-specific equalizer - */ - private final Equalizer mEqualizer; - /** - * Session-specific bassboost - */ - private final BassBoost mBassBoost; - /** - * Session-specific virtualizer - */ - private final Virtualizer mVirtualizer; - - private final PresetReverb mPresetReverb; - - private short mEqNumPresets = -1; - private short mEqNumBands = -1; - - public EffectSet(int sessionId) { - mEqualizer = new Equalizer(0, sessionId); - mBassBoost = new BassBoost(0, sessionId); - mVirtualizer = new Virtualizer(0, sessionId); - mPresetReverb = new PresetReverb(0, sessionId); - } - - /* - * Take lots of care to not poke values that don't need - * to be poked- this can cause audible pops. - */ - - public void enableEqualizer(boolean enable) { - if (enable != mEqualizer.getEnabled()) { - if (!enable) { - for (short i = 0; i < getNumEqualizerBands(); i++) { - mEqualizer.setBandLevel(i, (short) 0); - } - } - mEqualizer.setEnabled(enable); - } - } - - public void setEqualizerLevels(short[] levels) { - if (mEqualizer.getEnabled()) { - for (short i = 0; i < levels.length; i++) { - if (mEqualizer.getBandLevel(i) != levels[i]) { - mEqualizer.setBandLevel(i, levels[i]); - } - } - } - } - - public short getNumEqualizerBands() { - if (mEqNumBands < 0) { - mEqNumBands = mEqualizer.getNumberOfBands(); - } - return mEqNumBands; - } - - public short getNumEqualizerPresets() { - if (mEqNumPresets < 0) { - mEqNumPresets = mEqualizer.getNumberOfPresets(); - } - return mEqNumPresets; - } - - public void enableBassBoost(boolean enable) { - if (enable != mBassBoost.getEnabled()) { - if (!enable) { - mBassBoost.setStrength((short) 1); - mBassBoost.setStrength((short) 0); - } - mBassBoost.setEnabled(enable); - } - } - - public void setBassBoostStrength(short strength) { - if (mBassBoost.getEnabled() && mBassBoost.getRoundedStrength() != strength) { - mBassBoost.setStrength(strength); - } - } - - public void enableVirtualizer(boolean enable) { - if (enable != mVirtualizer.getEnabled()) { - if (!enable) { - mVirtualizer.setStrength((short) 1); - mVirtualizer.setStrength((short) 0); - } - mVirtualizer.setEnabled(enable); - } - } - - public void setVirtualizerStrength(short strength) { - if (mVirtualizer.getEnabled() && mVirtualizer.getRoundedStrength() != strength) { - mVirtualizer.setStrength(strength); - } - } - - public void enableReverb(boolean enable) { - if (enable != mPresetReverb.getEnabled()) { - if (!enable) { - mPresetReverb.setPreset((short) 0); - } - mPresetReverb.setEnabled(enable); - } - } - - public void setReverbPreset(short preset) { - if (mPresetReverb.getEnabled() && mPresetReverb.getPreset() != preset) { - mPresetReverb.setPreset(preset); - } - } - - public void release() { - mEqualizer.release(); - mBassBoost.release(); - mVirtualizer.release(); - mPresetReverb.release(); - } - } - - protected static final String TAG = HeadsetService.class.getSimpleName(); - public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - - private void addSession(int sessionId) { - if (sessionId == 0) { - return; - } - if (DEBUG) Log.i(TAG, String.format("New audio session: %d", sessionId)); - - synchronized (mAudioSessionsL) { - if (mAudioSessionsL.indexOfKey(sessionId) < 0) { - mAudioSessionsL.put(sessionId, new EffectSet(sessionId)); - } - updateLocked(); - } - } - - private void removeSession(int sessionId) { - if (sessionId == 0) { - return; - } - if (DEBUG) Log.i(TAG, String.format("Audio session removed: %d", sessionId)); - - synchronized (mAudioSessionsL) { - EffectSet gone = mAudioSessionsL.removeReturnOld(sessionId); - if (gone != null) { - gone.release(); - } - } - } - - public void addSession(AudioSessionInfo info) { - if (info.getStream() == AudioManager.STREAM_MUSIC && - (info.getFlags() < 0 || (info.getFlags() & 0x8) > 0 || (info.getFlags() & 0x10) > 0) && - (info.getChannelMask() < 0 || info.getChannelMask() > 1)) { - - // Never auto-attach is someone is recording! We don't want to - // interfere with any sort of - // loopback mechanisms. - final boolean recording = AudioSystem.isSourceActive(0) - || AudioSystem.isSourceActive(6); - if (recording) { - Log.w(TAG, "Recording in progress, not performing auto-attach!"); - return; - } - addSession(info.getSessionId()); - } - } - - public void removeSession(AudioSessionInfo info) { - if (info.getStream() == AudioManager.STREAM_MUSIC) { - removeSession(info.getSessionId()); - } - } - - public class LocalBinder extends Binder { - public HeadsetService getService() { - return HeadsetService.this; - } - } - - private final LocalBinder mBinder = new LocalBinder(); - - /** - * Known audio sessions and their associated audioeffect suites. - */ - private final SparseArray<EffectSet> mAudioSessionsL = new SparseArray<EffectSet>(); - - AudioPortListener mAudioPortListener; - - /** - * Has DSPManager assumed control of equalizer levels? - */ - private float[] mOverriddenEqualizerLevels; - - /** - * Update audio parameters when preferences have been updated. - */ - private final BroadcastReceiver mPreferenceUpdateReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "Preferences updated."); - update(); - } - }; - - private class AudioPortListener implements AudioManager.OnAudioPortUpdateListener { - private boolean mUseBluetooth; - private boolean mUseHeadset; - private boolean mUseUSB; - private boolean mUseWifiDisplay; - private boolean mUseSpeaker; - private boolean mUseLineOut; - - private final Context mContext; - - public AudioPortListener(Context context) { - mContext = context; - } - - @Override - public void onAudioPortListUpdate(AudioPort[] portList) { - final boolean prevUseHeadset = mUseHeadset; - final boolean prevUseBluetooth = mUseBluetooth; - final boolean prevUseUSB = mUseUSB; - final boolean prevUseWireless = mUseWifiDisplay; - final boolean prevUseSpeaker = mUseSpeaker; - final boolean prevUseLineOut = mUseLineOut; - - AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - int device = am.getDevicesForStream(AudioManager.STREAM_MUSIC); - mUseBluetooth = (device & AudioManager.DEVICE_OUT_BLUETOOTH_A2DP) != 0 - || (device & AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) != 0 - || (device & AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) != 0 - || (device & AudioManager.DEVICE_OUT_BLUETOOTH_SCO) != 0 - || (device & AudioManager.DEVICE_OUT_BLUETOOTH_SCO_CARKIT) != 0 - || (device & AudioManager.DEVICE_OUT_BLUETOOTH_SCO_HEADSET) != 0; - - mUseHeadset = (device & AudioManager.DEVICE_OUT_WIRED_HEADPHONE) != 0 - || (device & AudioManager.DEVICE_OUT_WIRED_HEADSET) != 0; - - mUseLineOut = (device & AudioManager.DEVICE_OUT_LINE) != 0; - - mUseUSB = (device & AudioManager.DEVICE_OUT_USB_ACCESSORY) != 0 - || (device & AudioManager.DEVICE_OUT_USB_DEVICE) != 0; - - mUseWifiDisplay = false; //TODO add support for wireless display.. - - mUseSpeaker = (device & AudioManager.DEVICE_OUT_SPEAKER) != 0; - - Log.i(TAG, "Headset=" + mUseHeadset + "; Bluetooth=" - + mUseBluetooth + " ; USB=" + mUseUSB + "; Speaker=" + mUseSpeaker + - "; Line out=" + mUseLineOut); - - if (prevUseHeadset != mUseHeadset - || prevUseBluetooth != mUseBluetooth - || prevUseUSB != mUseUSB - || prevUseWireless != mUseWifiDisplay - || prevUseSpeaker != mUseSpeaker - || prevUseLineOut != mUseLineOut) { - - update(); - - Intent i = new Intent(ACTION_UPDATE_PREFERENCES); - mContext.sendBroadcast(i); - } - } - - @Override - public void onAudioPatchListUpdate(AudioPatch[] patchList) { - - } - - @Override - public void onServiceDied() { - - } - - public String getInternalAudioOutputRouting() { - if (mUseSpeaker) { - return "speaker"; - } - if (mUseBluetooth) { - return "bluetooth"; - } - if (mUseHeadset) { - return "headset"; - } - if (mUseUSB) { - return "usb"; - } - if (mUseWifiDisplay) { - return "wireless"; - } - if (mUseLineOut) { - return "lineout"; - } - return "speaker"; - } - } - - @Override - public void onCreate() { - super.onCreate(); - Log.i(TAG, "Starting service."); - - registerReceiver(mPreferenceUpdateReceiver, - new IntentFilter(ACTION_UPDATE_PREFERENCES)); - - AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - am.registerAudioPortUpdateListener(mAudioPortListener = new AudioPortListener(this)); - - saveDefaults(); - } - - - /** - * maps {@link AudioEffect#EXTRA_CONTENT_TYPE} to an AudioManager.STREAM_* item - */ - private static int mapContentTypeToStream(int contentType) { - switch (contentType) { - case AudioEffect.CONTENT_TYPE_VOICE: - return AudioManager.STREAM_VOICE_CALL; - case AudioEffect.CONTENT_TYPE_GAME: - // explicitly don't support game effects right now - return -1; - case AudioEffect.CONTENT_TYPE_MOVIE: - case AudioEffect.CONTENT_TYPE_MUSIC: - default: - return AudioManager.STREAM_MUSIC; - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (intent != null && intent.getAction() != null) { - if (DEBUG) { - Log.i(TAG, "onStartCommand() called with " + "intent = [" + intent + "], flags = [" - + flags + "], startId = [" + startId + "], extras = [" + - (intent.getExtras() == null ? "null" : intent.getExtras().toString()) - + "]"); - } - String action = intent.getAction(); - int sessionId = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, 0); - String pkg = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME); - int stream = mapContentTypeToStream( - intent.getIntExtra(AudioEffect.EXTRA_CONTENT_TYPE, - AudioEffect.CONTENT_TYPE_MUSIC)); - - if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) { - if (DEBUG) { - Log.i(TAG, String.format("New audio session: %d package: %s contentType=%d", - sessionId, pkg, stream)); - } - addSession(sessionId); - - } else if (action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) { - - removeSession(sessionId); - - } else if (action.equals(CMAudioManager.ACTION_AUDIO_SESSIONS_CHANGED)) { - - final AudioSessionInfo info = (AudioSessionInfo) intent.getParcelableExtra( - CMAudioManager.EXTRA_SESSION_INFO); - if (info != null && info.getSessionId() > 0) { - boolean added = intent.getBooleanExtra(CMAudioManager.EXTRA_SESSION_ADDED, - false); - if (added) { - addSession(info); - } else { - removeSession(info); - } - } - } - } - return START_STICKY; - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.i(TAG, "Stopping service."); - - unregisterReceiver(mPreferenceUpdateReceiver); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - /** - * Gain temporary control over the global equalizer. - * Used by DSPManager when testing a new equalizer setting. - * - * @param levels - */ - public void setEqualizerLevels(float[] levels) { - mOverriddenEqualizerLevels = levels; - update(); - } - - /** - * There appears to be no way to find out what the current actual audio routing is. - * For instance, if a wired headset is plugged in, the following objects/classes are involved: - * </p> - * <ol> - * <li>wiredaccessoryobserver</li> - * <li>audioservice</li> - * <li>audiosystem</li> - * <li>audiopolicyservice</li> - * <li>audiopolicymanager</li> - * </ol> - * <p>Once the decision of new routing has been made by the policy manager, it is relayed to - * audiopolicyservice, which waits for some time to let application buffers drain, and then - * informs it to hardware. The full chain is:</p> - * <ol> - * <li>audiopolicymanager</li> - * <li>audiopolicyservice</li> - * <li>audiosystem</li> - * <li>audioflinger</li> - * <li>audioeffect (if any)</li> - * </ol> - * <p>However, the decision does not appear to be relayed to java layer, so we must - * make a guess about what the audio output routing is.</p> - * - * @return string token that identifies configuration to use - */ - public String getAudioOutputRouting() { - if (mAudioPortListener != null) { - return mAudioPortListener.getInternalAudioOutputRouting(); - } - return "speaker"; - } - - public EffectSet getEffects(int session) { - synchronized (mAudioSessionsL) { - return mAudioSessionsL.get(session); - } - } - - private void saveDefaults() { - EffectSet temp; - try { - temp = new EffectSet(0); - } catch (Exception e) { - // this is really bad- likely the media stack is broken. - // disable ourself if we get into this state, as the service - // will restart itself repeatedly! - Log.e(TAG, e.getMessage(), e); - stopSelf(); - return; - } - - SharedPreferences prefs = getSharedPreferences("global", 0); - - final int numBands = temp.getNumEqualizerBands(); - final int numPresets = temp.getNumEqualizerPresets(); - SharedPreferences.Editor editor = prefs.edit(); - editor.putString("equalizer.number_of_presets", String.valueOf(numPresets)).apply(); - editor.putString("equalizer.number_of_bands", String.valueOf(numBands)).apply(); - - // range - short[] rangeShortArr = temp.mEqualizer.getBandLevelRange(); - - - editor.putString("equalizer.band_level_range", rangeShortArr[0] + ";" + rangeShortArr[1]).apply(); - - // center freqs - StringBuilder centerFreqs = new StringBuilder(); - // audiofx.global.centerfreqs - for (short i = 0; i < numBands; i++) { - centerFreqs.append(temp.mEqualizer.getCenterFreq(i)); - centerFreqs.append(";"); - - } - centerFreqs.deleteCharAt(centerFreqs.length() - 1); - editor.putString("equalizer.center_freqs", centerFreqs.toString()).apply(); - - // populate preset names - StringBuilder presetNames = new StringBuilder(); - for (int i = 0; i < numPresets; i++) { - String presetName = temp.mEqualizer.getPresetName((short) i); - presetNames.append(presetName); - presetNames.append("|"); - - // populate preset band values - StringBuilder presetBands = new StringBuilder(); - temp.mEqualizer.usePreset((short) i); - - for (int j = 0; j < numBands; j++) { - // loop through preset bands - presetBands.append(temp.mEqualizer.getBandLevel((short) j)); - presetBands.append(";"); - } - presetBands.deleteCharAt(presetBands.length() - 1); - editor.putString("equalizer.preset." + i, presetBands.toString()).apply(); - } - presetNames.deleteCharAt(presetNames.length() - 1); - editor.putString("equalizer.preset_names", presetNames.toString()).apply(); - temp.release(); - - // add ci-extreme - StringBuilder ciExtremeBuilder = new StringBuilder("0;800;400;100;1000"); - if (numBands > 5) { - int extraBands = numBands - 5; - for (int i = 0; i < extraBands; i++) { - ciExtremeBuilder.insert(0, "0;"); - } - } - editor.putString("equalizer.preset." + numPresets, ciExtremeBuilder.toString()).apply(); - - // add small-speaker - StringBuilder ssBuilder = new StringBuilder("-170;270;50;-220;200"); - if (numBands > 5) { - int extraBands = numBands - 5; - for (int i = 0; i < extraBands; i++) { - ssBuilder.insert(0, "0;"); - } - } - editor.putString("equalizer.preset." + (numPresets + 1), ssBuilder.toString()).apply(); - editor.commit(); - - // Enable for the speaker by default - if (!getSharedPrefsFile("speaker").exists()) { - SharedPreferences spk = getSharedPreferences("speaker", 0); - spk.edit().putBoolean("audiofx.global.enable", true).apply(); - spk.edit().putString("audiofx.eq.preset", String.valueOf(numPresets + 1)).apply(); - } - } - - /** - * Push new configuration to audio stack. - */ - void update() { - synchronized (mAudioSessionsL) { - updateLocked(); - } - } - - private void updateLocked() { - final String mode = getAudioOutputRouting(); - SharedPreferences preferences = getSharedPreferences( - mode, 0); - - if (DEBUG) Log.i(TAG, "Selected configuration: " + mode); - - for (int i = 0; i < mAudioSessionsL.size(); i++) { - updateDsp(preferences, mAudioSessionsL.valueAt(i)); - } - } - - private void updateDsp(SharedPreferences prefs, EffectSet session) { - final boolean globalEnabled = prefs.getBoolean("audiofx.global.enable", false); - - try { - session.enableBassBoost(globalEnabled && prefs.getBoolean("audiofx.bass.enable", false)); - session.setBassBoostStrength(Short.valueOf(prefs - .getString("audiofx.bass.strength", "0"))); - - } catch (Exception e) { - Log.e(TAG, "Error enabling bass boost!", e); - } - - try { - short preset = Short.decode(prefs.getString("audiofx.reverb.preset", - String.valueOf(PresetReverb.PRESET_NONE))); - session.enableReverb(globalEnabled && (preset > 0)); - session.setReverbPreset(preset); - - } catch (Exception e) { - Log.e(TAG, "Error enabling reverb preset", e); - } - - try { - session.enableEqualizer(globalEnabled); - final int customPresetPos = session.getNumEqualizerPresets() + 2; - final int preset = Integer.valueOf(prefs.getString("audiofx.eq.preset", - String.valueOf(customPresetPos))); - final int bands = session.getNumEqualizerBands(); - - /* - * Equalizer state is in a single string preference with all values - * separated by ; - */ - String[] levels = null; - short[] equalizerLevels = null; - - if (mOverriddenEqualizerLevels != null) { - - } else if (preset == customPresetPos) { - if (DEBUG) Log.i(TAG, "loading custom band levels"); - levels = prefs.getString("audiofx.eq.bandlevels.custom", - getZeroedBandsString(bands)).split(";"); - } else { - if (DEBUG) Log.i(TAG, "loading preset band levels"); - levels = getSharedPreferences("global", 0).getString("equalizer.preset." + preset, - getZeroedBandsString(bands)).split(";"); - } - - if (levels != null) { - if (DEBUG) Log.i(TAG, "band levels applied: " + Arrays.toString(levels)); - equalizerLevels = new short[levels.length]; - for (int i = 0; i < levels.length; i++) { - equalizerLevels[i] = (short) (Float.parseFloat(levels[i])); - } - } else if (mOverriddenEqualizerLevels != null) { - equalizerLevels = new short[mOverriddenEqualizerLevels.length]; - for (int i = 0; i < mOverriddenEqualizerLevels.length; i++) { - equalizerLevels[i] = (short) mOverriddenEqualizerLevels[i]; - } - } - if (equalizerLevels != null) { - session.setEqualizerLevels(equalizerLevels); - } - - - } catch (Exception e) { - Log.e(TAG, "Error enabling equalizer!", e); - } - - try { - session.enableVirtualizer(globalEnabled - && prefs.getBoolean("audiofx.virtualizer.enable", false)); - session.setVirtualizerStrength(Short.valueOf(prefs.getString( - "audiofx.virtualizer.strength", "0"))); - - } catch (Exception e) { - Log.e(TAG, "Error enabling virtualizer!"); - } - } -} diff --git a/src/org/cyanogenmod/audiofx/OpenSLESConstants.java b/src/org/cyanogenmod/audiofx/OpenSLESConstants.java deleted file mode 100644 index 2131b55..0000000 --- a/src/org/cyanogenmod/audiofx/OpenSLESConstants.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2010-2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.cyanogenmod.audiofx; - -/** - * OpenSL ES constants class - */ -public final class OpenSLESConstants { - private OpenSLESConstants() { - // Empty constructor - } - - /** - * Minimum volume level in millibel (mb). - */ - public static final short SL_MILLIBEL_MIN = -9600; - /** - * This value is used when equalizer setting is not defined. - */ - public static final short SL_EQUALIZER_UNDEFINED = (short) 0xFFFF; - - /** - * The minimum bass boost strength in o/oo. - */ - public static final short BASSBOOST_MIN_STRENGTH = 0; - /** - * The maximum bass boost strength in o/oo. - */ - public static final short BASSBOOST_MAX_STRENGTH = 1000; - - /** - * The minimum reverb room level in mb. - */ - public static final short REVERB_MIN_ROOM_LEVEL = SL_MILLIBEL_MIN; - /** - * The maximum reverb room level in mb. - */ - public static final short REVERB_MAX_ROOM_LEVEL = 0; - /** - * The minimum reverb room HF level in mb. - */ - public static final short REVERB_MIN_ROOM_HF_LEVEL = SL_MILLIBEL_MIN; - /** - * The maximum reverb room HF level in mb. - */ - public static final short REVERB_MAX_ROOM_HF_LEVEL = 0; - /** - * The minimum reverb decay time in ms. - */ - public static final short REVERB_MIN_DECAY_TIME = 100; - /** - * The maximum reverb decay time in ms. - */ - // XXX: OpenSL ES is normally 20000 but can only support 7000 for now - public static final short REVERB_MAX_DECAY_TIME = 7000; - /** - * The minimum reverb decay HF ratio in o/oo. - */ - public static final short REVERB_MIN_DECAY_HF_RATIO = 100; - /** - * The maximum reverb decay HF ratio in o/oo. - */ - public static final short REVERB_MAX_DECAY_HF_RATIO = 2000; - /** - * The minimum reverb level in mb. - */ - public static final short REVERB_MIN_REVERB_LEVEL = SL_MILLIBEL_MIN; - /** - * The maximum reverb level in mb. - */ - public static final short REVERB_MAX_REVERB_LEVEL = 2000; - /** - * The minimum reverb diffusion in o/oo. - */ - public static final short REVERB_MIN_DIFFUSION = 0; - /** - * The maximum reverb diffusion in o/oo. - */ - public static final short REVERB_MAX_DIFFUSION = 1000; - /** - * The minimum reverb density in o/oo. - */ - public static final short REVERB_MIN_DENSITY = 0; - /** - * The maximum reverb density in o/oo. - */ - public static final short REVERB_MAX_DENSITY = 1000; - - /** - * The minimum virtualizer strength in o/oo. - */ - public static final short VIRTUALIZER_MIN_STRENGTH = 0; - /** - * The maximum virtualizer strength in o/oo. - */ - public static final short VIRTUALIZER_MAX_STRENGTH = 1000; - - /** - * The minimum volume effect level in millibel (mb). - */ - public static final short VOLUME_MIN_LEVEL = SL_MILLIBEL_MIN; - /** - * The minimum volume stereo position in o/oo. - */ - public static final short VOLUME_MIN_STEREO_POSITION = -1000; - /** - * The maximum volume stereo position in o/oo. - */ - public static final short VOLUME_MAX_STEREO_POSITION = 1000; -} diff --git a/src/org/cyanogenmod/audiofx/audiofx/AudioFxApplication.java b/src/org/cyanogenmod/audiofx/audiofx/AudioFxApplication.java new file mode 100644 index 0000000..fa47637 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/AudioFxApplication.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cyngn.audiofx; + +import android.app.Application; +import android.util.Log; + +import com.cyanogen.ambient.analytics.AnalyticsServices; +import com.cyanogen.ambient.analytics.Event; +import com.cyanogen.ambient.common.api.AmbientApiClient; + +public class AudioFxApplication extends Application { + + private static final String TAG = AudioFxApplication.class.getSimpleName(); + private static final boolean DEBUG = false; + + private AmbientApiClient mClient; + + @Override + public void onCreate() { + super.onCreate(); + mClient = new AmbientApiClient.Builder(this) + .addApi(AnalyticsServices.API) + .build(); + mClient.connect(); + } + + public void sendEvent(Event event) { + if (DEBUG) { + Log.i(TAG, "sendEvent() called with event = [" + event + "]"); + } + AnalyticsServices.AnalyticsApi.sendEvent(mClient, event); + } +} diff --git a/src/org/cyanogenmod/audiofx/Compatibility.java b/src/org/cyanogenmod/audiofx/audiofx/Compatibility.java index c0e72c2..313c480 100644 --- a/src/org/cyanogenmod/audiofx/Compatibility.java +++ b/src/org/cyanogenmod/audiofx/audiofx/Compatibility.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.cyanogenmod.audiofx; +package com.cyngn.audiofx; import android.app.Activity; import android.app.IntentService; @@ -30,6 +30,8 @@ import android.media.audiofx.AudioEffect; import android.net.Uri; import android.os.Bundle; import android.util.Log; +import com.cyngn.audiofx.activity.ActivityMusic; +import com.cyngn.audiofx.stats.UserSession; import java.util.List; @@ -64,8 +66,8 @@ public class Compatibility { Intent i = new Intent(getIntent()); i.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); - String defPackage = pref.getString("defaultpanelpackage", null); - String defName = pref.getString("defaultpanelname", null); + String defPackage = pref.getString(Constants.MUSICFX_DEFAULT_PACKAGE_KEY, null); + String defName = pref.getString(Constants.MUSICFX_DEFAULT_PANEL_KEY, null); log("read " + defPackage + "/" + defName + " as default"); if (defPackage == null || defName == null) { Log.e(TAG, "no default set!"); @@ -79,6 +81,8 @@ public class Compatibility { } else { i.setComponent(new ComponentName(defPackage, defName)); } + + i.putExtra(ActivityMusic.EXTRA_CALLING_PACKAGE, getCallingPackage()); startActivity(i); finish(); } @@ -149,9 +153,9 @@ public class Compatibility { Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); List<ResolveInfo> ris = mPackageManager.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS); log("found: " + ris.size()); - SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); - String savedDefPackage = pref.getString("defaultpanelpackage", null); - String savedDefName = pref.getString("defaultpanelname", null); + SharedPreferences pref = Constants.getMusicFxPrefs(this); + String savedDefPackage = pref.getString(Constants.MUSICFX_DEFAULT_PACKAGE_KEY, null); + String savedDefName = pref.getString(Constants.MUSICFX_DEFAULT_PANEL_KEY, null); log("saved default: " + savedDefName); for (ResolveInfo foo: ris) { if (foo.activityInfo.name.equals(Compatibility.Redirector.class.getName())) { @@ -206,10 +210,10 @@ public class Compatibility { // Write the selected default to the prefs so that the Redirector activity // knows which one to use. - SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); + SharedPreferences pref = Constants.getMusicFxPrefs(this); Editor ed = pref.edit(); - ed.putString("defaultpanelpackage", defPackage); - ed.putString("defaultpanelname", defName); + ed.putString(Constants.MUSICFX_DEFAULT_PACKAGE_KEY, defPackage); + ed.putString(Constants.MUSICFX_DEFAULT_PANEL_KEY, defName); ed.commit(); log("wrote " + defPackage + "/" + defName + " as default"); } diff --git a/src/org/cyanogenmod/audiofx/audiofx/Constants.java b/src/org/cyanogenmod/audiofx/audiofx/Constants.java new file mode 100644 index 0000000..c1d5475 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/Constants.java @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cyngn.audiofx; + +import android.content.Context; +import android.content.SharedPreferences; +import com.cyngn.audiofx.eq.EqUtils; + +import java.util.ArrayList; +import java.util.List; + +public class Constants { + + // current pref version, bump to rebuild prefs + public static final int CURRENT_PREFS_INT_VERSION = 2; + + // effect type identifiers + public static final int EFFECT_TYPE_ANDROID = 1; + public static final int EFFECT_TYPE_MAXXAUDIO = 2; + public static final int EFFECT_TYPE_DTS = 3; + + // global settings + public static final String AUDIOFX_GLOBAL_FILE = "global"; + + public static final String DEVICE_SPEAKER = "speaker"; + public static final String DEVICE_HEADSET = "headset"; + public static final String DEVICE_LINE_OUT = "lineout"; + public static final String DEVICE_PREFIX_USB = "usb"; + public static final String DEVICE_PREFIX_CAST = "wireless"; + public static final String DEVICE_PREFIX_BLUETOOTH = "bluetooth"; + + public static final String SAVED_DEFAULTS = "saved_defaults"; + + public static final String AUDIOFX_GLOBAL_USE_DTS = "audiofx.global.use_dts"; + public static final String AUDIOFX_GLOBAL_HAS_DTS = "audiofx.global.has_dts"; + public static final String AUDIOFX_GLOBAL_ENABLE_DTS = "audiofx.global.dts.enable"; + public static final String AUDIOFX_GLOBAL_HAS_MAXXAUDIO = "audiofx.global.hasmaxxaudio"; + public static final String AUDIOFX_GLOBAL_HAS_BASSBOOST = "audiofx.global.hasbassboost"; + public static final String AUDIOFX_GLOBAL_HAS_VIRTUALIZER = "audiofx.global.hasvirtualizer"; + public static final String AUDIOFX_GLOBAL_PREFS_VERSION_INT = "audiofx.global.prefs.version"; + + // per-device settings + public static final boolean DEVICE_DEFAULT_GLOBAL_ENABLE = false; + + /** + * not really global enable, but really the device global enable... + */ + public static final String DEVICE_AUDIOFX_GLOBAL_ENABLE = "audiofx.global.enable"; + public static final String DEVICE_AUDIOFX_BASS_ENABLE = "audiofx.bass.enable"; + public static final String DEVICE_AUDIOFX_BASS_STRENGTH = "audiofx.bass.strength"; + public static final String DEVICE_AUDIOFX_REVERB_PRESET = "audiofx.reverb.preset"; + public static final String DEVICE_AUDIOFX_VIRTUALIZER_ENABLE = "audiofx.virtualizer.enable"; + public static final String DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH = "audiofx.virtualizer.strength"; + public static final String DEVICE_AUDIOFX_TREBLE_ENABLE = "audiofx.treble.enable"; + public static final String DEVICE_AUDIOFX_TREBLE_STRENGTH = "audiofx.treble.strength"; + public static final String DEVICE_AUDIOFX_MAXXVOLUME_ENABLE = "audiofx.maxxvolume.enable"; + + public static final String DEVICE_AUDIOFX_EQ_PRESET = "audiofx.eq.preset"; + public static final String DEVICE_AUDIOFX_EQ_PRESET_LEVELS = "audiofx.eq.preset.levels"; + + // eq + public static final String EQUALIZER_NUMBER_OF_PRESETS = "equalizer.number_of_presets"; + public static final String EQUALIZER_NUMBER_OF_BANDS = "equalizer.number_of_bands"; + public static final String EQUALIZER_BAND_LEVEL_RANGE = "equalizer.band_level_range"; + public static final String EQUALIZER_CENTER_FREQS = "equalizer.center_freqs"; + public static final String EQUALIZER_PRESET = "equalizer.preset."; + public static final String EQUALIZER_PRESET_NAMES = "equalizer.preset_names"; + + // musicfx constants + public static final String MUSICFX_PREF_NAME = "musicfx"; + public static final String MUSICFX_DEFAULT_PACKAGE_KEY = "defaultpanelpackage"; + public static final String MUSICFX_DEFAULT_PANEL_KEY = "defaultpanelname"; + + public static SharedPreferences getMusicFxPrefs(Context context) { + return context.getSharedPreferences(MUSICFX_PREF_NAME, Context.MODE_PRIVATE); + } + + public static SharedPreferences getGlobalPrefs(Context context) { + return context.getSharedPreferences(AUDIOFX_GLOBAL_FILE, 0); + } + + public static List<Preset> getCustomPresets(Context ctx, int bands) { + ArrayList<Preset> presets = new ArrayList<Preset>(); + final SharedPreferences presetPrefs = ctx.getSharedPreferences("custom_presets", 0); + String[] presetNames = presetPrefs.getString("preset_names", "").split("\\|"); + + for (int i = 0; i < presetNames.length; i++) { + String storedPresetString = presetPrefs.getString(presetNames[i], null); + if (storedPresetString == null) { + continue; + } + Preset.CustomPreset p = Preset.CustomPreset.fromString(storedPresetString); + presets.add(p); + } + + return presets; + } + + public static void saveCustomPresets(Context ctx, List<Preset> presets) { + final SharedPreferences.Editor presetPrefs = ctx.getSharedPreferences("custom_presets", 0).edit(); + presetPrefs.clear(); + + StringBuffer presetNames = new StringBuffer(); + for (int i = 0; i < presets.size(); i++) { + final Preset preset = presets.get(i); + if (preset instanceof Preset.CustomPreset + && !(preset instanceof Preset.PermCustomPreset)) { + Preset.CustomPreset p = (Preset.CustomPreset) preset; + presetNames.append(p.getName()); + presetNames.append("|"); + + presetPrefs.putString(p.getName(), p.toString()); + } + } + if (presetNames.length() > 0) { + presetNames.deleteCharAt(presetNames.length() - 1); + } + + presetPrefs.putString("preset_names", presetNames.toString()); + presetPrefs.commit(); + } + + public static int[] getBandLevelRange(Context context) { + String savedCenterFreqs = context.getSharedPreferences("global", 0).getString("equalizer.band_level_range", null); + if (savedCenterFreqs == null || savedCenterFreqs.isEmpty()) { + return new int[]{-1500, 1500}; + } else { + String[] split = savedCenterFreqs.split(";"); + int[] freqs = new int[split.length]; + for (int i = 0; i < split.length; i++) { + freqs[i] = Integer.valueOf(split[i]); + } + return freqs; + } + } + + public static int[] getCenterFreqs(Context context, int eqBands) { + String savedCenterFreqs = context.getSharedPreferences("global", 0).getString("equalizer.center_freqs", + EqUtils.getZeroedBandsString(eqBands)); + String[] split = savedCenterFreqs.split(";"); + int[] freqs = new int[split.length]; + for (int i = 0; i < split.length; i++) { + freqs[i] = Integer.valueOf(split[i]); + } + return freqs; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/Preset.java b/src/org/cyanogenmod/audiofx/audiofx/Preset.java new file mode 100644 index 0000000..e284658 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/Preset.java @@ -0,0 +1,236 @@ +package com.cyngn.audiofx; + +import android.os.Parcel; +import android.os.Parcelable; +import com.cyngn.audiofx.eq.EqUtils; + +public class Preset implements Parcelable { + + protected String mName; + protected final float[] mLevels; + + private Preset(String name, float[] levels) { + this.mName = name; + mLevels = new float[levels.length]; + for (int i = 0; i < levels.length; i++) { + mLevels[i] = levels[i]; + } + } + + public float[] getLevels() { + return mLevels; + } + + public float getBandLevel(int band) { + return mLevels[band]; + } + + @Override + public String toString() { + return mName + "|" + EqUtils.floatLevelsToString(mLevels); + } + + private static Preset fromString(String input) { + final String[] split = input.split("\\|"); + if (split == null || split.length != 2) { + return null; + } + float[] levels = EqUtils.stringBandsToFloats(split[1]); + return new Preset(split[0], levels); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Preset) { + Preset other = (Preset) o; + + if (this.mLevels.length != ((Preset) o).mLevels.length) { + return false; + } + + for(int i = 0; i < mLevels.length; i++) { + if (mLevels[i] != other.mLevels[i]) { + return false; + } + } + + return other.mName.equals(mName); + } + return super.equals(o); + } + + private Preset(Parcel in) { + if (in.readInt() == 1) { + mName = in.readString(); + } + mLevels = new float[in.readInt()]; + in.readFloatArray(mLevels); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mName != null ? 1 : 0); + if (mName != null) { + dest.writeString(mName); + } + dest.writeInt(mLevels.length); + dest.writeFloatArray(mLevels); + } + + public static final Parcelable.Creator<Preset> CREATOR = new Parcelable.Creator<Preset>() { + @Override + public Preset createFromParcel(Parcel in) { + return new Preset(in); + } + + @Override + public Preset[] newArray(int size) { + return new Preset[size]; + } + }; + + public String getName() { + return mName; + } + + public static class StaticPreset extends Preset { + public StaticPreset(String name, float[] levels) { + super(name, levels); + } + } + + public static class CustomPreset extends Preset { + + private boolean mLocked; + + public CustomPreset(String name, float[] levels, boolean locked) { + super(name, levels); + mLocked = locked; + } + + public boolean isLocked() { + return mLocked; + } + + public void setLocked(boolean locked) { + mLocked = locked; + } + + public void setName(String name) { + mName = name; + } + + public void setLevel(int band, float level) { + mLevels[band] = level; + } + + public void setLevels(float[] levels) { + for (int i = 0; i < levels.length; i++) { + mLevels[i] = levels[i]; + } + } + + public float getLevel(int band) { + return mLevels[band]; + } + + @Override + public boolean equals(Object o) { + if (o instanceof CustomPreset) { + return super.equals(o) && mLocked == ((CustomPreset) o).mLocked; + } + return false; + } + + @Override + public String toString() { + return super.toString() + "|" + mLocked; + } + + public static CustomPreset fromString(String input) { + final String[] split = input.split("\\|"); + if (split == null || split.length != 3) { + return null; + } + float[] levels = EqUtils.stringBandsToFloats(split[1]); + return new CustomPreset(split[0], levels, Boolean.valueOf(split[2])); + } + + public static final Parcelable.Creator<CustomPreset> CREATOR + = new Parcelable.Creator<CustomPreset>() { + @Override + public CustomPreset createFromParcel(Parcel in) { + return new CustomPreset(in); + } + + @Override + public CustomPreset[] newArray(int size) { + return new CustomPreset[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mLocked ? 1 : 0); + } + + protected CustomPreset(Parcel in) { + super(in); + mLocked = in.readInt() == 1; + } + + } + + public static class PermCustomPreset extends CustomPreset { + + public PermCustomPreset(String name, float[] levels) { + super(name, levels, false); + } + + @Override + public String toString() { + return mName + "|" + EqUtils.floatLevelsToString(mLevels); + } + + public static PermCustomPreset fromString(String input) { + final String[] split = input.split("\\|"); + if (split == null || split.length != 2) { + return null; + } + float[] levels = EqUtils.stringBandsToFloats(split[1]); + return new PermCustomPreset(split[0], levels); + } + + protected PermCustomPreset(Parcel in) { + super(in); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + public static final Creator<PermCustomPreset> CREATOR = new Creator<PermCustomPreset>() { + @Override + public PermCustomPreset createFromParcel(Parcel in) { + return new PermCustomPreset(in); + } + + @Override + public PermCustomPreset[] newArray(int size) { + return new PermCustomPreset[size]; + } + }; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/activity/ActivityMusic.java b/src/org/cyanogenmod/audiofx/audiofx/activity/ActivityMusic.java new file mode 100644 index 0000000..f75273d --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/activity/ActivityMusic.java @@ -0,0 +1,198 @@ +package com.cyngn.audiofx.activity; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewStub; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import com.cyanogen.ambient.analytics.Event; +import com.cyngn.audiofx.AudioFxApplication; +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.fragment.AudioFxFragment; +import com.cyngn.audiofx.knobs.KnobCommander; +import com.cyngn.audiofx.service.AudioFxService; +import com.cyngn.audiofx.stats.AppState; +import com.cyngn.audiofx.stats.UserSession; + +public class ActivityMusic extends Activity { + + private static final String TAG = ActivityMusic.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final String TAG_AUDIOFX = "audiofx"; + public static final String EXTRA_CALLING_PACKAGE = "audiofx::extra_calling_package"; + + private CheckBox mCurrentDeviceToggle; + MasterConfigControl mConfig; + String mCallingPackage; + + private boolean mWaitingForService = true; + private SharedPreferences.OnSharedPreferenceChangeListener mServiceReadyObserver; + + private CompoundButton.OnCheckedChangeListener mGlobalEnableToggleListener + = new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(final CompoundButton buttonView, + final boolean isChecked) { + if (UserSession.getInstance() != null) { + UserSession.getInstance().deviceEnabledDisabled(); + } + mConfig.setCurrentDeviceEnabled(isChecked); + } + }; + + @Override + public void onCreate(final Bundle savedInstanceState) { + if (DEBUG) + Log.i(TAG, "onCreate() called with " + + "savedInstanceState = [" + savedInstanceState + "]"); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + mCallingPackage = getIntent().getStringExtra(EXTRA_CALLING_PACKAGE); + Log.i(TAG, "calling package: " + mCallingPackage); + + mConfig = MasterConfigControl.getInstance(this); + + final SharedPreferences globalPrefs = Constants.getGlobalPrefs(this); + + mWaitingForService = !defaultsSetup(); + if (mWaitingForService) { + Log.w(TAG, "waiting for service."); + mServiceReadyObserver = new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(Constants.SAVED_DEFAULTS) && defaultsSetup()) { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + mConfig.onResetDefaults(); + init(savedInstanceState); + + mWaitingForService = false; + invalidateOptionsMenu(); + mServiceReadyObserver = null; + } + } + }; + globalPrefs.registerOnSharedPreferenceChangeListener(mServiceReadyObserver); + startService(new Intent(ActivityMusic.this, AudioFxService.class)); + // TODO add loading fragment if service initialization takes too long + } else { + init(savedInstanceState); + } + } + + private boolean defaultsSetup() { + final int targetVersion = Constants.CURRENT_PREFS_INT_VERSION; + final SharedPreferences prefs = Constants.getGlobalPrefs(this); + final int currentVersion = prefs.getInt(Constants.AUDIOFX_GLOBAL_PREFS_VERSION_INT, 0); + final boolean defaultsSaved = prefs.getBoolean(Constants.SAVED_DEFAULTS, false); + return defaultsSaved && currentVersion >= targetVersion; + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + // should null it out if one was there, compat redirector with package will go through onCreate + mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE); + } + + @Override + protected void onDestroy() { + if (mServiceReadyObserver != null) { + Constants.getGlobalPrefs(this) + .unregisterOnSharedPreferenceChangeListener(mServiceReadyObserver); + mServiceReadyObserver = null; + } + super.onDestroy(); + } + + private void init(Bundle savedInstanceState) { + mConfig = MasterConfigControl.getInstance(this); + + ActionBar ab = getActionBar(); + ab.setTitle(R.string.app_title); + ab.setDisplayShowTitleEnabled(true); + + final View extraView = LayoutInflater.from(this) + .inflate(R.layout.action_bar_custom_components, null); + ActionBar.LayoutParams lp = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, + ActionBar.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.CENTER_VERTICAL); + ab.setCustomView(extraView, lp); + ab.setDisplayShowCustomEnabled(true); + + mCurrentDeviceToggle = (CheckBox) ab.getCustomView().findViewById(R.id.global_toggle); + mCurrentDeviceToggle.setOnCheckedChangeListener(mGlobalEnableToggleListener); + + if (savedInstanceState == null && findViewById(R.id.main_fragment) != null) { + getFragmentManager() + .beginTransaction() + .add(R.id.main_fragment, new AudioFxFragment(), TAG_AUDIOFX) + .commit(); + } + applyOemDecor(); + } + + private void applyOemDecor() { + ActionBar ab = getActionBar(); + if (mConfig.hasMaxxAudio()) { + ab.setSubtitle(R.string.powered_by_maxx_audio); + } else if (mConfig.hasDts()) { + final ViewStub stub = (ViewStub) ab.getCustomView().findViewById(R.id.logo_stub); + stub.setLayoutResource(R.layout.action_bar_dts_logo); + stub.inflate(); + } + } + + @Override + protected void onResume() { + if (DEBUG) Log.i(TAG, "onResume() called with " + ""); + super.onResume(); + + // initiate a new session + new UserSession(mCallingPackage); + } + + @Override + protected void onPause() { + super.onPause(); + + if (DEBUG) Log.d(TAG, "Session: " + UserSession.getInstance()); + + final Event.Builder builder = new Event.Builder("session", "ended"); + UserSession.getInstance().append(builder); + AppState.appendState(mConfig, KnobCommander.getInstance(this), builder); + ((AudioFxApplication) getApplicationContext()).sendEvent(builder.build()); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (DEBUG) Log.i(TAG, "onConfigurationChanged() called with " + + "newConfig = [" + newConfig + "]"); + if (newConfig.orientation != getResources().getConfiguration().orientation) { + mCurrentDeviceToggle = null; + } + } + + public void setGlobalToggleChecked(boolean checked) { + if (mCurrentDeviceToggle != null) { + mCurrentDeviceToggle.setOnCheckedChangeListener(null); + mCurrentDeviceToggle.setChecked(checked); + mCurrentDeviceToggle.setOnCheckedChangeListener(mGlobalEnableToggleListener); + } + } + + public CompoundButton getGlobalSwitch() { + return mCurrentDeviceToggle; + } +} diff --git a/src/org/cyanogenmod/audiofx/ControlPanelPicker.java b/src/org/cyanogenmod/audiofx/audiofx/activity/ControlPanelPicker.java index 1c6eaaf..a9fd876 100644 --- a/src/org/cyanogenmod/audiofx/ControlPanelPicker.java +++ b/src/org/cyanogenmod/audiofx/audiofx/activity/ControlPanelPicker.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.cyanogenmod.audiofx; +package com.cyngn.audiofx.activity; import com.android.internal.app.AlertActivity; import com.android.internal.app.AlertController; import com.android.internal.app.AlertController.AlertParams.OnPrepareListViewListener; -import org.cyanogenmod.audiofx.Compatibility.Service; +import com.cyngn.audiofx.Compatibility; +import com.cyngn.audiofx.Compatibility.Service; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; @@ -31,11 +32,8 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.media.audiofx.AudioEffect; import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ListView; +import com.cyngn.audiofx.R; import java.util.List; @@ -81,9 +79,9 @@ public class ControlPanelPicker extends AlertActivity implements OnClickListener p.mOnClickListener = mItemClickListener; p.mLabelColumn = "title"; p.mIsSingleChoice = true; - p.mPositiveButtonText = getString(com.android.internal.R.string.ok); + p.mPositiveButtonText = getString(getOkStringResId()); p.mPositiveButtonListener = this; - p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); + p.mNegativeButtonText = getString(getCancelStringResId()); p.mOnPrepareListViewListener = this; p.mTitle = getString(R.string.picker_title); p.mCheckedItem = defpanelidx; @@ -91,6 +89,14 @@ public class ControlPanelPicker extends AlertActivity implements OnClickListener setupAlert(); } + private int getOkStringResId() { + return getResources().getIdentifier("ok", "string", "android"); + } + + private int getCancelStringResId() { + return getResources().getIdentifier("cancel", "string", "android"); + } + private DialogInterface.OnClickListener mItemClickListener = new DialogInterface.OnClickListener() { diff --git a/src/org/cyanogenmod/audiofx/audiofx/activity/EqualizerManager.java b/src/org/cyanogenmod/audiofx/audiofx/activity/EqualizerManager.java new file mode 100644 index 0000000..03e9853 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/activity/EqualizerManager.java @@ -0,0 +1,634 @@ +package com.cyngn.audiofx.activity; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.widget.CompoundButton; + +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.eq.EqUtils; +import com.cyngn.audiofx.service.AudioFxService; +import com.cyngn.audiofx.stats.UserSession; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + +public class EqualizerManager { + + private static final String TAG = EqualizerManager.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final MasterConfigControl mConfig; + private final Context mContext; + + private float mMinFreq; + private float mMaxFreq; + + private float mMinDB; + private float mMaxDB; + private int mNumBands; + private CompoundButton.OnCheckedChangeListener mLockChangeListener; + + /* + * presets from the library custom preset. + */ + private int mPredefinedPresets; + private float[] mCenterFreqs; + private float[] mGlobalLevels; + + private AtomicBoolean mAnimatingToCustom = new AtomicBoolean(false); + + // whether we are in between presets, animating them and such + private boolean mChangingPreset = false; + + private int mCurrentPreset; + + private final ArrayList<Preset> mEqPresets = new ArrayList<Preset>(); + private int mEQCustomPresetPosition; + + private String mZeroedBandString; + + private static final int MSG_SAVE_PRESETS = 1; + private static final int MSG_SEND_EQ_OVERRIDE = 2; + + private Handler mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAVE_PRESETS: + Constants.saveCustomPresets(mContext, mEqPresets); + break; + case MSG_SEND_EQ_OVERRIDE: + mConfig.overrideEqLevels((short)msg.arg1, (short) msg.arg2); + break; + } + return true; + }}, true); + + public EqualizerManager(Context context, MasterConfigControl config) { + mContext = context; + mConfig = config; + + applyDefaults(); + } + + public void applyDefaults() { + mEqPresets.clear(); + // setup eq + int bands = Integer.parseInt(getGlobalPref("equalizer.number_of_bands", "5")); + final int[] centerFreqs = Constants.getCenterFreqs(mContext, bands); + final int[] bandLevelRange = Constants.getBandLevelRange(mContext); + + float[] centerFreqsKHz = new float[centerFreqs.length]; + for (int i = 0; i < centerFreqs.length; i++) { + centerFreqsKHz[i] = (float) centerFreqs[i] / 1000.0f; + } + + mMinDB = bandLevelRange[0] / 100; + mMaxDB = bandLevelRange[1] / 100; + + mNumBands = centerFreqsKHz.length; + mGlobalLevels = new float[mNumBands]; + for (int i = 0; i < mGlobalLevels.length; i++) { + mGlobalLevels[i] = 0; + } + + mZeroedBandString = EqUtils.getZeroedBandsString(getNumBands()); + + mCenterFreqs = Arrays.copyOf(centerFreqsKHz, mNumBands); + System.arraycopy(centerFreqsKHz, 0, mCenterFreqs, 0, mNumBands); + mMinFreq = mCenterFreqs[0] / 2; + mMaxFreq = (float) Math.pow(mCenterFreqs[mNumBands - 1], 2) / mCenterFreqs[mNumBands - 2] / 2; + + // setup equalizer presets + final int numPresets = Integer.parseInt(getGlobalPref("equalizer.number_of_presets", "0")); + + if (numPresets > 0) { + // add library-provided presets + String[] presetNames = getGlobalPref("equalizer.preset_names", "").split("\\|"); + mPredefinedPresets = presetNames.length + 1; // we consider first EQ to be part of predefined + for (int i = 0; i < numPresets; i++) { + mEqPresets.add(new Preset.StaticPreset(presetNames[i], getPersistedPresetLevels(i))); + } + } else { + mPredefinedPresets = 1; // custom is predefined + } + // add custom preset + mEqPresets.add(new Preset.PermCustomPreset(mContext.getString(R.string.user), + getPersistedCustomLevels())); + mEQCustomPresetPosition = mEqPresets.size() - 1; + + // restore custom prefs + mEqPresets.addAll(Constants.getCustomPresets(mContext, mNumBands)); + + // setup default preset for speaker + mCurrentPreset = Integer.parseInt(getPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, "0")); + if (mCurrentPreset > mEqPresets.size() - 1) { + mCurrentPreset = 0; + } + setPreset(mCurrentPreset); + } + + public boolean isUserPreset() { + boolean result = mCurrentPreset >= mPredefinedPresets; + /*if (DEBUG) { + Log.i(TAG, "isUserPreset(), current preset: " + mCurrentPreset); + Log.i(TAG, "----> predefined presets: " + mPredefinedPresets); + Log.d(TAG, "----> RESULT: " + result); + }*/ + return result; + } + + public boolean isCustomPreset() { + return mCurrentPreset == mEQCustomPresetPosition; + } + + public CompoundButton.OnCheckedChangeListener getLockChangeListener() { + if (mLockChangeListener == null) { + mLockChangeListener = new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isUserPreset()) { + ((Preset.CustomPreset) mEqPresets.get(mCurrentPreset)).setLocked(isChecked); + } + } + }; + } + return mLockChangeListener; + } + + public boolean isChangingPresets() { + return mChangingPreset; + } + + public void setChangingPresets(boolean changing) { + if (mChangingPreset != changing) { + mChangingPreset = changing; + if (changing) { + mConfig.getCallbacks().notifyEqControlStateChanged(false, false, false, false); + } else { + updateEqControls(); + } + } + } + + public boolean isAnimatingToCustom() { + return mAnimatingToCustom.get(); + } + + public void setAnimatingToCustom(boolean animating) { + mAnimatingToCustom.set(animating); + if (!animating) { + // finished animation + updateEqControls(); + } + } + + private void savePresetsDelayed() { + mHandler.sendEmptyMessageDelayed(MSG_SAVE_PRESETS, 500); + } + + public int indexOf(Preset p) { + return mEqPresets.indexOf(p); + } + + void onPreDeviceChanged() { + // need to update the current preset based on the device here. + int newPreset = Integer.parseInt(getPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, "0")); + if (newPreset > mEqPresets.size() - 1) { + newPreset = 0; + } + + // this should be ready to go for callbacks to query the new device preset below + mCurrentPreset = newPreset; + + } + + void onPostDeviceChanged() { + setPreset(mCurrentPreset, false); + } + + public Preset getCurrentPreset() { + return mEqPresets.get(mCurrentPreset); + } + + /** + * Copy current config levels from the current preset into custom values since the user has + * initiated some change. Then update the current preset to 'custom'. + */ + public int copyToCustom() { + updateGlobalLevels(mCurrentPreset); + if (DEBUG) { + Log.w(TAG, "using levels from preset: " + mCurrentPreset + ": " + Arrays.toString(mGlobalLevels)); + } + + String levels = EqUtils.floatLevelsToString( + EqUtils.convertDecibelsToMillibels( + mEqPresets.get(mCurrentPreset).getLevels())); + setGlobalPref("custom", levels); + + ((Preset.PermCustomPreset) mEqPresets.get(mEQCustomPresetPosition)).setLevels(mGlobalLevels); + if (DEBUG) + Log.i(TAG, "copyToCustom() wrote current preset levels to index: " + mEQCustomPresetPosition); + setPreset(mEQCustomPresetPosition); + savePresetsDelayed(); + return mEQCustomPresetPosition; + } + + public int addPresetFromCustom() { + updateGlobalLevels(mEQCustomPresetPosition); + if (DEBUG) { + Log.w(TAG, "using levels from preset: " + mCurrentPreset + ": " + Arrays.toString(mGlobalLevels)); + } + + int writtenToIndex = addPreset(mGlobalLevels); + if (DEBUG) + Log.i(TAG, "addPresetFromCustom() wrote current preset levels to index: " + writtenToIndex); + setPreset(writtenToIndex); + savePresetsDelayed(); + return writtenToIndex; + } + + /** + * Loops through all presets. And finds the first preset that can be written to. + * If one is not found, then one is inserted, and that new index is returned. + * @return the index that the levels were copied to + */ + private int addPreset(float[] levels) { + if (UserSession.getInstance() != null) { + UserSession.getInstance().presetCreated(); + } + + final int customPresets = Constants.getCustomPresets(mContext, mNumBands).size(); + // format the name so it's like "Custom <N>", start with "Custom 2" + final String name = String.format(mContext.getString(R.string.user_n), customPresets + 2); + + Preset.CustomPreset customPreset = new Preset.CustomPreset(name, levels, false); + mEqPresets.add(customPreset); + + mConfig.getCallbacks().notifyPresetsChanged(); + + return mEqPresets.size() - 1; + } + + /** + * Set a new level! + * <p/> + * This call will be propogated to all listeners registered with addEqStateChangeCallback(). + * + * @param band the band index the band index which changed + * @param dB the new decibel value + * @param systemChange is this change generated by the system? + */ + public void setLevel(final int band, final float dB, final boolean fromSystem) { + if (DEBUG) Log.i(TAG, "setLevel(" + band + ", " + dB + ", " + fromSystem + ")"); + + mGlobalLevels[band] = dB; + + if (fromSystem && !mConfig.isUserDeviceOverride()) { + // quickly convert decibel to millibel and send away to the service + mHandler.obtainMessage(MSG_SEND_EQ_OVERRIDE, band, (short) (dB * 100)).sendToTarget(); + } + + mConfig.getCallbacks().notifyBandLevelChangeChanged(band, dB, fromSystem); + + if (!fromSystem) { // user is touching + // persist + + final Preset preset = mEqPresets.get(mCurrentPreset); + if (preset instanceof Preset.CustomPreset) { + if (mAnimatingToCustom.get()) { + if (DEBUG) { + Log.d(TAG, "setLevel() not persisting new custom band becuase animating."); + } + } else { + ((Preset.CustomPreset) preset).setLevel(band, dB); + if (preset instanceof Preset.PermCustomPreset) { + // store these as millibels + String levels = EqUtils.floatLevelsToString( + EqUtils.convertDecibelsToMillibels( + preset.getLevels())); + setGlobalPref("custom", levels); + } + } + // needs to be updated immediately here for the service. + final String levels = EqUtils.floatLevelsToString(preset.getLevels()); + setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET_LEVELS, levels); + + mConfig.updateService(AudioFxService.EQ_CHANGED); + } + savePresetsDelayed(); + } + } + + /** + * Set a new preset index. + * <p/> + * This call will be propogated to all listeners registered with addEqStateChangeCallback(). + * + * @param newPresetIndex the new preset index. + */ + public void setPreset(final int newPresetIndex, boolean updateBackend) { + mCurrentPreset = newPresetIndex; + updateEqControls(); // do this before callback is propogated + + mConfig.getCallbacks().notifyPresetChanged(newPresetIndex); + + // persist + setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET, String.valueOf(newPresetIndex)); + + // update mGlobalLevels + float[] newlevels = getPresetLevels(newPresetIndex); + for (int i = 0; i < newlevels.length; i++) { + setLevel(i, newlevels[i], true); + } + + setPref(Constants.DEVICE_AUDIOFX_EQ_PRESET_LEVELS, EqUtils.floatLevelsToString(newlevels)); + + if (updateBackend) { + mConfig.updateService(AudioFxService.EQ_CHANGED); + } + } + + public void setPreset(final int newPresetIndex) { + setPreset(newPresetIndex, true); + } + + private void updateEqControls() { + final boolean userPreset = isUserPreset(); + mConfig.getCallbacks().notifyEqControlStateChanged(mEQCustomPresetPosition == mCurrentPreset, + userPreset, userPreset, userPreset); + } + + /** + * @return Get the current preset index + */ + public int getCurrentPresetIndex() { + return mCurrentPreset; + } + + /*=============== + * eq methods + *===============*/ + + public float projectX(double freq) { + double pos = Math.log(freq); + double minPos = Math.log(mMinFreq); + double maxPos = Math.log(mMaxFreq); + return (float) ((pos - minPos) / (maxPos - minPos)); + } + + public double reverseProjectX(float pos) { + double minPos = Math.log(mMinFreq); + double maxPos = Math.log(mMaxFreq); + return Math.exp(pos * (maxPos - minPos) + minPos); + } + + public float projectY(double dB) { + double pos = (dB - mMinDB) / (mMaxDB - mMinDB); + return (float) (1 - pos); + } + + public static double lin2dB(double rho) { + return rho != 0 ? Math.log(rho) / Math.log(10) * 20 : -99.9; + } + + public float getMinFreq() { + return mMinFreq; + } + + public float getMaxFreq() { + return mMaxFreq; + } + + public float getMinDB() { + return mMinDB; + } + + public float getMaxDB() { + return mMaxDB; + } + + public int getNumBands() { + return mNumBands; + } + + public float getCenterFreq(int band) { + return mCenterFreqs[band]; + } + + public float[] getCenterFreqs() { + return mCenterFreqs; + } + + public float[] getLevels() { + return mGlobalLevels; + } + + public float getLevel(int band) { + return mGlobalLevels[band]; + } + + /*=============== + * preset methods + *===============*/ + + public float[] getPersistedPresetLevels(int presetIndex) { + String newLevels = null; + + if (mEqPresets.size() > presetIndex + && mEqPresets.get(presetIndex) instanceof Preset.PermCustomPreset) { + return getPersistedCustomLevels(); + } else { + newLevels = getGlobalPref("equalizer.preset." + presetIndex, mZeroedBandString); + } + + // stored as millibels, convert to decibels + float[] levels = EqUtils.stringBandsToFloats(newLevels); + return EqUtils.convertMillibelsToDecibels(levels); + } + + private float[] getPersistedCustomLevels() { + String newLevels = getGlobalPref("custom", mZeroedBandString); + // stored as millibels, convert to decibels + float[] levels = EqUtils.stringBandsToFloats(newLevels); + return EqUtils.convertMillibelsToDecibels(levels); + } + + /** + * Get preset levels in decibels for a given index + * + * @param presetIndex index which to fetch preset levels for + * @return an array of floats[] with the given index's preset levels + */ + public float[] getPresetLevels(int presetIndex) { + return mEqPresets.get(presetIndex).getLevels(); + } + + /** + * Helper method which maps a preset index to a color value. + * + * @param index the preset index which to fetch a color for + * @return a color which is associated with this preset. + */ + public int getAssociatedPresetColorHex(int index) { + int r = -1; + index = index % mEqPresets.size(); + if (mEqPresets.get(index) instanceof Preset.CustomPreset) { + r = R.color.preset_custom; + } else { + switch (index) { + case 0: + r = R.color.preset_normal; + break; + case 1: + r = R.color.preset_classical; + break; + case 2: + r = R.color.preset_dance; + break; + case 3: + r = R.color.preset_flat; + break; + case 4: + r = R.color.preset_folk; + break; + case 5: + r = R.color.preset_metal; + break; + case 6: + r = R.color.preset_hiphop; + break; + case 7: + r = R.color.preset_jazz; + break; + case 8: + r = R.color.preset_pop; + break; + case 9: + r = R.color.preset_rock; + break; + case 10: + r = R.color.preset_electronic; + break; + case 11: + r = R.color.preset_small_speakers; + break; + default: + return r; + } + } + return mContext.getResources().getColor(r); + } + + /** + * Get total number of presets + * + * @return int value with total number of presets + */ + public int getPresetCount() { + return mEqPresets.size(); + } + + public Preset getPreset(int index) { + return mEqPresets.get(index); + } + + public String getLocalizedPresetName(int index) { + // already localized + return localizePresetName(mEqPresets.get(index).getName()); + } + + private final String localizePresetName(final String name) { + // missing electronic, multimedia, small speakers, custom + final String[] names = { + "Normal", "Classical", "Dance", "Flat", "Folk", + "Heavy Metal", "Hip Hop", "Jazz", "Pop", "Rock", + "Electronic", "Small speakers", "Multimedia", + "Custom" + }; + final int[] ids = { + R.string.normal, R.string.classical, R.string.dance, R.string.flat, R.string.folk, + R.string.heavy_metal, R.string.hip_hop, R.string.jazz, R.string.pop, R.string.rock, + R.string.ci_extreme, R.string.small_speakers, R.string.multimedia, + R.string.user + }; + + for (int i = names.length - 1; i >= 0; --i) { + if (names[i].equalsIgnoreCase(name)) { + return mContext.getString(ids[i]); + } + } + return name; + } + + public boolean isEqualizerLocked() { + return getCurrentPreset() instanceof Preset.CustomPreset + && !(getCurrentPreset() instanceof Preset.PermCustomPreset) + && ((Preset.CustomPreset) getCurrentPreset()).isLocked(); + } + + public void renameCurrentPreset(String s) { + if (UserSession.getInstance() != null) { + UserSession.getInstance().presetRenamed(); + } + + if (isUserPreset()) { + ((Preset.CustomPreset) getCurrentPreset()).setName(s); + } + + mConfig.getCallbacks().notifyPresetsChanged(); + + savePresetsDelayed(); + } + + public boolean removePreset(int index) { + if (UserSession.getInstance() != null) { + UserSession.getInstance().presetRemoved(); + } + + if (index > mEQCustomPresetPosition) { + mEqPresets.remove(index); + mConfig.getCallbacks().notifyPresetsChanged(); + + if (mCurrentPreset == index) { + if (DEBUG) { + Log.w(TAG, "removePreset() called on current preset, changing preset"); + } + updateGlobalLevels(mCurrentPreset - 1); + setPreset(mCurrentPreset - 1); + } + savePresetsDelayed(); + return true; + } + return false; + } + + private void updateGlobalLevels(int presetIndexToCopy) { + final float[] presetLevels = getPresetLevels(presetIndexToCopy); + for (int i = 0; i < mGlobalLevels.length; i++) { + mGlobalLevels[i] = presetLevels[i]; + } + } + + // I AM SO LAZY! + private String getGlobalPref(String key, String defValue) { + return mConfig.getGlobalPrefs().getString(key, defValue); + } + + private void setGlobalPref(String key, String value) { + mConfig.getGlobalPrefs().edit().putString(key, value).apply(); + } + + private String getPref(String key, String defValue) { + return mConfig.getPrefs().getString(key, defValue); + } + + private void setPref(String key, String value) { + mConfig.getPrefs().edit().putString(key, value).apply(); + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/activity/MasterConfigControl.java b/src/org/cyanogenmod/audiofx/audiofx/activity/MasterConfigControl.java new file mode 100644 index 0000000..d83d0de --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/activity/MasterConfigControl.java @@ -0,0 +1,367 @@ +package com.cyngn.audiofx.activity; + +import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; +import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_SCO; +import static android.media.AudioDeviceInfo.TYPE_DOCK; +import static android.media.AudioDeviceInfo.TYPE_IP; +import static android.media.AudioDeviceInfo.TYPE_LINE_ANALOG; +import static android.media.AudioDeviceInfo.TYPE_LINE_DIGITAL; +import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY; +import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES; +import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET; +import static android.media.AudioDeviceInfo.convertDeviceTypeToInternalDevice; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.os.IBinder; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.service.AudioFxService; + +import java.util.ArrayList; +import java.util.List; + +/** + * Master configuration class for AudioFX. + * + * Contains the main hub where data is stored for the current eq graph (which there should be + * one of, thus only once instance of this class exists). + * + * Anyone can obtain an instance of this class. If one does not exist, a new one is created. + * Immediately before the new instance creation happens, some defaults are pre-populated + * with MasterConfigControl.saveDefaults(). That method doesn't ever have to be directly called. + */ +public class MasterConfigControl { + + private static final String TAG = MasterConfigControl.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean SERVICE_DEBUG = false; + + private final Context mContext; + + private AudioFxService.LocalBinder mService; + private ServiceConnection mServiceConnection; + private int mServiceRefCount = 0; + + private AudioDeviceInfo mCurrentDevice; + private AudioDeviceInfo mUserDeviceOverride; + + private final StateCallbacks mCallbacks; + private final EqualizerManager mEqManager; + private final AudioManager mAudioManager; + + private static MasterConfigControl sInstance; + private boolean mShouldBindToService = false; + + public static MasterConfigControl getInstance(Context context) { + if (sInstance == null) { + sInstance = new MasterConfigControl(context); + } + return sInstance; + } + + private MasterConfigControl(Context context) { + mContext = context.getApplicationContext(); + + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + mCallbacks = new StateCallbacks(this); + mEqManager = new EqualizerManager(context, this); + } + + public void onResetDefaults() { + mEqManager.applyDefaults(); + } + + public synchronized boolean bindService() { + boolean conn = true; + if (SERVICE_DEBUG) Log.i(TAG, "bindService() refCount=" + mServiceRefCount); + if (mServiceConnection == null && mServiceRefCount == 0) { + mServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + if (SERVICE_DEBUG) Log.i(TAG, "onServiceConnected refCount=" + mServiceRefCount); + mService = ((AudioFxService.LocalBinder) binder); + LocalBroadcastManager.getInstance(mContext).registerReceiver( + mDeviceChangeReceiver, + new IntentFilter(AudioFxService.ACTION_DEVICE_OUTPUT_CHANGED)); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (SERVICE_DEBUG) Log.w(TAG, "onServiceDisconnected refCount =" + mServiceRefCount); + LocalBroadcastManager.getInstance(mContext).unregisterReceiver( + mDeviceChangeReceiver); + mService = null; + } + }; + + Intent serviceIntent = new Intent(mContext, AudioFxService.class); + conn = mContext.bindService(serviceIntent, mServiceConnection, + Context.BIND_AUTO_CREATE); + } + if (conn) { + mServiceRefCount++; + } + return mServiceRefCount > 0; + } + + public synchronized void unbindService() { + if (SERVICE_DEBUG) Log.i(TAG, "unbindService() called refCount=" + mServiceRefCount); + if (mServiceRefCount > 0) { + mServiceRefCount--; + if (mServiceRefCount == 0) { + mContext.unbindService(mServiceConnection); + mService = null; + mServiceConnection = null; + } + } + } + + public boolean checkService() { + if (mService == null && mServiceRefCount == 0 && mShouldBindToService) { + Log.e(TAG, "Service went away, rebinding"); + bindService(); + } + return mService != null; + } + + private final BroadcastReceiver mDeviceChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int device = intent.getIntExtra("device", -1); + Log.d(TAG, "deviceChanged: " + device); + if (device > -1) { + AudioDeviceInfo info = getDeviceById(device); + if (info != null) { + setCurrentDevice(info, false); + } + } + } + }; + + public void updateService(int flags) { + if (checkService()) { + mService.update(flags); + } + } + + public StateCallbacks getCallbacks() { + return mCallbacks; + } + + public EqualizerManager getEqualizerManager() { + return mEqManager; + } + + public synchronized void setCurrentDeviceEnabled(boolean isChecked) { + getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE, isChecked).apply(); + getCallbacks().notifyGlobalToggle(isChecked); + updateService(AudioFxService.ALL_CHANGED); + } + + public synchronized boolean isCurrentDeviceEnabled() { + return getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE, false); + } + + public synchronized SharedPreferences getGlobalPrefs() { + return mContext.getSharedPreferences(Constants.AUDIOFX_GLOBAL_FILE, 0); + } + + /** + * Update the current device used when querying any device-specific values such as the current + * preset, or the user's custom eq preset settings. + * + * @param audioOutputRouting the new device key + */ + public synchronized void setCurrentDevice(AudioDeviceInfo device, final boolean userSwitch) { + + final AudioDeviceInfo current = getCurrentDevice(); + + Log.d(TAG, "setCurrentDevice name=" + (current == null ? null : current.getProductName()) + + " fromUser=" + userSwitch + + " cur=" + (current == null ? null : current.getType()) + + " new=" + (device == null ? null : device.getType())); + + if (userSwitch) { + mUserDeviceOverride = device; + } else { + if (device != null) { + mCurrentDevice = device; + } + mUserDeviceOverride = null; + } + + mEqManager.onPreDeviceChanged(); + + mCallbacks.notifyDeviceChanged(device, userSwitch); + + mEqManager.onPostDeviceChanged(); + } + + public AudioDeviceInfo getSystemDevice() { + if (mCurrentDevice == null) { + final int forMusic = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioDeviceInfo ai : getConnectedDevices()) { + if ((convertDeviceTypeToInternalDevice(ai.getType()) & forMusic) > 0) { + return ai; + } + } + } + return mCurrentDevice; + } + + public boolean isUserDeviceOverride() { + return mUserDeviceOverride != null; + } + + public AudioDeviceInfo getCurrentDevice() { + if (isUserDeviceOverride()) { + return mUserDeviceOverride; + } + return getSystemDevice(); + } + + public AudioDeviceInfo getDeviceById(int id) { + for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) { + if (ai.getId() == id) { + return ai; + } + } + return null; + } + + public List<AudioDeviceInfo> getConnectedDevices(int... filter) { + final List<AudioDeviceInfo> devices = new ArrayList<AudioDeviceInfo>(); + for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) { + if (filter.length == 0) { + devices.add(ai); + } else { + for (int i = 0; i < filter.length; i++) { + if (ai.getType() == filter[i]) { + devices.add(ai); + continue; + } + } + } + } + return devices; + } + + public String getCurrentDeviceIdentifier() { + return getDeviceIdentifierString(getCurrentDevice()); + } + + public SharedPreferences getPrefs() { + return mContext.getSharedPreferences(getCurrentDeviceIdentifier(), 0); + } + + public boolean hasDts() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_DTS, false); + } + + public boolean hasMaxxAudio() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false); + } + + public boolean getMaxxVolumeEnabled() { + return getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, false); + } + + public boolean hasBassBoost() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_BASSBOOST, false); + } + + public boolean hasVirtualizer() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_VIRTUALIZER, false); + } + + public void setMaxxVolumeEnabled(boolean enable) { + getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, enable).apply(); + updateService(AudioFxService.VOLUME_BOOST_CHANGED); + } + + void overrideEqLevels(short band, short level) { + if (checkService()) { + mService.setOverrideLevels(band, level); + } + } + + public static String getDeviceDisplayString(Context context, AudioDeviceInfo info) { + int type = info == null ? -1 : info.getType(); + switch (type) { + case TYPE_WIRED_HEADSET: + case TYPE_WIRED_HEADPHONES: + return context.getString(com.cyngn.audiofx.R.string.device_headset); + case TYPE_LINE_ANALOG: + case TYPE_LINE_DIGITAL: + return context.getString(com.cyngn.audiofx.R.string.device_line_out); + case TYPE_BLUETOOTH_SCO: + case TYPE_BLUETOOTH_A2DP: + case TYPE_USB_DEVICE: + case TYPE_USB_ACCESSORY: + case TYPE_DOCK: + case TYPE_IP: + return info.getProductName().toString(); + default: + return context.getString(com.cyngn.audiofx.R.string.device_speaker); + } + } + + private static String appendProductName(AudioDeviceInfo info, String prefix) { + StringBuilder nm = new StringBuilder(prefix); + if (info != null && info.getProductName() != null) { + nm.append("-").append(info.getProductName().toString().replaceAll("\\W+", "")); + } + return nm.toString(); + } + + private static String appendDeviceAddress(AudioDeviceInfo info, String prefix) { + StringBuilder nm = new StringBuilder(prefix); + if (info != null && info.getAddress() != null) { + nm.append("-").append(info.getAddress().replace(":", "")); + } + return nm.toString(); + } + + public static String getDeviceIdentifierString(AudioDeviceInfo info) { + int type = info == null ? -1 : info.getType(); + switch (type) { + case TYPE_WIRED_HEADSET: + case TYPE_WIRED_HEADPHONES: + return Constants.DEVICE_HEADSET; + case TYPE_LINE_ANALOG: + case TYPE_LINE_DIGITAL: + return Constants.DEVICE_LINE_OUT; + case TYPE_BLUETOOTH_SCO: + case TYPE_BLUETOOTH_A2DP: + return appendDeviceAddress(info, Constants.DEVICE_PREFIX_BLUETOOTH); + case TYPE_USB_DEVICE: + case TYPE_USB_ACCESSORY: + case TYPE_DOCK: + return appendProductName(info, Constants.DEVICE_PREFIX_USB); + case TYPE_IP: + return appendProductName(info, Constants.DEVICE_PREFIX_CAST); + default: + return Constants.DEVICE_SPEAKER; + } + } + + /** + * Set whether to automatically attempt to bind to the service. + * @param bindToService + */ + public void setAutoBindToService(boolean bindToService) { + mShouldBindToService = bindToService; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/activity/StateCallbacks.java b/src/org/cyanogenmod/audiofx/audiofx/activity/StateCallbacks.java new file mode 100644 index 0000000..0aa7379 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/activity/StateCallbacks.java @@ -0,0 +1,154 @@ + +package com.cyngn.audiofx.activity; + +import android.media.AudioDeviceInfo; + +import java.util.ArrayList; +import java.util.List; + +public class StateCallbacks { + + private static final String TAG = "StateCallbacks"; + + private final MasterConfigControl mConfig; + + private final List<EqUpdatedCallback> mEqUpdateCallbacks = new ArrayList<EqUpdatedCallback>(); + + private final List<DeviceChangedCallback> mDeviceChangedCallbacks = new ArrayList<DeviceChangedCallback>(); + + private final List<EqControlStateCallback> mEqControlStateCallbacks = new ArrayList<EqControlStateCallback>(); + + StateCallbacks(MasterConfigControl config) { + mConfig = config; + } + + /** + * Implement this callback to receive any changes called to the + * MasterConfigControl instance + */ + public interface EqUpdatedCallback { + /** + * A band level has been changed + * + * @param band the band index which changed + * @param dB the new decibel value + * @param fromSystem whether the event was from the system or from the + * user + */ + public void onBandLevelChange(int band, float dB, boolean fromSystem); + + /** + * The preset has been set + * + * @param newPresetIndex the new preset index. + */ + public void onPresetChanged(int newPresetIndex); + + public void onPresetsChanged(); + } + + public void addEqUpdatedCallback(EqUpdatedCallback callback) { + synchronized (mEqUpdateCallbacks) { + mEqUpdateCallbacks.add(callback); + } + } + + public void removeEqUpdatedCallback(EqUpdatedCallback callback) { + synchronized (mEqUpdateCallbacks) { + mEqUpdateCallbacks.remove(callback); + } + } + + void notifyPresetsChanged() { + synchronized (mEqUpdateCallbacks) { + for (final EqUpdatedCallback callback : mEqUpdateCallbacks) { + callback.onPresetsChanged(); + } + } + } + + void notifyPresetChanged(final int index) { + synchronized (mEqUpdateCallbacks) { + for (final EqUpdatedCallback callback : mEqUpdateCallbacks) { + callback.onPresetChanged(index); + } + } + } + + void notifyBandLevelChangeChanged(final int band, final float dB, final boolean fromSystem) { + synchronized (mEqUpdateCallbacks) { + for (final EqUpdatedCallback callback : mEqUpdateCallbacks) { + callback.onBandLevelChange(band, dB, fromSystem); + } + } + } + + /** + * Callback for changes to visibility and state of the EQ + */ + public interface EqControlStateCallback { + public void updateEqState(boolean saveVisible, boolean removeVisible, + boolean renameVisible, boolean unlockVisible); + } + + public void addEqControlStateCallback(EqControlStateCallback callback) { + synchronized (mEqControlStateCallbacks) { + mEqControlStateCallbacks.add(callback); + } + } + + public synchronized void removeEqControlStateCallback(EqControlStateCallback callback) { + synchronized (mEqControlStateCallbacks) { + mEqControlStateCallbacks.remove(callback); + } + } + + void notifyEqControlStateChanged(boolean saveVisible, boolean removeVisible, + boolean renameVisible, boolean unlockVisible) { + synchronized (mEqControlStateCallbacks) { + for (final EqControlStateCallback callback : mEqControlStateCallbacks) { + callback.updateEqState(saveVisible, removeVisible, renameVisible, unlockVisible); + } + } + } + + /** + * Register this callback to receive notification when the output device + * changes. + */ + public interface DeviceChangedCallback { + public void onDeviceChanged(AudioDeviceInfo device, boolean userChange); + public void onGlobalDeviceToggle(boolean on); + + } + + public void addDeviceChangedCallback(DeviceChangedCallback callback) { + synchronized (mDeviceChangedCallbacks) { + mDeviceChangedCallbacks.add(callback); + callback.onDeviceChanged(mConfig.getCurrentDevice(), false); + } + } + + public synchronized void removeDeviceChangedCallback(DeviceChangedCallback callback) { + synchronized (mDeviceChangedCallbacks) { + mDeviceChangedCallbacks.remove(callback); + } + } + + void notifyGlobalToggle(boolean on) { + synchronized (mDeviceChangedCallbacks) { + for (DeviceChangedCallback callback : mDeviceChangedCallbacks) { + callback.onGlobalDeviceToggle(on); + } + + } + } + + void notifyDeviceChanged(final AudioDeviceInfo newDevice, final boolean fromUser) { + synchronized (mDeviceChangedCallbacks) { + for (final DeviceChangedCallback callback : mDeviceChangedCallbacks) { + callback.onDeviceChanged(newDevice, fromUser); + } + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/backends/AndroidEffects.java b/src/org/cyanogenmod/audiofx/audiofx/backends/AndroidEffects.java new file mode 100644 index 0000000..090eb73 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/backends/AndroidEffects.java @@ -0,0 +1,185 @@ +package com.cyngn.audiofx.backends; + +import android.media.AudioDeviceInfo; +import android.media.audiofx.AudioEffect; +import android.media.audiofx.BassBoost; +import android.media.audiofx.PresetReverb; +import android.media.audiofx.Virtualizer; +import android.util.Log; + +import com.cyngn.audiofx.Constants; + +/** + * EffectSet which comprises standard Android effects + */ +class AndroidEffects extends EffectSetWithAndroidEq { + + /** + * Session-specific bassboost + */ + private BassBoost mBassBoost; + + /** + * Session-specific virtualizer + */ + private Virtualizer mVirtualizer; + + /** + * Session-specific reverb + */ + private PresetReverb mPresetReverb; + + public AndroidEffects(int sessionId, AudioDeviceInfo deviceInfo) { + super(sessionId, deviceInfo); + } + + @Override + protected void onCreate() { + super.onCreate(); + + mBassBoost = new BassBoost(100, mSessionId); + mVirtualizer = new Virtualizer(100, mSessionId); + mPresetReverb = new PresetReverb(100, mSessionId); + } + + @Override + public void release() { + super.release(); + + try { + if (mBassBoost != null) { + mBassBoost.release(); + } + } catch (Exception e) { + // ignored; + } + try { + if (mVirtualizer != null) { + mVirtualizer.release(); + } + } catch (Exception e) { + // ignored + } + try { + if (mPresetReverb != null) { + mPresetReverb.release(); + } + } catch (Exception e) { + // ignored + } + mBassBoost = null; + mVirtualizer = null; + mPresetReverb = null; + } + + @Override + public synchronized void setDevice(AudioDeviceInfo deviceInfo) { + super.setDevice(deviceInfo); + } + + @Override + public void setGlobalEnabled(boolean globalEnabled) { + super.setGlobalEnabled(globalEnabled); + + if (!globalEnabled) { + // disable everything. it will get explictly enabled + // individually when necessary. + try { + if (mVirtualizer != null) { + mVirtualizer.setEnabled(false); + } + } catch (Exception e) { + Log.e(TAG, "Unable to disable virtualizer!", e); + } + try { + if (mBassBoost != null) { + mBassBoost.setEnabled(false); + } + } catch (Exception e) { + Log.e(TAG, "Unable to disable bass boost!", e); + } + try { + if (mPresetReverb != null) { + mPresetReverb.setEnabled(false); + } + } catch (Exception e) { + Log.e(TAG, "Unable to disable reverb!", e); + } + } + } + + @Override + public boolean hasVirtualizer() { + return mVirtualizer != null && mVirtualizer.getStrengthSupported(); + } + + @Override + public boolean hasBassBoost() { + return mBassBoost != null && mBassBoost.getStrengthSupported(); + } + + @Override + public void enableBassBoost(boolean enable) { + try { + if (mBassBoost != null) { + mBassBoost.setEnabled(enable); + } + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " bass boost!", e); + } + } + + @Override + public void setBassBoostStrength(short strength) { + setParameterSafe(mBassBoost, BassBoost.PARAM_STRENGTH, strength); + } + + @Override + public void enableVirtualizer(boolean enable) { + try { + if (mVirtualizer != null) { + mVirtualizer.setEnabled(enable); + } + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " virtualizer!", e); + } + } + + @Override + public void setVirtualizerStrength(short strength) { + setParameterSafe(mVirtualizer, Virtualizer.PARAM_STRENGTH, strength); + } + + @Override + public void enableReverb(boolean enable) { + try { + if (mPresetReverb != null) { + mPresetReverb.setEnabled(enable); + } + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " preset reverb!", e); + } + + } + + @Override + public void setReverbPreset(short preset) { + setParameterSafe(mPresetReverb, PresetReverb.PARAM_PRESET, preset); + } + + @Override + public int getBrand() { + return Constants.EFFECT_TYPE_ANDROID; + } + + private void setParameterSafe(AudioEffect e, int p, short v) { + if (e == null) { + return; + } + try { + e.setParameter(p, v); + } catch (Exception ex) { + Log.e(TAG, "Failed to set param " + p + " for effect " + e.getDescriptor().name, ex); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSet.java b/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSet.java new file mode 100644 index 0000000..47ae894 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSet.java @@ -0,0 +1,235 @@ +package com.cyngn.audiofx.backends; + +import android.media.AudioDeviceInfo; +import android.util.Log; + +/** + * Helper class representing the full complement of effects attached to one + * audio session. + */ +public abstract class EffectSet { + + protected static final String TAG = "AudioFx-EffectSet"; + + protected final int mSessionId; + + protected boolean mGlobalEnabled; + + private AudioDeviceInfo mDeviceInfo; + + private boolean mMarkedForDeath = false; + + public EffectSet(int sessionId, AudioDeviceInfo deviceInfo) { + mSessionId = sessionId; + mDeviceInfo = deviceInfo; + try { + onCreate(); + } catch (Exception e) { + Log.e(TAG, "error creating" + this + ", releasing and throwing!"); + release(); + throw e; + } + } + + /** + * Called to do subclass-first initialization in case + * an implementation has ordering restrictions. + * + * This call is wrapped in a try/catch - if an exception is thrown here, + * a release will immediately be called. + */ + protected void onCreate() { } + + /** + * Destroy all effects in this set. + * + * Attempting to use this object after calling release is + * undefined behavior. + */ + public void release() { } + + /** + * Returns the enumerated brand of this implementation + * @return brandId + */ + public abstract int getBrand(); + + /** + * Called when the user toggles the engine on or off. If the + * implementation has a built-in bypass mode, this is where + * to use it. + * + * @param globalEnabled + */ + public void setGlobalEnabled(boolean globalEnabled) { + mGlobalEnabled = globalEnabled; + } + + public boolean isGlobalEnabled() { + return mGlobalEnabled; + } + + /** + * Called when the output device has changed. All cached + * data should be cleared at this point. + * + * @param deviceInfo + */ + public void setDevice(AudioDeviceInfo deviceInfo) { + mDeviceInfo = deviceInfo; + } + + /** + * Return the current active output device + * @return deviceInfo + */ + public AudioDeviceInfo getDevice() { + return mDeviceInfo; + } + + /** + * Begin bulk-update of parameters. This can be used if the + * implementation supports operation in a transactional/atomic + * manner. Parameter changes will immediately follow this call + * and should be committed to the backend when the subsequent + * commitUpdate() is called. + * + * Optional. + * + * @return status - false on failure + */ + public boolean beginUpdate() { return true; } + + /** + * Commit accumulated updates to the backend. See above. + * + * begin/commit are used when a large number of parameters need + * to be sent to the backend, such as in the case of a device + * switch or preset change. This can increase performance and + * reduce click/pop issues. + * + * Optional. + * + * @return status - false on failure + */ + public boolean commitUpdate() { return true; } + + /* ---- Top level effects begin here ---- */ + + // required effects + public abstract boolean hasVirtualizer(); + + public abstract boolean hasBassBoost(); + + // optional effects + public boolean hasTrebleBoost() { + return false; + } + + public boolean hasVolumeBoost() { + return false; + } + + public boolean hasReverb() { + return false; + } + + public abstract void enableEqualizer(boolean enable); + + /** + * @param levels in decibels + */ + public abstract void setEqualizerLevelsDecibels(float[] levels); + + public abstract short getNumEqualizerBands(); + + /** + * @param band + * @param level in millibels + */ + public abstract void setEqualizerBandLevel(short band, float level); + + /** + * @return level in millibels + */ + public abstract int getEqualizerBandLevel(short band); + + public abstract String getEqualizerPresetName(short preset); + + public abstract void useEqualizerPreset(short preset); + + public abstract short getNumEqualizerPresets(); + + public abstract short[] getEqualizerBandLevelRange(); + + /** + * @param band + * @return center frequency of the band in millihertz + */ + public abstract int getCenterFrequency(short band); + + public abstract void enableBassBoost(boolean enable); + + /** + * @param strength with range [0-1000] + */ + public abstract void setBassBoostStrength(short strength); + + public abstract void enableVirtualizer(boolean enable); + + /** + * @param strength with range [0-1000] + */ + public abstract void setVirtualizerStrength(short strength); + + public void enableReverb(boolean enable) { + return; + } + + public void setReverbPreset(short preset) { + return; + } + + public void enableTrebleBoost(boolean enable) { + return; + } + + /** + * @param strength with range [0-100] + */ + public void setTrebleBoostStrength(short strength) { + return; + } + + public void enableVolumeBoost(boolean enable) { + return; + } + + /** + * How long should we delay for when releasing the effects? + * This helps certain effect implementations when the + * app is reusing a session ID. By default this + * behavior is disabled. + */ + public int getReleaseDelay() { + return 0; + } + + public boolean isMarkedForDeath() { + return mMarkedForDeath; + } + + public void setMarkedForDeath(boolean die) { + mMarkedForDeath = die; + } + + @Override + public String toString() { + return "EffectSet (" + this.getClass().getSimpleName() + ")" + + " [ " + + " mSessionId: " + mSessionId + + " mDeviceInfo: " + mDeviceInfo + + " mGlobalEnabled: " + mGlobalEnabled + + " ]"; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSetWithAndroidEq.java b/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSetWithAndroidEq.java new file mode 100644 index 0000000..81cd53c --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/backends/EffectSetWithAndroidEq.java @@ -0,0 +1,123 @@ +package com.cyngn.audiofx.backends; + +import android.media.AudioDeviceInfo; +import android.media.audiofx.Equalizer; +import android.util.Log; + +import com.cyngn.audiofx.eq.EqUtils; + +/** + * Created by roman on 3/1/16. + */ +public abstract class EffectSetWithAndroidEq extends EffectSet { + /** + * Session-specific equalizer + */ + private Equalizer mEqualizer; + + private short mEqNumPresets = -1; + private short mEqNumBands = -1; + + public EffectSetWithAndroidEq(int sessionId, AudioDeviceInfo deviceInfo) { + super(sessionId, deviceInfo); + } + + @Override + protected void onCreate() { + mEqualizer = new Equalizer(100, mSessionId); + super.onCreate(); + + } + + @Override + public synchronized void release() { + if (mEqualizer != null) { + mEqualizer.release(); + mEqualizer = null; + } + super.release(); + } + + @Override + public void setGlobalEnabled(boolean globalEnabled) { + super.setGlobalEnabled(globalEnabled); + + enableEqualizer(globalEnabled); + } + + @Override + public void enableEqualizer(boolean enable) { + try { + mEqualizer.setEnabled(enable); + } catch (Exception e) { + Log.e(TAG, "enableEqualizer failed! enable=" + enable + " sessionId=" + mSessionId, e); + } + } + + @Override + public void setEqualizerLevelsDecibels(float[] levels) { + final short[] equalizerLevels = EqUtils.convertDecibelsToMillibelsInShorts(levels); + for (short i = 0; i < equalizerLevels.length; i++) { + setBandLevelSafe(i, equalizerLevels[i]); + } + } + + @Override + public short getNumEqualizerBands() { + if (mEqNumBands < 0) { + mEqNumBands = mEqualizer.getNumberOfBands(); + } + return mEqNumBands; + } + + @Override + public void setEqualizerBandLevel(short band, float level) { + setBandLevelSafe(band, (short)level); + } + + @Override + public int getEqualizerBandLevel(short band) { + return mEqualizer.getBandLevel(band); + } + + @Override + public String getEqualizerPresetName(short preset) { + return mEqualizer.getPresetName(preset); + } + + @Override + public void useEqualizerPreset(short preset) { + mEqualizer.usePreset(preset); + } + + @Override + public short getNumEqualizerPresets() { + if (mEqNumPresets < 0) { + mEqNumPresets = mEqualizer.getNumberOfPresets(); + } + return mEqNumPresets; + } + + @Override + public short[] getEqualizerBandLevelRange() { + return mEqualizer.getBandLevelRange(); + } + + @Override + public int getCenterFrequency(short band) { + return mEqualizer.getCenterFreq(band); + } + + @Override + public synchronized void setDevice(AudioDeviceInfo deviceInfo) { + super.setDevice(deviceInfo); + } + + private synchronized void setBandLevelSafe(short band, short level) { + try { + mEqualizer.setBandLevel(band, level); + } catch (Exception e) { + Log.e(TAG, "Unable to set eq band=" + band + " level=" + level, e); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/backends/IEffectFactory.java b/src/org/cyanogenmod/audiofx/audiofx/backends/IEffectFactory.java new file mode 100644 index 0000000..8e629b8 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/backends/IEffectFactory.java @@ -0,0 +1,16 @@ +package com.cyngn.audiofx.backends; + +import android.content.Context; +import android.media.AudioDeviceInfo; + +interface IEffectFactory { + + /** + * Create a new EffectSet based on current stream parameters. + * @param context context to create the effect with + * @param sessionId session id to attach the effect to + * @param currentDevice current device that the effect should initially setup for + * @return an {@link EffectSet} + */ + EffectSet createEffectSet(Context context, int sessionId, AudioDeviceInfo currentDevice); +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/eq/EqBarView.java b/src/org/cyanogenmod/audiofx/audiofx/eq/EqBarView.java new file mode 100644 index 0000000..df697dd --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/eq/EqBarView.java @@ -0,0 +1,208 @@ +package com.cyngn.audiofx.eq; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.activity.StateCallbacks; + +public class EqBarView extends FrameLayout implements StateCallbacks.EqUpdatedCallback { + + private static final String TAG = EqBarView.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private EqualizerManager mEqManager; + + private float mNormalWidth; + private float mParentHeight = -1; + private float mLastTouchX; + private float mLastTouchY; + private float mPosX; + private float mPosY = -1; + private boolean mUserInteracting; + private int mParentTop; + private Integer mIndex; + private float mInitialLevel; + + public EqBarView(Context context) { + super(context); + init(); + } + + public EqBarView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public EqBarView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + mEqManager = MasterConfigControl.getInstance(mContext).getEqualizerManager(); + mNormalWidth = getResources().getDimension(R.dimen.eq_bar_width); + } + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + private EqContainerView.EqBandInfo getInfo() { + return (EqContainerView.EqBandInfo) getTag(); + } + + public void setParentHeight(float h, int top) { + mParentHeight = h; + mParentTop = top; + updateHeight(); + } + + void updateHeight() { + if (DEBUG) Log.d(TAG, "updateHeight()"); + + if (getInfo() != null) { + float level = mEqManager.getLevel(getIndex()); + float yProjection = 1 - mEqManager.projectY(level); + float height = (yProjection * (mParentHeight)); + mPosY = height; + + if (DEBUG) { + Log.d(TAG, getIndex() + "level: " + level + ", yProjection: " + + yProjection + ", mPosY: " + mPosY); + } + updateHeight((int) mPosY); + } else { + if (DEBUG) Log.d(TAG, "could not updateHeight()"); + } + } + + public int getIndex() { + if (mIndex == null) { + mIndex = (getInfo()).mIndex; + } + return mIndex; + } + + public boolean isUserInteracting() { + return mUserInteracting; + } + + /* package */ void startInteraction(float x, float y) { + + mLastTouchX = x; + mLastTouchY = y; + mUserInteracting = true; + + if (DEBUG) Log.d(TAG, "initial level: " + mInitialLevel); + mInitialLevel = (1 - (mPosY / mParentHeight)) * (mEqManager.getMinDB() - mEqManager.getMaxDB()) + - mEqManager.getMinDB(); + + updateWidth((int) (mNormalWidth * 2)); + } + + /* package */ void endInteraction() { + mUserInteracting = false; + + updateWidth((int) mNormalWidth); + } + + private void updateHeight(int h) { + if (!isInLayout()) { + final ViewGroup.LayoutParams params = getLayoutParams(); + params.height = h; + setLayoutParams(params); + } + } + + private void updateWidth(int w) { + final ViewGroup.LayoutParams params = getLayoutParams(); + params.width = w; + setLayoutParams(params); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mEqManager.isEqualizerLocked()) { + return false; + } + + final float x = event.getRawX(); + final float y = event.getRawY() - mParentTop; + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + startInteraction(x, y); + break; + + case MotionEvent.ACTION_MOVE: + // Calculate the distance moved + final float dx = x - mLastTouchX; + final float dy = y - mLastTouchY; + + mPosX += dx; + mPosY -= dy; + + // Remember this touch position for the next move event + mLastTouchX = x; + mLastTouchY = y; + + int wy = (int) mParentHeight; + float level = (1 - (mPosY / wy)) * (mEqManager.getMinDB() - mEqManager.getMaxDB()) + - mEqManager.getMinDB(); + + if (DEBUG) Log.d(TAG, "new level: " + level); + if (level < mEqManager.getMinDB()) { + level = mEqManager.getMinDB(); + } else if (level > mEqManager.getMaxDB()) { + level = mEqManager.getMaxDB(); + } + + if (mInitialLevel != level) { + mEqManager.setLevel(getInfo().mIndex, level, false); + } else { + updateHeight(); + } + + break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + endInteraction(); + break; + + } + + return true; + } + + public float getPosY() { + return mPosY; + } + + @Override + public void onBandLevelChange(int band, float dB, boolean fromSystem) { + if (getInfo().mIndex != band) { + return; + } + + updateHeight(); + } + + @Override + public void onPresetChanged(int newPresetIndex) { + + } + + @Override + public void onPresetsChanged() { + + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/eq/EqContainerView.java b/src/org/cyanogenmod/audiofx/audiofx/eq/EqContainerView.java new file mode 100644 index 0000000..51b58a6 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/eq/EqContainerView.java @@ -0,0 +1,518 @@ +package com.cyngn.audiofx.eq; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.DashPathEffect; +import android.graphics.Paint; +import android.graphics.Path; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.activity.StateCallbacks; + +import java.util.ArrayList; +import java.util.List; + +public class EqContainerView extends FrameLayout + implements StateCallbacks.EqUpdatedCallback, StateCallbacks.EqControlStateCallback { + + private static final String TAG = EqContainerView.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private int mWidth; + private int mHeight; + private MasterConfigControl mConfig; + private EqualizerManager mEqManager; + private List<EqBandInfo> mBandInfo; + private List<EqBarView> mBarViews; + private List<Integer> mSelectedBands; + + private CheckBox mLockBox; + private ImageView mRenameControl; + private ImageView mRemoveControl; + private ImageView mSaveControl; + private ViewGroup mControls; + private boolean mControlsVisible; + + private boolean mSaveVisible; + private boolean mRemoveVisible; + private boolean mRenameVisible; + private boolean mUnlockVisible; + + private int mSelectedBandColor; + private boolean mFirstLayout = true; + + private Paint mTextPaint; + private Paint mFreqPaint; + private Paint mSelectedFreqPaint; + private Paint mCenterLinePaint; + private Path mDashPath; + + private Handler mHandler; + + private Runnable mVibrateRunnable = new Runnable() { + @Override + public void run() { + Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + v.vibrate(30); + } + }; + + private int mPaddingTop; + private int mPaddingBottom; + private int mBarWidth; + private int mBarSeparation; + private int mBarBottomGrabSpacePadding; + + public void stopListening() { + for (EqBarView barView : mBarViews) { + barView.setTag(null); + mConfig.getCallbacks().removeEqUpdatedCallback(barView); + } + mConfig.getCallbacks().removeEqUpdatedCallback(this); + } + + public void startListening() { + for (int i = 0; i < mBandInfo.size(); i++) { + + final EqBarView eqBarView = mBarViews.get(i); + eqBarView.setTag(mBandInfo.get(i)); + mConfig.getCallbacks().addEqUpdatedCallback(eqBarView); + } + mConfig.getCallbacks().addEqUpdatedCallback(this); + } + + public static class EqBandInfo { + public int mIndex; + + public String mFreq; + public String mDb; + public EqBarView mBar; + } + + public EqContainerView(Context context) { + super(context); + init(); + } + + public EqContainerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public EqContainerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + setLayerType(LAYER_TYPE_HARDWARE, null); + + mHandler = new Handler(); + + final Resources r = getResources(); + + mBarWidth = r.getDimensionPixelSize(R.dimen.eq_bar_width); + mBarSeparation = r.getDimensionPixelSize(R.dimen.separator_width); + + mBarBottomGrabSpacePadding = r.getDimensionPixelSize(R.dimen.eq_bar_bottom_grab_space); + int freqTextSize = r.getDimensionPixelSize(R.dimen.eq_label_text_size); + int selectedBoxTextSize = r.getDimensionPixelSize(R.dimen.eq_selected_box_height); + + int extraTopSpace = r.getDimensionPixelSize(R.dimen.eq_bar_top_padding); + + mPaddingTop = selectedBoxTextSize + extraTopSpace; + mPaddingBottom = selectedBoxTextSize + mBarBottomGrabSpacePadding; + + mConfig = MasterConfigControl.getInstance(mContext); + mEqManager = mConfig.getEqualizerManager(); + + mBarViews = new ArrayList<>(); + mBandInfo = new ArrayList<>(); + mSelectedBands = new ArrayList<>(); + + setWillNotDraw(false); + + mSelectedBandColor = r.getColor(R.color.band_bar_color_selected); + + mTextPaint = new Paint(); + mTextPaint.setAntiAlias(true); + mTextPaint.setColor(Color.WHITE); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setElegantTextHeight(true); + mTextPaint.setTextSize(selectedBoxTextSize); + + mFreqPaint = new Paint(); + mFreqPaint.setAntiAlias(true); + mFreqPaint.setColor(Color.WHITE); + mFreqPaint.setTextAlign(Paint.Align.CENTER); + mFreqPaint.setTextSize(freqTextSize); + + mSelectedFreqPaint = new Paint(mFreqPaint); + mSelectedFreqPaint.setAntiAlias(true); + mSelectedFreqPaint.setTextSize(selectedBoxTextSize); + + mCenterLinePaint = new Paint(); + mCenterLinePaint.setColor(Color.WHITE); + mCenterLinePaint.setAntiAlias(true); + mCenterLinePaint.setPathEffect(new DashPathEffect(new float[]{6, 6}, 0)); + mCenterLinePaint.setStyle(Paint.Style.STROKE); + mCenterLinePaint.setAntiAlias(true); + + getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + generateAndAddBars(); + } + }); + } + + @Override + public boolean hasOverlappingRendering() { + return true; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + mControls = (ViewGroup) findViewById(R.id.eq_controls); + + mLockBox = (CheckBox) findViewById(R.id.lock); + mLockBox.setOnCheckedChangeListener(mEqManager.getLockChangeListener()); + + mRenameControl = (ImageView) findViewById(R.id.rename); + mRemoveControl = (ImageView) findViewById(R.id.remove); + mSaveControl = (ImageView) findViewById(R.id.save); + } + + @Override + protected void onAttachedToWindow() { + if (DEBUG) Log.d(TAG, "onAttachedToWindow()"); + super.onAttachedToWindow(); + + mConfig.getCallbacks().addEqControlStateCallback(this); + onPresetChanged(mEqManager.getCurrentPresetIndex()); // update initial state + } + + @Override + protected void onDetachedFromWindow() { + if (DEBUG) Log.d(TAG, "onDetachedFromWindow()"); + mConfig.getCallbacks().removeEqControlStateCallback(this); + super.onDetachedFromWindow(); + } + + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + + private void generateAndAddBars() { + if (mFirstLayout) { + mFirstLayout = false; + mBarViews.clear(); + + for (int i = 0; i < mEqManager.getNumBands(); i++) { + final EqBandInfo band = new EqBandInfo(); + band.mIndex = i; + mBandInfo.add(band); + + final EqBarView bar = new EqBarView(mContext); + band.mBar = bar; + bar.setTag(band); + bar.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mEqManager.isEqualizerLocked()) { + return false; + } + switch (event.getActionMasked()) { + + case MotionEvent.ACTION_DOWN: + startBarInteraction(bar); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + stopBarInteraction(bar); + break; + } + + return false; + } + }); + + // set correct initial alpha + if (i % 2 == 0) { + bar.setAlpha(0.6f); + } else { + bar.setAlpha(0.8f); + } + bar.setBackgroundColor(Color.WHITE); + bar.setElevation(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, + getResources().getDisplayMetrics())); + + addView(bar, getFrameParams(i)); + bar.setParentHeight(mHeight, getTop()); + + final float freq = mEqManager.getCenterFreq(i); + String frequencyText = String.format(freq < 1000 ? "%.0f" : "%.0fk", + freq < 1000 ? freq : freq / 1000); + band.mFreq = frequencyText; + mBarViews.add(bar); + } + updateSelectedBands(); + } else { + for (EqBarView barView : mBarViews) { + barView.setParentHeight(mHeight, getTop()); + } + } + } + + public EqBarView startTouchingBarUnder(MotionEvent event) { + EqBarView foundBar = findBar(event.getX(), event.getY(), mBarViews); + if (foundBar != null) { + foundBar.updateHeight(); + + foundBar.startInteraction(event.getRawX(), event.getRawY()); + startBarInteraction(foundBar); + } + return foundBar; + } + + public void startBarInteraction(EqBarView bar) { + setControlsVisible(false, false); + EqBandInfo band = (EqBandInfo) bar.getTag(); + mSelectedBands.add(band.mIndex); + updateSelectedBands(); + AsyncTask.execute(mVibrateRunnable); + } + + public void stopBarInteraction(EqBarView bar) { + EqBandInfo band = (EqBandInfo) bar.getTag(); + mSelectedBands.remove((Integer) band.mIndex); + updateSelectedBands(); + setControlsVisible(mControlsVisible, true); + } + + private EqBarView findBar(float x, float y, List<EqBarView> targets) { + final int count = targets.size(); + for (int i = 0; i < count; i++) { + final EqBarView target = targets.get(i); + if (target.getRight() > x && target.getTop() < y + && target.getBottom() > y && target.getLeft() < x) { + return target; + } + } + return null; + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mWidth = w; + mHeight = h - mPaddingTop - mPaddingBottom; + generateAndAddBars(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + //--------------------------------------------------- + + if (mFirstLayout) + return; + + int dashY = bottom - mPaddingBottom - (mHeight / 2); + + final int widthOfBars = (mEqManager.getNumBands() * mBarWidth) + + ((mEqManager.getNumBands() - 1) * mBarSeparation); + final int freeSpace = mWidth - widthOfBars; + + int mCurLeft = (freeSpace / 2); + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + + if (child instanceof EqBarView) { + final int childWidth = child.getMeasuredWidth(); + final int childHeight = child.getMeasuredHeight(); + + int l = mCurLeft; + int r = l + mBarWidth; + + mCurLeft += mBarWidth + mBarSeparation; + + if (((EqBarView) child).isUserInteracting()) { + l -= childWidth / 4; + r += childWidth / 4; + } + + final int layoutTop = top + mHeight - childHeight + mPaddingTop; + final int layoutBottom = layoutTop + childHeight + + mPaddingBottom - (mPaddingBottom - mBarBottomGrabSpacePadding); + child.layout(l, layoutTop, r, layoutBottom); + } + } + + if (changed || mDashPath == null) { + mDashPath = new Path(); + mDashPath.reset(); + mDashPath.moveTo(freeSpace / 2, dashY); + mDashPath.lineTo(widthOfBars + (freeSpace / 2), dashY); + } + + mControls.layout( + right - mControls.getMeasuredWidth() - mControls.getPaddingLeft(), + top + mControls.getPaddingTop(), + right - mControls.getPaddingRight(), + top + mControls.getMeasuredHeight() + mControls.getPaddingTop() + + mControls.getPaddingBottom() + ); + } + + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawPath(mDashPath, mCenterLinePaint); + + for (int i = 0; i < mBandInfo.size(); i++) { + EqBandInfo info = mBandInfo.get(i); + + final float x = info.mBar.getX() + (info.mBar.getWidth() / 2); + final boolean userInteracting = info.mBar.isUserInteracting(); + if (userInteracting) { + canvas.drawText( + info.mDb, + x, + info.mBar.getY() - (mTextPaint.getTextSize() / 2), + mTextPaint); + } + + Paint drawPaint = userInteracting ? mSelectedFreqPaint : mFreqPaint; + + canvas.drawText(info.mFreq, x, + info.mBar.getBottom() + drawPaint.getTextSize(), + drawPaint); + } + } + + private void updateSelectedBands() { + for (int i = 0; i < mEqManager.getNumBands(); i++) { + EqBandInfo tag = mBandInfo.get(i); + final EqBarView bar = (EqBarView) findViewWithTag(tag); + if (bar != null) { + final ViewPropertyAnimator barAnimation = bar.animate().withLayer(); + if (mSelectedBands.isEmpty()) { + if (i % 2 == 0) { + barAnimation.alpha(0.6f); + } else { + barAnimation.alpha(0.8f); + } + } else if (mSelectedBands.contains(i)) { + barAnimation.alpha(1f); + bar.setBackgroundColor(mSelectedBandColor); + } else { + barAnimation.alpha(0.40f); + } + } + } + } + + private FrameLayout.LayoutParams getFrameParams(int index) { + int width = getResources().getDimensionPixelSize(R.dimen.eq_bar_width); + int height = Math.round((1 - mEqManager.projectY(mEqManager.getLevel(index))) * mHeight); + FrameLayout.LayoutParams ll = new FrameLayout.LayoutParams(width, height); + ll.gravity = Gravity.TOP; + return ll; + } + + @Override + public void onBandLevelChange(int band, float dB, boolean fromSystem) { + if (mFirstLayout) return; + mBandInfo.get(band).mDb = dB != 0 ? String.format("%+1.1f", dB) : "0.0"; + invalidate(); + } + + @Override + public void onPresetChanged(int newPresetIndex) { + updateEqState(); + if (mEqManager.isUserPreset()) { + mLockBox.setChecked(mEqManager.isEqualizerLocked()); + } + } + + @Override + public void updateEqState(boolean saveVisible, boolean removeVisible, + boolean renameVisible, boolean unlockVisible) { + mControlsVisible = mEqManager.isUserPreset() || mEqManager.isCustomPreset(); + mSaveVisible = saveVisible; + mRemoveVisible = removeVisible; + mRenameVisible = renameVisible; + mUnlockVisible = unlockVisible; + updateEqState(); + } + + public void updateEqState() { + setControlsVisible(mControlsVisible && mSelectedBands.isEmpty(), false); + + animateControl(mLockBox, mUnlockVisible); + animateControl(mRemoveControl, mRemoveVisible); + animateControl(mRenameControl, mRenameVisible); + animateControl(mSaveControl, mSaveVisible); + } + + private void animateControl(final View v, boolean visible) { + if (visible) { + v.setVisibility(View.VISIBLE); + v.animate() + .alpha(1f) + .setDuration(350) + .withEndAction(null); + } else { + v.animate() + .alpha(0f) + .setDuration(350) + .withEndAction(new Runnable() { + @Override + public void run() { + v.setVisibility(View.INVISIBLE); + } + }); + } + } + + @Override + public void onPresetsChanged() { + } + + public void setControlsVisible(boolean visible, boolean keepChange) { + if (keepChange) { + mControlsVisible = visible; + } + + if (mControls != null) { + animateControl(mControls, visible); + } + } + +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/eq/EqSwipeController.java b/src/org/cyanogenmod/audiofx/audiofx/eq/EqSwipeController.java new file mode 100644 index 0000000..e96d944 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/eq/EqSwipeController.java @@ -0,0 +1,133 @@ +package com.cyngn.audiofx.eq; + +import android.content.Context; +import android.provider.Settings; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.preset.InfiniteViewPager; + +public class EqSwipeController extends LinearLayout { + + /* + * x velocity max for deciding whether to try to grab a bar + */ + private static final int X_VELOCITY_THRESH = 20; + + private static final int MINIMUM_TIME_HOLD_TIME = 100; + + EqContainerView mEq; + InfiniteViewPager mPager; + private VelocityTracker mVelocityTracker = null; + long mDownTime; + EqBarView mBar; + boolean mBarActive; + private ViewGroup mControls; + + private final EqualizerManager mEqManager; + private float mDownPositionX; + private float mDownPositionY; + + public EqSwipeController(Context context, AttributeSet attrs) { + super(context, attrs); + mEqManager = MasterConfigControl.getInstance(context).getEqualizerManager(); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mEq = (EqContainerView) findViewById(R.id.eq_container); + mPager = (InfiniteViewPager) findViewById(R.id.pager); + mControls = (ViewGroup) findViewById(R.id.eq_controls); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + + // don't intercept touches over the EQ controls + if (mControls.getRight() > x && mControls.getTop() < y + && mControls.getBottom() > y && mControls.getLeft() < x) { + return false; + } + + return true; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int index = event.getActionIndex(); + int action = event.getActionMasked(); + int pointerId = event.getPointerId(index); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mDownPositionX = event.getRawX(); + mDownPositionY = event.getRawY(); + mDownTime = System.currentTimeMillis(); + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + mVelocityTracker.addMovement(event); + break; + case MotionEvent.ACTION_MOVE: + if (mVelocityTracker != null) { + mVelocityTracker.addMovement(event); + mVelocityTracker.computeCurrentVelocity(1000); + float xVelocity = mVelocityTracker.getXVelocity(pointerId); + float yVelocity = mVelocityTracker.getYVelocity(pointerId); + + final float deltaX = mDownPositionX - event.getRawX(); + final float deltaY = mDownPositionY - event.getRawY(); + final float distanceSquared = deltaX * deltaX + deltaY * deltaY; + + final ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext()); + final int touchSlop = viewConfiguration.getScaledTouchSlop(); + + if (!mBarActive && !mEqManager.isChangingPresets() + && !mEqManager.isEqualizerLocked() + && Math.abs(xVelocity) < X_VELOCITY_THRESH + && System.currentTimeMillis() - mDownTime > MINIMUM_TIME_HOLD_TIME) { + if (distanceSquared < touchSlop * touchSlop) { + mBarActive = true; + mBar = mEq.startTouchingBarUnder(event); + } + } + } + + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + + if (mBarActive) { + // reset state? + if (mBar != null) { + mEq.stopBarInteraction(mBar); + mBar.endInteraction(); + } + } + mBar = null; + mBarActive = false; + break; + } + if (mBarActive && mBar != null) { + return mBar.onTouchEvent(event); + } else { + return mPager.onTouchEvent(event); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/eq/EqUtils.java b/src/org/cyanogenmod/audiofx/audiofx/eq/EqUtils.java new file mode 100644 index 0000000..30d546c --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/eq/EqUtils.java @@ -0,0 +1,149 @@ +package com.cyngn.audiofx.eq; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +import com.cyngn.audiofx.Preset; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class EqUtils { + + private static final String TAG = EqUtils.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String DEFAULT_DELIMITER = ";"; + + public static String getZeroedBandsString(int length) { + return getZeroedBandsString(length, DEFAULT_DELIMITER); + } + + public static float[] stringBandsToFloats(String input) { + return stringBandsToFloats(input, DEFAULT_DELIMITER); + } + + public static String floatLevelsToString(float[] levels) { + return floatLevelsToString(levels, DEFAULT_DELIMITER); + } + + public static short[] stringBandsToShorts(String input) { + return stringBandsToShorts(input, DEFAULT_DELIMITER); + } + + public static String shortLevelsToString(short[] levels) { + return shortLevelsToString(levels, DEFAULT_DELIMITER); + } + + public static String getZeroedBandsString(int length, final String delimiter) { + StringBuilder buff = new StringBuilder(); + for (int i = 0; i < length; i++) { + buff.append("0").append(delimiter); + } + buff.deleteCharAt(buff.length() - 1); + return buff.toString(); + } + + public static String floatLevelsToString(float[] levels, final String delimiter) { + // save + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < levels.length; i++) { + builder.append(levels[i]); + builder.append(delimiter); + } + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } + + + public static String shortLevelsToString(short[] levels, final String delimiter) { + // save + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < levels.length; i++) { + builder.append(levels[i]); + builder.append(delimiter); + } + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } + + public static String intLevelsToString(int[] levels, final String delimiter) { + // save + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < levels.length; i++) { + builder.append(levels[i]); + builder.append(delimiter); + } + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } + + public static <T> String levelsToString(T[] levels, final String delimiter) { + // save + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < levels.length; i++) { + builder.append(levels[i]); + builder.append(delimiter); + } + builder.deleteCharAt(builder.length() - 1); + return builder.toString(); + } + + public static short[] stringBandsToShorts(String input, final String delimiter) { + String[] levels = input.split(delimiter); + + short[] equalizerLevels = new short[levels.length]; + for (int i = 0; i < levels.length; i++) { + equalizerLevels[i] = (short) (Float.parseFloat(levels[i])); + } + return equalizerLevels; + } + + + public static float[] stringBandsToFloats(String input, final String delimiter) { + String[] levels = input.split(delimiter); + + float[] equalizerLevels = new float[levels.length]; + for (int i = 0; i < levels.length; i++) { + equalizerLevels[i] = (Float.parseFloat(levels[i])); + } + return equalizerLevels; + } + + public static float[] convertDecibelsToMillibels(float[] decibels) { + if (DEBUG) Log.i(TAG, "++ convertDecibelsToMillibels(" + Arrays.toString(decibels) + ")"); + + float[] newvals = new float[decibels.length]; + for (int i = 0; i < decibels.length; i++) { + newvals[i] = decibels[i] * 100; + } + + + if (DEBUG) Log.i(TAG, "-- convertDecibelsToMillibels(" + Arrays.toString(newvals) + ")"); + return newvals; + } + + public static short[] convertDecibelsToMillibelsInShorts(float[] decibels) { + if (DEBUG) Log.i(TAG, "++ convertDecibelsToMillibels(" + Arrays.toString(decibels) + ")"); + + short[] newvals = new short[decibels.length]; + for (int i = 0; i < decibels.length; i++) { + newvals[i] = (short) (decibels[i] * 100); + } + + + if (DEBUG) Log.i(TAG, "-- convertDecibelsToMillibels(" + Arrays.toString(newvals) + ")"); + return newvals; + } + + public static float[] convertMillibelsToDecibels(float[] millibels) { + if (DEBUG) Log.i(TAG, "++ convertMillibelsToDecibels(" + Arrays.toString(millibels) + ")"); + float[] newvals = new float[millibels.length]; + for (int i = 0; i < millibels.length; i++) { + newvals[i] = millibels[i] / 100; + } + if (DEBUG) Log.i(TAG, "-- convertMillibelsToDecibels(" + Arrays.toString(newvals) + ")"); + return newvals; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxBaseFragment.java b/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxBaseFragment.java new file mode 100644 index 0000000..d487ff0 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxBaseFragment.java @@ -0,0 +1,66 @@ +package com.cyngn.audiofx.fragment; + +import android.animation.Animator; +import android.app.Fragment; +import android.os.Bundle; +import android.widget.CompoundButton; +import com.cyngn.audiofx.activity.ActivityMusic; +import com.cyngn.audiofx.activity.MasterConfigControl; + +public class AudioFxBaseFragment extends Fragment { + + MasterConfigControl mConfig; + + AudioFxFragment mFrag; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mFrag = (AudioFxFragment) getParentFragment(); + + mConfig = MasterConfigControl.getInstance(getActivity()); + } + + public int getDisabledColor() { + return mFrag.getDisabledColor(); + } + + public int getCurrentBackgroundColor() { + return mFrag.mCurrentBackgroundColor; + } + + public void animateBackgroundColorTo(Integer colorTo, Animator.AnimatorListener listener, + AudioFxFragment.ColorUpdateListener updateListener) { + if (mFrag != null) { + mFrag.animateBackgroundColorTo(colorTo, listener, updateListener); + } + } + + /** + * Call to change the color and propogate it up to the activity, which will call + * {@link #updateFragmentBackgroundColors(int)} + * + * @param color + */ + public void setBackgroundColor(int color, boolean cancelAnimated) { + if (mFrag != null) { + mFrag.updateBackgroundColors(color, cancelAnimated); + } + } + + /** + * For sub class fragments to override and apply the color + * + * @param color the new color to apply to any colored elements + */ + public void updateFragmentBackgroundColors(int color) { + } + + /** + * For sub class fragments to override when they might need to update their enabled states + */ + public void updateEnabledState() { + + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxFragment.java b/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxFragment.java new file mode 100644 index 0000000..2c9c5f5 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/fragment/AudioFxFragment.java @@ -0,0 +1,489 @@ +package com.cyngn.audiofx.fragment; + +import android.animation.Animator; +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.annotation.Nullable; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.media.AudioDeviceInfo; +import android.os.Bundle; +import android.os.Handler; +import android.util.ArrayMap; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import com.cyngn.audiofx.Compatibility; +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.ActivityMusic; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.activity.StateCallbacks; +import com.cyngn.audiofx.stats.UserSession; +import com.cyngn.audiofx.widget.InterceptableLinearLayout; + +import java.util.List; +import java.util.Map; + +public class AudioFxFragment extends Fragment implements StateCallbacks.DeviceChangedCallback { + + private static final String TAG = AudioFxFragment.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final String TAG_EQUALIZER = "equalizer"; + public static final String TAG_CONTROLS = "controls"; + + Handler mHandler; + int mCurrentBackgroundColor; + + // whether we are in the middle of animating while switching devices + boolean mDeviceChanging; + + private MenuItem mMenuDevices; + + // current selected index + public int mSelectedPosition = 0; + + EqualizerFragment mEqFragment; + ControlsFragment mControlFragment; + + InterceptableLinearLayout mInterceptLayout; + private ValueAnimator mColorChangeAnimator; + + private int mDisabledColor; + + private MasterConfigControl mConfig; + private EqualizerManager mEqManager; + + private AudioDeviceInfo mSystemDevice; + private AudioDeviceInfo mUserSelection; + + private final Map<MenuItem, AudioDeviceInfo> mMenuItems = new ArrayMap<MenuItem, AudioDeviceInfo>(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mConfig = MasterConfigControl.getInstance(getActivity()); + mEqManager = mConfig.getEqualizerManager(); + + if (savedInstanceState != null) { + int user = savedInstanceState.getInt("user_device"); + mUserSelection = mConfig.getDeviceById(user); + int system = savedInstanceState.getInt("system_device"); + mSystemDevice = mConfig.getDeviceById(system); + } + + mHandler = new Handler(); + mDisabledColor = getResources().getColor(R.color.disabled_eq); + + setHasOptionsMenu(true); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("user_device", mUserSelection == null ? -1 : mUserSelection.getId()); + outState.putInt("system_device", mSystemDevice == null ? -1 : mSystemDevice.getId()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + } + + private boolean showFragments() { + boolean createNewFrags = true; + final FragmentTransaction fragmentTransaction = getChildFragmentManager() + .beginTransaction(); + if (mEqFragment == null) { + mEqFragment = (EqualizerFragment) getChildFragmentManager() + .findFragmentByTag(TAG_EQUALIZER); + + if (mEqFragment != null) { + fragmentTransaction.show(mEqFragment); + } + } + if (mControlFragment == null) { + mControlFragment = (ControlsFragment) getChildFragmentManager() + .findFragmentByTag(TAG_CONTROLS); + if (mControlFragment != null) { + fragmentTransaction.show(mControlFragment); + } + } + + if (mEqFragment != null && mControlFragment != null) { + createNewFrags = false; + } + + fragmentTransaction.commit(); + + return createNewFrags; + } + + @Override + public void onResume() { + + mConfig.getCallbacks().addDeviceChangedCallback(this); + mConfig.bindService(); + mConfig.setAutoBindToService(true); + + updateEnabledState(); + + super.onResume(); + + mCurrentBackgroundColor = !mConfig.isCurrentDeviceEnabled() + ? mDisabledColor + : mEqManager.getAssociatedPresetColorHex( + mEqManager.getCurrentPresetIndex()); + updateBackgroundColors(mCurrentBackgroundColor, false); + + promptIfNotDefault(); + } + + private void promptIfNotDefault() { + final String audioFxPackageName = getActivity().getPackageName(); + + final SharedPreferences musicFxPrefs = Constants.getMusicFxPrefs(getActivity()); + final String defaultPackage = musicFxPrefs.getString(Constants.MUSICFX_DEFAULT_PACKAGE_KEY, + audioFxPackageName); + final boolean notDefault = !defaultPackage.equals(audioFxPackageName); + + if (notDefault) { + new AlertDialog.Builder(getActivity()) + .setMessage(R.string.snack_bar_not_default) + .setNegativeButton(R.string.snack_bar_not_default_not_now, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + getActivity().finish(); + } + }) + .setPositiveButton(R.string.snack_bar_not_default_set, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent updateIntent = new Intent(getActivity(), + Compatibility.Service.class); + updateIntent.putExtra("defPackage", audioFxPackageName); + updateIntent.putExtra("defName", ActivityMusic.class.getName()); + getActivity().startService(updateIntent); + dialog.dismiss(); + } + }) + .setCancelable(false) + .create() + .show(); + } + } + + @Override + public void onPause() { + mConfig.setAutoBindToService(false); + mConfig.getCallbacks().removeDeviceChangedCallback(this); + super.onPause(); + mConfig.unbindService(); + } + + public void updateBackgroundColors(Integer color, boolean cancelAnimated) { + if (cancelAnimated && mColorChangeAnimator != null) { + mColorChangeAnimator.cancel(); + } + mCurrentBackgroundColor = color; + if (mEqFragment != null) { + mEqFragment.updateFragmentBackgroundColors(color); + } + if (mControlFragment != null) { + mControlFragment.updateFragmentBackgroundColors(color); + } + } + + public void updateEnabledState() { + boolean currentDeviceEnabled = mConfig.isCurrentDeviceEnabled(); + if (mEqFragment != null) { + mEqFragment.updateEnabledState(); + } + if (mControlFragment != null) { + mControlFragment.updateEnabledState(); + } + + ((ActivityMusic) getActivity()).setGlobalToggleChecked(currentDeviceEnabled); + + if (mInterceptLayout != null) { + mInterceptLayout.setInterception(!currentDeviceEnabled); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.devices, menu); + mMenuDevices = menu.findItem(R.id.devices); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + mMenuDevices.getSubMenu().clear(); + mMenuItems.clear(); + + final AudioDeviceInfo currentDevice = mConfig.getCurrentDevice(); + + MenuItem selectedItem = null; + + List<AudioDeviceInfo> speakerDevices = mConfig.getConnectedDevices( + AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); + if (speakerDevices.size() > 0) { + AudioDeviceInfo ai = speakerDevices.get(0); + int viewId = View.generateViewId(); + MenuItem item = mMenuDevices.getSubMenu().add(R.id.devices, viewId, + Menu.NONE, MasterConfigControl.getDeviceDisplayString(getActivity(), ai)); + item.setIcon(R.drawable.ic_action_dsp_icons_speaker); + mMenuItems.put(item, ai); + selectedItem = item; + } + + List<AudioDeviceInfo> headsetDevices = mConfig.getConnectedDevices( + AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET); + if (headsetDevices.size() > 0) { + AudioDeviceInfo ai = headsetDevices.get(0); + int viewId = View.generateViewId(); + MenuItem item = mMenuDevices.getSubMenu().add(R.id.devices, viewId, + Menu.NONE, MasterConfigControl.getDeviceDisplayString(getActivity(), ai)); + item.setIcon(R.drawable.ic_action_dsp_icons_headphones); + mMenuItems.put(item, ai); + if (currentDevice.getId() == ai.getId()) { + selectedItem = item; + } + } + + List<AudioDeviceInfo> lineOutDevices = mConfig.getConnectedDevices( + AudioDeviceInfo.TYPE_LINE_ANALOG, AudioDeviceInfo.TYPE_LINE_DIGITAL); + if (lineOutDevices.size() > 0) { + AudioDeviceInfo ai = lineOutDevices.get(0); + int viewId = View.generateViewId(); + MenuItem item = mMenuDevices.getSubMenu().add(R.id.devices, viewId, + Menu.NONE, MasterConfigControl.getDeviceDisplayString(getActivity(), ai)); + item.setIcon(R.drawable.ic_action_dsp_icons_lineout); + mMenuItems.put(item, ai); + if (currentDevice.getId() == ai.getId()) { + selectedItem = item; + } + } + + List<AudioDeviceInfo> bluetoothDevices = mConfig.getConnectedDevices( + AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); + for (AudioDeviceInfo ai : bluetoothDevices) { + int viewId = View.generateViewId(); + MenuItem item = mMenuDevices.getSubMenu().add(R.id.devices, viewId, + Menu.NONE, MasterConfigControl.getDeviceDisplayString(getActivity(), ai)); + item.setIcon(R.drawable.ic_action_dsp_icons_bluetoof); + mMenuItems.put(item, ai); + if (currentDevice.getId() == ai.getId()) { + selectedItem = item; + } + } + + List<AudioDeviceInfo> usbDevices = mConfig.getConnectedDevices( + AudioDeviceInfo.TYPE_USB_ACCESSORY, AudioDeviceInfo.TYPE_USB_DEVICE); + for (AudioDeviceInfo ai : usbDevices) { + int viewId = View.generateViewId(); + MenuItem item = mMenuDevices.getSubMenu().add(R.id.devices, viewId, + Menu.NONE, MasterConfigControl.getDeviceDisplayString(getActivity(), ai)); + item.setIcon(R.drawable.ic_action_device_usb); + mMenuItems.put(item, ai); + if (currentDevice.getId() == ai.getId()) { + selectedItem = item; + } + } + mMenuDevices.getSubMenu().setGroupCheckable(R.id.devices, true, true); + selectedItem.setChecked(true); + mMenuDevices.setIcon(selectedItem.getIcon()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + AudioDeviceInfo device = mMenuItems.get(item); + + if (device != null) { + UserSession.getInstance().deviceChanged(); + mDeviceChanging = true; + if (item.isCheckable()) { + item.setChecked(!item.isChecked()); + } + mSystemDevice = mConfig.getSystemDevice(); + mUserSelection = device; + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + mConfig.setCurrentDevice(mUserSelection, true); + } + }); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + Bundle savedInstanceState) { + if (container == null) { + Log.w(TAG, "container is null."); + // no longer displaying this fragment + return null; + } + + View root = inflater.inflate(mConfig.hasMaxxAudio() + ? R.layout.fragment_audiofx_maxxaudio + : R.layout.fragment_audiofx, container, false); + + final FragmentTransaction fragmentTransaction = getChildFragmentManager() + .beginTransaction(); + + boolean createNewFrags = true; + + if (savedInstanceState != null) { + createNewFrags = showFragments(); + } + + if (createNewFrags) { + fragmentTransaction.add(R.id.equalizer, mEqFragment = new EqualizerFragment(), + TAG_EQUALIZER); + fragmentTransaction.add(R.id.controls, mControlFragment = new ControlsFragment(), + TAG_CONTROLS); + } + + fragmentTransaction.commit(); + + + return root; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + // view was destroyed + final FragmentTransaction fragmentTransaction = getChildFragmentManager() + .beginTransaction(); + + if (mEqFragment != null) { + fragmentTransaction.remove(mEqFragment); + mEqFragment = null; + } + if (mControlFragment != null) { + fragmentTransaction.remove(mControlFragment); + mControlFragment = null; + } + + fragmentTransaction.commitAllowingStateLoss(); + + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mInterceptLayout = (InterceptableLinearLayout) view.findViewById(R.id.interceptable_layout); + } + + public void animateBackgroundColorTo(int colorTo, Animator.AnimatorListener listener, + ColorUpdateListener updateListener) { + if (mColorChangeAnimator != null) { + mColorChangeAnimator.cancel(); + mColorChangeAnimator = null; + } + mColorChangeAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), + mCurrentBackgroundColor, colorTo); + mColorChangeAnimator.setDuration(500); + mColorChangeAnimator.addUpdateListener(updateListener != null ? updateListener + : mColorUpdateListener); + if (listener != null) { + mColorChangeAnimator.addListener(listener); + } + mColorChangeAnimator.start(); + } + + @Override + public void onDeviceChanged(AudioDeviceInfo device, boolean userChange) { + updateEnabledState(); + getActivity().invalidateOptionsMenu(); + } + + public CompoundButton getGlobalSwitch() { + return ((ActivityMusic) getActivity()).getGlobalSwitch(); + } + + @Override + public void onGlobalDeviceToggle(final boolean checked) { + final CompoundButton buttonView = getGlobalSwitch(); + final Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + buttonView.setEnabled(false); + } + + @Override + public void onAnimationEnd(Animator animation) { + updateEnabledState(); + buttonView.setEnabled(true); + } + + @Override + public void onAnimationCancel(Animator animation) { + buttonView.setEnabled(true); + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }; + final Integer colorTo = checked + ? mEqManager.getAssociatedPresetColorHex(mEqManager.getCurrentPresetIndex()) + : mDisabledColor; + animateBackgroundColorTo(colorTo, animatorListener, null); + } + + private ValueAnimator.AnimatorUpdateListener mColorUpdateListener + = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateBackgroundColors((Integer) animation.getAnimatedValue(), false); + } + }; + + public static class ColorUpdateListener implements ValueAnimator.AnimatorUpdateListener { + + final AudioFxBaseFragment mFrag; + + public ColorUpdateListener(AudioFxBaseFragment frag) { + this.mFrag = frag; + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + mFrag.setBackgroundColor((Integer) animation.getAnimatedValue(), false); + } + } + + public int getDisabledColor() { + return mDisabledColor; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/fragment/ControlsFragment.java b/src/org/cyanogenmod/audiofx/audiofx/fragment/ControlsFragment.java new file mode 100644 index 0000000..02e7077 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/fragment/ControlsFragment.java @@ -0,0 +1,103 @@ +package com.cyngn.audiofx.fragment; + +import android.annotation.Nullable; +import android.media.AudioDeviceInfo; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.knobs.KnobCommander; +import com.cyngn.audiofx.knobs.KnobContainer; +import com.cyngn.audiofx.stats.UserSession; + +public class ControlsFragment extends AudioFxBaseFragment { + + private static final String TAG = ControlsFragment.class.getSimpleName(); + private static final boolean DEBUG = false; + + KnobCommander mKnobCommander; + KnobContainer mKnobContainer; + CheckBox mMaxxVolumeSwitch; + + private CompoundButton.OnCheckedChangeListener mMaxxVolumeListener + = new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (mConfig.getMaxxVolumeEnabled() != isChecked) { + UserSession.getInstance().maxxVolumeToggled(); + } + mConfig.setMaxxVolumeEnabled(isChecked); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mKnobCommander = KnobCommander.getInstance(getActivity()); + } + + @Override + public void onPause() { + MasterConfigControl.getInstance(getActivity()).getCallbacks().removeDeviceChangedCallback(mKnobContainer); + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + MasterConfigControl.getInstance(getActivity()).getCallbacks().addDeviceChangedCallback(mKnobContainer); + } + + @Override + public void updateFragmentBackgroundColors(int color) { + if (mKnobContainer != null) { + mKnobContainer.updateKnobHighlights(color); + } + } + + + public void updateEnabledState() { + final AudioDeviceInfo device = mConfig.getCurrentDevice(); + boolean currentDeviceEnabled = mConfig.isCurrentDeviceEnabled(); + + if (DEBUG) { + Log.d(TAG, "updating with current device: " + device.getType()); + } + + if (mMaxxVolumeSwitch != null) { + mMaxxVolumeSwitch.setChecked(mConfig.getMaxxVolumeEnabled()); + mMaxxVolumeSwitch.setEnabled(currentDeviceEnabled); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + Bundle savedInstanceState) { + View root = inflater.inflate(mConfig.hasMaxxAudio() ? R.layout.controls_maxx_audio + : R.layout.controls_generic, container, false); + return root; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mKnobContainer = (KnobContainer) view.findViewById(R.id.knob_container); + mMaxxVolumeSwitch = (CheckBox) view.findViewById(R.id.maxx_volume_switch); + + updateFragmentBackgroundColors(getCurrentBackgroundColor()); + + if (mMaxxVolumeSwitch != null) { + mMaxxVolumeSwitch.setOnCheckedChangeListener(mMaxxVolumeListener); + } + } + + +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/fragment/EqualizerFragment.java b/src/org/cyanogenmod/audiofx/audiofx/fragment/EqualizerFragment.java new file mode 100644 index 0000000..2d958b2 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/fragment/EqualizerFragment.java @@ -0,0 +1,559 @@ +package com.cyngn.audiofx.fragment; + +import android.animation.Animator; +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.annotation.Nullable; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.graphics.drawable.ColorDrawable; +import android.media.AudioDeviceInfo; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.view.ViewPager; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.TextView; + +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.activity.StateCallbacks; +import com.cyngn.audiofx.eq.EqContainerView; +import com.cyngn.audiofx.preset.InfinitePagerAdapter; +import com.cyngn.audiofx.preset.InfiniteViewPager; +import com.cyngn.audiofx.preset.PresetPagerAdapter; +import com.cyngn.audiofx.stats.UserSession; +import com.cyngn.audiofx.viewpagerindicator.CirclePageIndicator; + +import java.util.Arrays; + +public class EqualizerFragment extends AudioFxBaseFragment + implements StateCallbacks.DeviceChangedCallback, StateCallbacks.EqUpdatedCallback { + + private static final String TAG = EqualizerFragment.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final boolean DEBUG_VIEWPAGER = true; + + private final ArgbEvaluator mArgbEval = new ArgbEvaluator(); + + public EqContainerView mEqContainer; + InfiniteViewPager mPresetPager; + CirclePageIndicator mPresetPageIndicator; + PresetPagerAdapter mDataAdapter; + InfinitePagerAdapter mInfiniteAdapter; + int mCurrentRealPage; + + private Handler mHandler; + + // whether we are in the middle of animating while switching devices + boolean mDeviceChanging; + + private ViewPager mFakePager; + + private int mAnimatingToRealPageTarget = -1; + + /* + * this array can hold on to arrays which store preset levels, + * so modifying values in here should only be done with extreme care + */ + private float[] mSelectedPositionBands; + + // current selected index + public int mSelectedPosition = 0; + + private MasterConfigControl mConfig; + private EqualizerManager mEqManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mConfig = MasterConfigControl.getInstance(getActivity()); + mEqManager = mConfig.getEqualizerManager(); + + mHandler = new Handler(); + } + + @Override + public void onPause() { + mEqContainer.stopListening(); + mConfig.getCallbacks().removeDeviceChangedCallback(this); + mConfig.getCallbacks().removeEqUpdatedCallback(this); + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + mEqContainer.startListening(); + mConfig.getCallbacks().addEqUpdatedCallback(this); + mConfig.getCallbacks().addDeviceChangedCallback(this); + mPresetPageIndicator.notifyDataSetChanged(); + mDataAdapter.notifyDataSetChanged(); + } + + @Override + public void updateFragmentBackgroundColors(int color) { + if (getActivity() != null && getActivity().getWindow() != null) { + getActivity().getWindow().setBackgroundDrawable(new ColorDrawable(color)); + } + } + + public void jumpToPreset(int index) { + int diff = index - (mCurrentRealPage % mDataAdapter.getCount()); + // double it, short (e.g. 1 hop) distances sometimes bug out?? + diff += mDataAdapter.getCount(); + int newPage = mCurrentRealPage + diff; + mPresetPager.setCurrentItemAbsolute(newPage, false); + } + + private void removeCurrentCustomPreset(boolean showWarning) { + if (showWarning) { + Preset p = mEqManager.getCurrentPreset(); + new AlertDialog.Builder(getActivity()) + .setMessage(String.format(getString( + R.string.remove_custom_preset_warning_message), p.getName())) + .setNegativeButton(android.R.string.no, null) + .setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + removeCurrentCustomPreset(false); + } + }) + .create() + .show(); + return; + } + + final int currentIndexBeforeRemove = mEqManager.getCurrentPresetIndex(); + if (mEqManager.removePreset(currentIndexBeforeRemove)) { + mInfiniteAdapter.notifyDataSetChanged(); + mDataAdapter.notifyDataSetChanged(); + mPresetPageIndicator.notifyDataSetChanged(); + + jumpToPreset(mSelectedPosition - 1); + } + } + + private void openRenameDialog() { + AlertDialog.Builder renameDialog = new AlertDialog.Builder(getActivity()); + renameDialog.setTitle(R.string.rename); + final EditText newName = new EditText(getActivity()); + newName.setText(mEqManager.getCurrentPreset().getName()); + renameDialog.setView(newName); + renameDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface d, int which) { + mEqManager.renameCurrentPreset(newName.getText().toString()); + final TextView viewWithTag = (TextView) mPresetPager + .findViewWithTag(mEqManager.getCurrentPreset()); + viewWithTag.setText(newName.getText().toString()); + mDataAdapter.notifyDataSetChanged(); + mPresetPager.invalidate(); + } + }); + + renameDialog.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + + public void onClick(DialogInterface d, int which) { + } + }); + + // disable ok button if text is empty + final AlertDialog dialog = renameDialog.create(); + newName.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() == 0) { + dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(false); + } else { + dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled(true); + } + } + }); + + dialog.show(); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.equalizer, container, false); + return root; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mSelectedPositionBands = mEqManager.getPersistedPresetLevels(mEqManager.getCurrentPresetIndex()); + mSelectedPosition = mEqManager.getCurrentPresetIndex(); + + mEqContainer = (EqContainerView) view.findViewById(R.id.eq_container); + mPresetPager = (InfiniteViewPager) view.findViewById(R.id.pager); + mPresetPageIndicator = (CirclePageIndicator) view.findViewById(R.id.indicator); + mFakePager = (ViewPager) view.findViewById(R.id.fake_pager); + + mEqContainer.findViewById(R.id.save).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + + final int newidx = mEqManager.addPresetFromCustom(); + mInfiniteAdapter.notifyDataSetChanged(); + mDataAdapter.notifyDataSetChanged(); + mPresetPageIndicator.notifyDataSetChanged(); + + jumpToPreset(newidx); + } + } + ); + mEqContainer.findViewById(R.id.rename).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mEqManager.isUserPreset()) { + openRenameDialog(); + } + } + } + ); + mEqContainer.findViewById(R.id.remove).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + removeCurrentCustomPreset(true); + } + } + ); + + mDataAdapter = new PresetPagerAdapter(getActivity()); + mInfiniteAdapter = new InfinitePagerAdapter(mDataAdapter); + + mPresetPager.setAdapter(mInfiniteAdapter); + mPresetPager.setOnPageChangeListener(mViewPageChangeListener); + + mFakePager.setAdapter(mDataAdapter); + mCurrentRealPage = mPresetPager.getCurrentItem(); + + mPresetPageIndicator.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + // eat all events + return true; + } + }); + mPresetPageIndicator.setSnap(true); + + mPresetPageIndicator.setViewPager(mFakePager, 0); + mPresetPageIndicator.setCurrentItem(mSelectedPosition); + + mFakePager.setCurrentItem(mSelectedPosition); + mPresetPager.setCurrentItem(mSelectedPosition); + } + + @Override + public void onBandLevelChange(int band, float dB, boolean fromSystem) { + // call backs we get when bands are changing, check if the user is physically touching them + // and set the preset to "custom" and do proper animations. + if (!fromSystem) { // from user + if (!mEqManager.isCustomPreset() // not on custom already + && !mEqManager.isUserPreset() // or not on a user preset + && !mEqManager.isAnimatingToCustom()) { // and animation hasn't started + if (DEBUG) Log.w(TAG, "met conditions to start an animation to custom trigger"); + // view pager is infinite, so we can't set the item to 0. find NEXT 0 + mEqManager.setAnimatingToCustom(true); + + final int newIndex = mEqManager.copyToCustom(); + + mInfiniteAdapter.notifyDataSetChanged(); + mDataAdapter.notifyDataSetChanged(); + mPresetPager.getAdapter().notifyDataSetChanged(); + // do background transition manually as viewpager can't handle this bg change + final Integer colorTo = !mConfig.isCurrentDeviceEnabled() + ? getDisabledColor() + : mEqManager.getAssociatedPresetColorHex(newIndex); + final Animator.AnimatorListener listener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + int diff = newIndex - (mCurrentRealPage % mDataAdapter.getCount()); + diff += mDataAdapter.getCount(); + int newPage = mCurrentRealPage + diff; + + mAnimatingToRealPageTarget = newPage; + mPresetPager.setCurrentItemAbsolute(newPage); + } + + @Override + public void onAnimationEnd(Animator animation) { + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }; + animateBackgroundColorTo(colorTo, listener, null); + + } + mSelectedPositionBands[band] = dB; + } + } + + @Override + public void onPresetChanged(int newPresetIndex) { + } + + @Override + public void onPresetsChanged() { + mDataAdapter.notifyDataSetChanged(); + } + + @Override + public void onDeviceChanged(AudioDeviceInfo device, boolean userChange) { + int diff = mEqManager.getCurrentPresetIndex() - mSelectedPosition; + final boolean samePage = diff == 0; + diff = mDataAdapter.getCount() + diff; + if (DEBUG) { + Log.d(TAG, "diff: " + diff); + } + mCurrentRealPage = mPresetPager.getCurrentItem(); + + if (DEBUG) Log.d(TAG, "mCurrentRealPage Before: " + mCurrentRealPage); + final int newPage = mCurrentRealPage + diff; + if (DEBUG) Log.d(TAG, "mCurrentRealPage After: " + newPage); + + mSelectedPositionBands = mEqManager.getPresetLevels(mSelectedPosition); + final float[] targetBandLevels = mEqManager.getPresetLevels(mEqManager.getCurrentPresetIndex()); + + // do background transition manually as viewpager can't handle this bg change + final Integer colorTo = !mConfig.isCurrentDeviceEnabled() + ? getDisabledColor() + : mEqManager.getAssociatedPresetColorHex(mEqManager.getCurrentPresetIndex()); + + final Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mEqManager.setChangingPresets(true); + + mDeviceChanging = true; + + if (!samePage) { + mPresetPager.setCurrentItemAbsolute(newPage); + } + } + + @Override + public void onAnimationEnd(Animator animation) { + mEqManager.setChangingPresets(false); + + mSelectedPosition = mEqManager.getCurrentPresetIndex(); + mSelectedPositionBands = mEqManager.getPresetLevels(mSelectedPosition); + + mDeviceChanging = false; + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }; + + final AudioFxFragment.ColorUpdateListener animatorUpdateListener + = new AudioFxFragment.ColorUpdateListener(this) { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + super.onAnimationUpdate(animator); + + final int N = mEqManager.getNumBands(); + for (int i = 0; i < N; i++) { // animate bands + float delta = targetBandLevels[i] - mSelectedPositionBands[i]; + float newBandLevel = mSelectedPositionBands[i] + + (delta * animator.getAnimatedFraction()); + //if (DEBUG_VIEWPAGER) Log.d(TAG, i + ", delta: " + delta + ", newBandLevel: " + newBandLevel); + mEqManager.setLevel(i, newBandLevel, true); + } + } + }; + + animateBackgroundColorTo(colorTo, animatorListener, animatorUpdateListener); + } + + @Override + public void onGlobalDeviceToggle(boolean on) { + if (!on) { + mFakePager.setCurrentItem(mFakePager.getCurrentItem(), true); + } + } + + + private ViewPager.OnPageChangeListener mViewPageChangeListener = new ViewPager.OnPageChangeListener() { + + int mState; + float mLastOffset; + boolean mJustGotToCustomAndSettling; + + @Override + public void onPageScrolled(int newPosition, float positionOffset, int positionOffsetPixels) { + if (DEBUG_VIEWPAGER) + Log.i(TAG, "onPageScrolled(" + newPosition + ", " + positionOffset + ", " + + positionOffsetPixels + ")"); + Integer colorFrom; + Integer colorTo; + + if (newPosition == mAnimatingToRealPageTarget && mEqManager.isAnimatingToCustom()) { + if (DEBUG_VIEWPAGER) Log.w(TAG, "settling var set to true"); + mJustGotToCustomAndSettling = true; + mAnimatingToRealPageTarget = -1; + } + + newPosition = newPosition % mDataAdapter.getCount(); + + + if (mEqManager.isAnimatingToCustom() || mDeviceChanging) { + if (DEBUG_VIEWPAGER) + Log.i(TAG, "ignoring onPageScrolled because animating to custom or device is changing"); + return; + } + + int toPos; + if (mLastOffset - positionOffset > 0.8) { // this is needed for flings + //Log.e(TAG, "OFFSET DIFF > 0.8! Setting selected position from: " + mSelectedPosition + " to " + newPosition); + mSelectedPosition = newPosition; + // mSelectedPositionBands will be reset by setPreset() below calling back to onPresetChanged() + + mEqManager.setPreset(mSelectedPosition); + } + + if (newPosition < mSelectedPosition || (newPosition == mDataAdapter.getCount() - 1) + && mSelectedPosition == 0) { + // scrolling left <<<<< + positionOffset = (1 - positionOffset); + //Log.v(TAG, "<<<<<< positionOffset: " + positionOffset + " (last offset: " + mLastOffset + ")"); + toPos = newPosition; + colorTo = mEqManager.getAssociatedPresetColorHex(toPos); + } else { + // scrolling right >>>>> + //Log.v(TAG, ">>>>>>> positionOffset: " + positionOffset + " (last offset: " + mLastOffset + ")"); + toPos = newPosition + 1 % mDataAdapter.getCount(); + if (toPos >= mDataAdapter.getCount()) { + toPos = 0; + } + + colorTo = mEqManager.getAssociatedPresetColorHex(toPos); + } + + if (!mDeviceChanging && mConfig.isCurrentDeviceEnabled()) { + colorFrom = mEqManager.getAssociatedPresetColorHex(mSelectedPosition); + setBackgroundColor((Integer) mArgbEval.evaluate(positionOffset, colorFrom, colorTo), + true); + } + + if (mSelectedPositionBands == null) { + mSelectedPositionBands = mEqManager.getPresetLevels(mSelectedPosition); + } + // get current bands + float[] finalPresetLevels = mEqManager.getPresetLevels(toPos); + + final int N = mEqManager.getNumBands(); + for (int i = 0; i < N; i++) { // animate bands + float delta = finalPresetLevels[i] - mSelectedPositionBands[i]; + float newBandLevel = mSelectedPositionBands[i] + (delta * positionOffset); + //if (DEBUG_VIEWPAGER) Log.d(TAG, i + ", delta: " + delta + ", newBandLevel: " + newBandLevel); + mEqManager.setLevel(i, newBandLevel, true); + } + mLastOffset = positionOffset; + + } + + @Override + public void onPageSelected(int position) { + if (DEBUG_VIEWPAGER) Log.i(TAG, "onPageSelected(" + position + ")"); + mCurrentRealPage = position; + position = position % mDataAdapter.getCount(); + if (DEBUG_VIEWPAGER) Log.e(TAG, "onPageSelected(" + position + ")"); + mFakePager.setCurrentItem(position); + mSelectedPosition = position; + if (!mDeviceChanging) { + mSelectedPositionBands = mEqManager.getPresetLevels(mSelectedPosition); + if (UserSession.getInstance() != null) { + UserSession.getInstance().presetSelected(); + } + } + } + + + @Override + public void onPageScrollStateChanged(int newState) { + mState = newState; + if (mDeviceChanging) { // avoid setting unwanted presets during custom animations + return; + } + if (DEBUG_VIEWPAGER) + Log.w(TAG, "onPageScrollStateChanged(" + stateToString(newState) + ")"); + + if (mJustGotToCustomAndSettling && mState == ViewPager.SCROLL_STATE_IDLE) { + if (DEBUG_VIEWPAGER) + Log.w(TAG, "onPageScrollChanged() setting animating to custom = false"); + mJustGotToCustomAndSettling = false; + mEqManager.setChangingPresets(false); + mEqManager.setAnimatingToCustom(false); + } else { + if (mState == ViewPager.SCROLL_STATE_IDLE) { + animateBackgroundColorTo(!mConfig.isCurrentDeviceEnabled() + ? getDisabledColor() + : mEqManager.getAssociatedPresetColorHex(mSelectedPosition), + null, null); + + mEqManager.setChangingPresets(false); + mEqManager.setPreset(mSelectedPosition); + + } else { + // not idle + mEqManager.setChangingPresets(true); + } + } + } + + private String stateToString(int state) { + switch (state) { + case 0: + return "STATE_IDLE"; + case 1: + return "STATE_DRAGGING"; + case 2: + return "STATE_SETTLING"; + default: + return "STATE_WUT"; + } + } + + }; +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobCommander.java b/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobCommander.java new file mode 100644 index 0000000..b045c16 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobCommander.java @@ -0,0 +1,177 @@ +package com.cyngn.audiofx.knobs; + +import android.content.Context; +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.service.AudioFxService; + +public class KnobCommander { + + public static final int KNOB_TREBLE = 0; + public static final int KNOB_BASS = 1; + public static final int KNOB_VIRTUALIZER = 2; + + private static KnobCommander sInstance; + + private Context mContext; + private MasterConfigControl mConfig; + + private KnobCommander(Context context) { + mContext = context; + mConfig = MasterConfigControl.getInstance(mContext); + } + + public static KnobCommander getInstance(Context context) { + if (sInstance == null) { + sInstance = new KnobCommander(context); + } + return sInstance; + } + + public RadialKnob.OnKnobChangeListener getRadialKnobCallback(int whichKnob) { + switch (whichKnob) { + case KNOB_TREBLE: return mTrebleKnobCallback; + case KNOB_BASS: return mBassKnobCallback; + case KNOB_VIRTUALIZER: return mVirtualizerCallback; + default: return null; + } + } + + public void updateTrebleKnob(RadialKnob trebleKnob, boolean enabled) { + if (trebleKnob != null) { + trebleKnob.setValue(getTrebleStrength()); + trebleKnob.setOn(isTrebleEffectEnabled()); + trebleKnob.setEnabled(enabled); + } + } + + public void updateBassKnob(RadialKnob bassKnob, boolean enabled) { + if (bassKnob != null) { + bassKnob.setValue(getBassStrength()); + bassKnob.setOn(isBassEffectEnabled()); + bassKnob.setEnabled(enabled); + } + } + + public void updateVirtualizerKnob(RadialKnob virtualizerKnob, boolean enabled) { + if (virtualizerKnob != null) { + virtualizerKnob.setValue(getVirtualizerStrength()); + virtualizerKnob.setOn(isVirtualizerEffectEnabled()); + virtualizerKnob.setEnabled(enabled); + } + } + + public boolean hasBassBoost() { + return mConfig.hasBassBoost(); + } + + public boolean hasTreble() { + return mConfig.hasMaxxAudio() || mConfig.hasDts(); + } + + public boolean hasVirtualizer() { + return mConfig.hasVirtualizer(); + } + + public boolean isBassEffectEnabled() { + return mConfig.getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_BASS_ENABLE, false); + } + + public boolean isTrebleEffectEnabled() { + return mConfig.getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_TREBLE_ENABLE, false); + } + + public boolean isVirtualizerEffectEnabled() { + return mConfig.getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_VIRTUALIZER_ENABLE, false); + } + + public int getVirtualizerStrength() { + return Integer.valueOf(mConfig.getPrefs().getString(Constants.DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH, "0")) / 10; + } + + public int getBassStrength() { + return Integer.valueOf(mConfig.getPrefs().getString(Constants.DEVICE_AUDIOFX_BASS_STRENGTH, "0")) / 10; + } + + public int getTrebleStrength() { + return Integer.valueOf(mConfig.getPrefs().getString(Constants.DEVICE_AUDIOFX_TREBLE_STRENGTH, "0")); + } + + public void setTrebleEnabled(boolean on) { + mConfig.getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_TREBLE_ENABLE, on).apply(); + mConfig.updateService(AudioFxService.TREBLE_BOOST_CHANGED); + } + + public void setTrebleStrength(int value) { + // set parameter and state + mConfig.getPrefs().edit().putString(Constants.DEVICE_AUDIOFX_TREBLE_STRENGTH, String.valueOf(value)).apply(); + mConfig.updateService(AudioFxService.TREBLE_BOOST_CHANGED); + } + + public void setBassEnabled(boolean on) { + mConfig.getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_BASS_ENABLE, on).apply(); + mConfig.updateService(AudioFxService.BASS_BOOST_CHANGED); + } + + public void setBassStrength(int value) { + // set parameter and state + mConfig.getPrefs().edit().putString(Constants.DEVICE_AUDIOFX_BASS_STRENGTH, String.valueOf(value * 10)).apply(); + mConfig.updateService(AudioFxService.BASS_BOOST_CHANGED); + } + + public void setVirtualizerEnabled(boolean on) { + mConfig.getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_VIRTUALIZER_ENABLE, on).apply(); + mConfig.updateService(AudioFxService.VIRTUALIZER_CHANGED); + } + + public void setVirtualiserStrength(int value) { + // set parameter and state + mConfig.getPrefs().edit().putString(Constants.DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH, String.valueOf(value * 10)).apply(); + mConfig.updateService(AudioFxService.VIRTUALIZER_CHANGED); + } + + private RadialKnob.OnKnobChangeListener mTrebleKnobCallback = new RadialKnob.OnKnobChangeListener() { + @Override + public void onValueChanged(RadialKnob knob, int value, boolean fromUser) { + if (fromUser) { + setTrebleStrength(value); + } + } + + @Override + public boolean onSwitchChanged(RadialKnob knob, boolean on) { + setTrebleEnabled(on); + return true; + } + }; + + private RadialKnob.OnKnobChangeListener mBassKnobCallback = new RadialKnob.OnKnobChangeListener() { + @Override + public void onValueChanged(RadialKnob knob, int value, boolean fromUser) { + if (fromUser) { + setBassStrength(value); + } + } + + @Override + public boolean onSwitchChanged(RadialKnob knob, boolean on) { + setBassEnabled(on); + return true; + } + }; + + private RadialKnob.OnKnobChangeListener mVirtualizerCallback = new RadialKnob.OnKnobChangeListener() { + @Override + public void onValueChanged(RadialKnob knob, int value, boolean fromUser) { + if (fromUser) { + setVirtualiserStrength(value); + } + } + + @Override + public boolean onSwitchChanged(RadialKnob knob, boolean on) { + setVirtualizerEnabled(on); + return true; + } + }; +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobContainer.java b/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobContainer.java new file mode 100644 index 0000000..91e811b --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/knobs/KnobContainer.java @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cyngn.audiofx.knobs; + +import android.content.Context; +import android.content.res.Configuration; +import android.media.AudioDeviceInfo; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.activity.StateCallbacks; + +public class KnobContainer extends LinearLayout + implements StateCallbacks.DeviceChangedCallback { + + private static final String TAG = KnobContainer.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final int NOTIFY_DISABLE_DELAY = 5000; + + private static final int MSG_EXPAND = 0; + private static final int MSG_CONTRACT = 1; + + private ViewGroup mTrebleContainer; + private ViewGroup mBassContainer; + private ViewGroup mVirtualizerContainer; + private RadialKnob mTrebleKnob; + private RadialKnob mBassKnob; + private RadialKnob mVirtualizerKnob; + + private H mHandler; + + private KnobCommander mKnobCommander; + + private long mLastDisabledNotifyTime = -1; + + public KnobContainer(Context context) { + super(context); + } + + public KnobContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public KnobContainer(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + private void init() { + mKnobCommander = KnobCommander.getInstance(mContext); + mHandler = new H(); + + if (!MasterConfigControl.getInstance(mContext).hasMaxxAudio()) { + // we must add the proper knobs dynamically. + if (mKnobCommander.hasBassBoost()) { + mBassContainer = addKnob(KnobCommander.KNOB_BASS); + } + if (mKnobCommander.hasTreble()) { + mTrebleContainer = addKnob(KnobCommander.KNOB_TREBLE); + } + if (mKnobCommander.hasVirtualizer()) { + mVirtualizerContainer = addKnob(KnobCommander.KNOB_VIRTUALIZER); + } + } + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + init(); + + if (DEBUG) Log.d(TAG, "onFinishInflate()"); + + OnTouchListener knobTouchListener = new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + Message message; + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + message = mHandler.obtainMessage(MSG_EXPAND, v.getTag()); + mHandler.sendMessageDelayed(message, 0); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mHandler.removeMessages(MSG_EXPAND); + message = mHandler.obtainMessage(MSG_CONTRACT, v.getTag()); + mHandler.sendMessageDelayed(message, 10); + break; + } + if (!v.isEnabled()) { + notifyDisabled(); + return true; + } + return false; + } + }; + + if (MasterConfigControl.getInstance(getContext()).hasMaxxAudio()) { + mVirtualizerContainer = (ViewGroup) findViewById(R.id.virtualizer_knob_container); + mBassContainer = (ViewGroup) findViewById(R.id.bass_knob_container); + mTrebleContainer = (ViewGroup) findViewById(R.id.treble_knob_container); + } + + if (mTrebleContainer != null) { + mTrebleKnob = (RadialKnob) mTrebleContainer.findViewById(R.id.knob); + mTrebleKnob.setTag(new KnobInfo(KnobCommander.KNOB_TREBLE, mTrebleKnob, + mTrebleContainer.findViewById(R.id.label))); + mTrebleKnob.setOnTouchListener(knobTouchListener); + mTrebleKnob.setOnKnobChangeListener( + KnobCommander.getInstance(getContext()).getRadialKnobCallback( + KnobCommander.KNOB_TREBLE + ) + ); + mTrebleKnob.setMax(100); + } + if (mBassContainer != null) { + mBassKnob = (RadialKnob) mBassContainer.findViewById(R.id.knob); + mBassKnob.setTag(new KnobInfo(KnobCommander.KNOB_BASS, mBassKnob, mBassContainer.findViewById(R.id.label))); + mBassKnob.setOnTouchListener(knobTouchListener); + mBassKnob.setOnKnobChangeListener( + KnobCommander.getInstance(getContext()).getRadialKnobCallback( + KnobCommander.KNOB_BASS + ) + ); + mBassKnob.setMax(100); + + + } + if (mVirtualizerContainer != null) { + mVirtualizerKnob = (RadialKnob) mVirtualizerContainer.findViewById(R.id.knob); + mVirtualizerKnob.setTag(new KnobInfo(KnobCommander.KNOB_VIRTUALIZER, mVirtualizerKnob, + mVirtualizerContainer.findViewById(R.id.label))); + mVirtualizerKnob.setOnTouchListener(knobTouchListener); + mVirtualizerKnob.setOnKnobChangeListener( + KnobCommander.getInstance(getContext()).getRadialKnobCallback( + KnobCommander.KNOB_VIRTUALIZER + ) + ); + mVirtualizerKnob.setMax(100); + } + updateKnobs(MasterConfigControl.getInstance(mContext).getCurrentDevice()); + + if (!MasterConfigControl.getInstance(mContext).hasMaxxAudio()) { + setLayoutTransition(null); + } + } + + private ViewGroup addKnob(int whichKnob) { + ViewGroup knobContainer = (ViewGroup) LayoutInflater.from(mContext) + .inflate(R.layout.generic_knob_control, this, false); + TextView label = (TextView) knobContainer.findViewById(R.id.label); + + int newContainerId = 0; + int knobLabelRes = 0; + switch (whichKnob) { + case KnobCommander.KNOB_BASS: + newContainerId = R.id.bass_knob_container; + knobLabelRes = R.string.bass; + break; + + case KnobCommander.KNOB_TREBLE: + newContainerId = R.id.treble_knob_container; + knobLabelRes = R.string.treble; + break; + + case KnobCommander.KNOB_VIRTUALIZER: + newContainerId = R.id.virtualizer_knob_container; + knobLabelRes = R.string.virtualizer; + break; + + default: + return null; + } + + knobContainer.setId(newContainerId); + label.setText(knobLabelRes); + + addView(knobContainer, getKnobParams()); + return knobContainer; + } + + private LayoutParams getKnobParams() { + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } else { + return new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1); + } + } + + public void setKnobVisible(int knob, boolean visible) { + final int newMode = visible ? View.VISIBLE : View.GONE; + ViewGroup v = null; + switch (knob) { + case KnobCommander.KNOB_VIRTUALIZER: + v = mVirtualizerContainer; + break; + case KnobCommander.KNOB_BASS: + v = mBassContainer; + break; + case KnobCommander.KNOB_TREBLE: + v = mTrebleContainer; + break; + } + if (v == null && visible) { + throw new UnsupportedOperationException("no knob container for knob: " + knob); + } + + if (newMode == v.getVisibility()) { + return; + } + Log.d(TAG, "setKnobVisible() knob=" + knob + " visible=" + visible); + v.setVisibility(newMode); + + // only used on maxx audio layout + if (MasterConfigControl.getInstance(mContext).hasMaxxAudio()) { + /* ensure spacing looks ok! + * + * it goes like, Space, knob layout, Space, knob layout, Space, etc..... + * starting with the first knob (skipping the first space), ensure the pairs have the + * same visibility so there's no extra space at the end. + */ + for (int i = 1; i < getChildCount() - 1; i += 2) { + View layout = getChildAt(i); + View space = getChildAt(i + 1); + if (space.getVisibility() != layout.getVisibility()) { + space.setVisibility(layout.getVisibility()); + } + } + } + } + + public void updateKnobHighlights(int color) { + if (mTrebleKnob != null) { + mTrebleKnob.setHighlightColor(color); + } + if (mBassKnob != null) { + mBassKnob.setHighlightColor(color); + } + if (mVirtualizerKnob != null) { + mVirtualizerKnob.setHighlightColor(color); + } + } + + private void notifyDisabled() { + final long now = System.currentTimeMillis(); + if (mLastDisabledNotifyTime == -1 || now - mLastDisabledNotifyTime > NOTIFY_DISABLE_DELAY) { + mLastDisabledNotifyTime = now; + Toast.makeText(mContext, R.string.effect_unavalable_for_speaker, + Toast.LENGTH_SHORT).show(); + } + } + + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private void resize(RadialKnob knob, View label, boolean makeBig) { + if (knob.isEnabled()) { + label.animate() + .alpha(makeBig ? 0 : 1) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(100); + + /* + if (makeBig) { + ResizeAnimation resizeAnimation = new ResizeAnimation(this); + resizeAnimation.setHeightParams(getHeight(), mExpandedHeight); + resizeAnimation.setDuration(100); + startAnimation(resizeAnimation); + } else { + ResizeAnimation resizeAnimation = new ResizeAnimation(this); + resizeAnimation.setHeightParams(getHeight(), mRegularHeight); + resizeAnimation.setDuration(100); + startAnimation(resizeAnimation); + } + */ + knob.resize(makeBig); + } + } + + @Override + public void onDeviceChanged(AudioDeviceInfo device, boolean userChange) { + if (device != null) { + updateKnobs(device); + } + } + + @Override + public void onGlobalDeviceToggle(boolean on) { + + } + + private void updateKnobs(AudioDeviceInfo device) { + if (device == null) { + return; + } + final boolean speaker = device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; + final boolean maxxAudio = MasterConfigControl.getInstance(mContext).hasMaxxAudio(); + final boolean dts = MasterConfigControl.getInstance(mContext).hasDts(); + final boolean effectsEnabled = !speaker || maxxAudio || dts; + + mKnobCommander.updateTrebleKnob(mTrebleKnob, effectsEnabled); + mKnobCommander.updateBassKnob(mBassKnob, effectsEnabled); + mKnobCommander.updateVirtualizerKnob(mVirtualizerKnob, effectsEnabled); + if (maxxAudio) { + // speaker? hide virtualizer + setKnobVisible(KnobCommander.KNOB_VIRTUALIZER, !speaker); + } else if (dts) { + // same for DTS + setKnobVisible(KnobCommander.KNOB_VIRTUALIZER, !speaker); + } else { + setKnobVisible(KnobCommander.KNOB_VIRTUALIZER, true); + } + } + + public static class KnobInfo { + int whichKnob; + RadialKnob knob; + View label; + + public KnobInfo(int whichKnob, RadialKnob knob, View label) { + this.knob = knob; + this.label = label; + this.whichKnob = whichKnob; + } + } + + private class H extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EXPAND: + case MSG_CONTRACT: + RadialKnob knob = ((KnobInfo) msg.obj).knob; + View label = ((KnobInfo) msg.obj).label; + boolean expand = msg.what == MSG_EXPAND; + resize(knob, label, expand); + break; + } + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/knobs/RadialKnob.java b/src/org/cyanogenmod/audiofx/audiofx/knobs/RadialKnob.java new file mode 100644 index 0000000..0e4cbb2 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/knobs/RadialKnob.java @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, The CyanogenMod Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.cyngn.audiofx.knobs; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.RectF; +import android.graphics.Typeface; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.widget.Toast; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.stats.UserSession; + +public class RadialKnob extends View { + + private static final String TAG = RadialKnob.class.getSimpleName(); + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final float REGULAR_SCALE = 0.8f; + + public static final float TOUCHING_SCALE = 1f; + private static final int DO_NOT_VIBRATE_THRESHOLD = 100; + + private static final int DEGREE_OFFSET = -225; + private static final int START_ANGLE = 360 + DEGREE_OFFSET; + private static final int MAX_DEGREES = 270; + + private final Paint mPaint, mTextPaint; + + ValueAnimator mAnimator; + float mOffProgress; + boolean mAnimating = false; + long mDownTime; + long mUpTime; + private OnKnobChangeListener mOnKnobChangeListener = null; + private float mProgress = 0.0f; + private float mTouchProgress = 0.0f; + private int mMax = 100; + private boolean mOn = false; + private boolean mEnabled = true; + private float mLastX; + private float mLastY; + private boolean mMoved; + private int mWidth = 0; + private RectF mRectF, mOuterRect = new RectF(), mInnerRect = new RectF(); + private float mLastAngle; + private Long mLastVibrateTime; + private int mHighlightColor; + private int mBackgroundArcColor; + private int mBackgroundArcColorDisabled; + private int mRectPadding; + private int mStrokeWidth; + private float mHandleWidth; // little square indicator where user touches + private float mTextOffset; + + Path mPath = new Path(); + PathMeasure mPathMeasure = new PathMeasure(); + float[] mTmp = new float[2]; + float mStartX, mStopX, mStartY, mStopY; + + public RadialKnob(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + Resources res = getResources(); + mBackgroundArcColor = res.getColor(R.color.radial_knob_arc_bg); + mBackgroundArcColorDisabled = res.getColor(R.color.radial_knob_arc_bg_disabled); + mHighlightColor = res.getColor(R.color.highlight); + + mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD)); + mTextPaint.setTextAlign(Paint.Align.CENTER); + mTextPaint.setElegantTextHeight(true); + mTextPaint.setFakeBoldText(true); + mTextPaint.setTextSize(res.getDimension(R.dimen.radial_text_size)); + mTextPaint.setColor(Color.LTGRAY); + + mTextOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, + getResources().getDisplayMetrics()); + + mHandleWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, + getResources().getDisplayMetrics()); + + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setColor(mHighlightColor); + mPaint.setStrokeWidth(mStrokeWidth = res.getDimensionPixelSize(R.dimen.radial_knob_stroke)); + mPaint.setStrokeCap(Paint.Cap.BUTT); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setShadowLayer(2, 1, -2, getResources().getColor(R.color.black)); + + setScaleX(REGULAR_SCALE); + setScaleY(REGULAR_SCALE); + + mRectPadding = res.getDimensionPixelSize(R.dimen.radial_rect_padding); + invalidate(); + } + + public RadialKnob(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public RadialKnob(Context context) { + this(context, null); + } + + public void setValue(int value) { + if (mMax != 0) { + setProgress(((float) value) / mMax); + mTouchProgress = mProgress; + mLastAngle = mProgress * MAX_DEGREES; + } + } + + public void setProgress(float progress, boolean fromUser) { + if (progress > 1.0f) { + progress = 1.0f; + } + if (progress < 0.0f) { + progress = 0.0f; + } + + mProgress = progress; + + invalidate(); + + if (mOnKnobChangeListener != null) { + mOnKnobChangeListener.onValueChanged(this, (int) (progress * mMax), fromUser); + } + } + + public void setMax(int max) { + mMax = max; + } + + public float getProgress() { + return mProgress; + } + + public void setProgress(float progress) { + setProgress(progress, false); + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public void setEnabled(boolean enabled) { + mEnabled = enabled; + if (enabled) { + setOn(mOn); + } + invalidate(); + } + + public void setOn(final boolean on) { + mOn = on; + if (mAnimator != null) { + mAnimator.cancel(); + } + invalidate(); + } + + public void setHighlightColor(int color) { + mPaint.setColor(mHighlightColor = color); + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + mPaint.setStrokeWidth(mStrokeWidth); + + mPaint.setColor(mEnabled ? mBackgroundArcColor : mBackgroundArcColorDisabled); + canvas.drawArc(mRectF, START_ANGLE, MAX_DEGREES, false, mPaint); + + final float sweepAngle = mEnabled ? mProgress * MAX_DEGREES : 0; + if (mOn) { + mPaint.setColor(mHighlightColor); + canvas.drawArc(mRectF, START_ANGLE, sweepAngle, false, mPaint); + } + + final float indicatorSweepAngle = Math.max(1f, sweepAngle); + + // render the indicator + mPath.reset(); + mPath.arcTo(mInnerRect, START_ANGLE, indicatorSweepAngle, true); + + mPathMeasure.setPath(mPath, false); + mPathMeasure.getPosTan(mPathMeasure.getLength(), mTmp, null); + + mStartX = mTmp[0]; + mStartY = mTmp[1]; + + mPath.reset(); + mPath.arcTo(mOuterRect, START_ANGLE, indicatorSweepAngle, true); + + mPathMeasure.setPath(mPath, false); + mPathMeasure.getPosTan(mPathMeasure.getLength(), mTmp, null); + + mStopX = mTmp[0]; + mStopY = mTmp[1]; + + mPaint.setStrokeWidth(mHandleWidth); + mPaint.setColor(Color.WHITE); + canvas.drawLine(mStartX, mStartY, mStopX, mStopY, mPaint); + + canvas.drawText(getProgressText(), + mOuterRect.centerX(), + mOuterRect.centerY() + (mTextPaint.getTextSize() / 2.f) - mTextOffset, + mTextPaint); + } + + private String getProgressText() { + if (mEnabled) { + return ((int) (mProgress * 100)) + "%"; + } else { + return "--"; + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldW, int oldH) { + super.onSizeChanged(w, h, oldW, oldH); + + int size = w > h ? h : w; + mWidth = size; + int diff; + if (w > h) { + diff = (w - h) / 2; + mRectF = new RectF(mRectPadding + diff, mRectPadding, + w - mRectPadding - diff, h - mRectPadding); + } else { + diff = (h - w) / 2; + mRectF = new RectF(mRectPadding, mRectPadding + diff, + w - mRectPadding, h - mRectPadding - diff); + } + mOuterRect.set(mRectF); + mOuterRect.inset(-mRectPadding, -mRectPadding); + mInnerRect.set(mRectF); + mInnerRect.inset(mRectPadding, mRectPadding); + } + + private boolean isUserSelected() { + return getScaleX() == TOUCHING_SCALE && getScaleY() == TOUCHING_SCALE; + } + + private void animateTo(float progress) { + if (DEBUG) Log.w(TAG, "animateTo(" + progress + ")"); + if (mAnimator != null) { + mAnimator.cancel(); + } + mAnimator = ValueAnimator.ofFloat(mProgress, progress); + mAnimator.setDuration(100); + mAnimator.setInterpolator(new AccelerateInterpolator()); + mAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mAnimating = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mAnimating = false; + postInvalidate(); + } + + @Override + public void onAnimationCancel(Animator animation) { + mAnimating = false; + postInvalidate(); + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float progress = (Float) animation.getAnimatedValue(); + mProgress = progress; + mLastAngle = mProgress * MAX_DEGREES; + if (DEBUG) Log.i(TAG, "onAnimationUpdate(): mProgress: " + + mProgress + ", mLastAngle: " + mLastAngle); + + setProgress(mProgress); + if (mOnKnobChangeListener != null) { + mOnKnobChangeListener.onValueChanged(RadialKnob.this, + (int) (progress * mMax), true); + } + postInvalidate(); + } + }); + mAnimator.start(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + final float x = event.getX(); + final float y = event.getY(); + + if (!mEnabled) { + return false; + } + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mDownTime = System.currentTimeMillis(); + mOffProgress = 0; + + getParent().requestDisallowInterceptTouchEvent(true); + vibrate(); + mLastX = event.getX(); + mLastY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + // we can be animating while moving + if (mAnimating) { + return true; + } + final float center = getWidth() / 2; + final float radius = (center / 2) - (mRectPadding * 2); + + final boolean inDeadzone = inCircle(x, y, center, center, radius); + final boolean inOuterCircle = inCircle(x, y, center, center, radius + 70); + if (DEBUG) + Log.d(TAG, "inOuterCircle: " + inOuterCircle + ", inDeadzone: " + inDeadzone); + final float delta = getDelta(x, y); + final float angle = angleWithOffset(x, y, DEGREE_OFFSET); + + if (mOn) { + if (isUserSelected() && (!inDeadzone)) { + float angleDiff = Math.abs(mLastAngle - angle); + if (mProgress == 1 && angle < (MAX_DEGREES / 2)) { + // oh jeez. no jumping from 100! + return true; + } + if (angleDiff < 90) { + // jump! + //Log.w(TAG, "using angle"); + mLastAngle = angle; + mTouchProgress = angle / MAX_DEGREES; + mMoved = true; + if (DEBUG) Log.v(TAG, "ANGLE setProgress: " + mTouchProgress); + setProgress(mTouchProgress, true); + } else if (angle > 0 && angle < MAX_DEGREES) { + if (DEBUG) Log.v(TAG, "ANGLE animateTo: " + angle); + mMoved = true; + animateTo(angle / MAX_DEGREES); + } + } + // if it's less than one degree, turn it off + // 1% ~= 2.7 degrees, pick something slightly higher + if (mTouchProgress < (2.71f / MAX_DEGREES) && mOn && mMoved) { + mTouchProgress = (2.71f / MAX_DEGREES); + if (mOnKnobChangeListener != null) { + mOnKnobChangeListener.onSwitchChanged(this, !mOn); + } + setOn(!mOn); + } + } else { + // off + if (isUserSelected() && (!inDeadzone)) { + if (delta > 0) { + mOffProgress += delta; + } else if (angle > 90) { + mOffProgress = 0; + } + if (DEBUG) + Log.d(TAG, "OFF, touching angle: " + angle + + ", mOffProgress: " + mOffProgress + ", delta " + delta); + // we want at least 1%, how many degrees = 1%? + a little padding + final float onePercentInDegrees = (MAX_DEGREES / 100) + 1f; + if (mOffProgress > 15 && angle < MAX_DEGREES + && angle >= onePercentInDegrees) { + if (DEBUG) Log.w(TAG, "delta: " + delta); + if (angle <= MAX_DEGREES) { + if (mOnKnobChangeListener != null) { + mOnKnobChangeListener.onSwitchChanged(this, !mOn); + } + + setOn(!mOn); + if (angle > 30) { + animateTo(angle / MAX_DEGREES); + } else { + setProgress(angle / MAX_DEGREES, true); + } + mLastAngle = angle; + mMoved = false; + } else { + if (DEBUG) Log.w(TAG, "off, angle > 300, ignoring"); + } + } + } + mLastX = x; + mLastY = y; + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mUpTime = System.currentTimeMillis(); + final float finalAngle = angleWithOffset(x, y, DEGREE_OFFSET); + if (DEBUG) Log.d(TAG, "angle at death: " + finalAngle); + if (mUpTime - mDownTime < 100 && mMoved && finalAngle < MAX_DEGREES) { + if (mOn) { + animateTo(finalAngle / MAX_DEGREES); + } else { + if (mOnKnobChangeListener != null) { + mOnKnobChangeListener.onSwitchChanged(this, !mOn); + } + + setOn(!mOn); + } + } + if (mMoved) { + UserSession.getInstance() + .knobOptionsAdjusted(((KnobContainer.KnobInfo)getTag()).whichKnob); + } + mLastX = -1; + mLastY = -1; + mOffProgress = 0; + mMoved = false; + break; + default: + break; + } + return true; + } + + private void vibrate() { + if (mLastVibrateTime == null || System.currentTimeMillis() - mLastVibrateTime + > DO_NOT_VIBRATE_THRESHOLD) { + Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(40); + mLastVibrateTime = System.currentTimeMillis(); + } + } + + public void resize(boolean selected) { + if (!mEnabled) { + return; + } + if (selected) { + animate() + .scaleY(RadialKnob.TOUCHING_SCALE) + .scaleX(RadialKnob.TOUCHING_SCALE) + .setDuration(100); + } else { + animate() + .scaleY(RadialKnob.REGULAR_SCALE) + .scaleX(RadialKnob.REGULAR_SCALE) + .setDuration(100); + } + } + + private float getDelta(float x, float y) { + float angle = angle(x, y); + float oldAngle = angle(mLastX, mLastY); + float delta = angle - oldAngle; + if (delta >= 180.0f) { + delta = -oldAngle; + } else if (delta <= -180.0f) { + delta = 360 - oldAngle; + } + return delta; + } + + private float angle(float x, float y) { + float center = mWidth / 2.0f; + x -= center; + y -= center; + + if (x == 0.0f) { + if (y > 0.0f) { + return 180.0f; + } else { + return 0.0f; + } + } + + float angle = (float) (Math.atan(y / x) / Math.PI * 180.0); + if (x > 0.0f) { + angle += 90; + } else { + angle += 270; + } + return angle; + } + + private float angleWithOffset(float x, float y, int degreeOffset) { + float angle = angle(x, y); + if (angle > 180) { + angle += degreeOffset; + } else { + angle += (360 + degreeOffset); + } + return angle; + } + + + private static boolean inCircle(float x, float y, float circleCenterX, float circleCenterY, + float circleRadius) { + double dx = Math.pow(x - circleCenterX, 2); + double dy = Math.pow(y - circleCenterY, 2); + + if ((dx + dy) < Math.pow(circleRadius, 2)) { + return true; + } else { + return false; + } + } + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + public void setOnKnobChangeListener(OnKnobChangeListener l) { + mOnKnobChangeListener = l; + } + + public interface OnKnobChangeListener { + void onValueChanged(RadialKnob knob, int value, boolean fromUser); + + boolean onSwitchChanged(RadialKnob knob, boolean on); + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/preset/InfinitePagerAdapter.java b/src/org/cyanogenmod/audiofx/audiofx/preset/InfinitePagerAdapter.java new file mode 100644 index 0000000..71811f4 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/preset/InfinitePagerAdapter.java @@ -0,0 +1,95 @@ +package com.cyngn.audiofx.preset; + +import android.os.Parcelable; +import android.support.v4.view.PagerAdapter; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +/** + * A PagerAdapter that wraps around another PagerAdapter to handle paging wrap-around. + */ +public class InfinitePagerAdapter extends PagerAdapter { + + private static final String TAG = "InfinitePagerAdapter"; + private static final boolean DEBUG = false; + + private PagerAdapter adapter; + + public InfinitePagerAdapter(PagerAdapter adapter) { + this.adapter = adapter; + } + + @Override + public int getCount() { + // warning: scrolling to very high values (1,000,000+) results in + // strange drawing behaviour + return Integer.MAX_VALUE; + } + + /** + * @return the {@link #getCount()} result of the wrapped adapter + */ + public int getRealCount() { + return adapter.getCount(); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + int virtualPosition = position % getRealCount(); + debug("instantiateItem: real position: " + position); + debug("instantiateItem: virtual position: " + virtualPosition); + + // only expose virtual position to the inner adapter + return adapter.instantiateItem(container, virtualPosition); + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + int virtualPosition = position % getRealCount(); + debug("destroyItem: real position: " + position); + debug("destroyItem: virtual position: " + virtualPosition); + + // only expose virtual position to the inner adapter + adapter.destroyItem(container, virtualPosition, object); + } + + /* + * Delegate rest of methods directly to the inner adapter. + */ + + @Override + public void finishUpdate(ViewGroup container) { + adapter.finishUpdate(container); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return adapter.isViewFromObject(view, object); + } + + @Override + public void restoreState(Parcelable bundle, ClassLoader classLoader) { + adapter.restoreState(bundle, classLoader); + } + + @Override + public Parcelable saveState() { + return adapter.saveState(); + } + + @Override + public void startUpdate(ViewGroup container) { + adapter.startUpdate(container); + } + + /* + * End delegation + */ + + private void debug(String message) { + if (DEBUG) { + Log.d(TAG, message); + } + } +}
\ No newline at end of file diff --git a/src/org/cyanogenmod/audiofx/audiofx/preset/InfiniteViewPager.java b/src/org/cyanogenmod/audiofx/audiofx/preset/InfiniteViewPager.java new file mode 100644 index 0000000..8a4ed38 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/preset/InfiniteViewPager.java @@ -0,0 +1,106 @@ +package com.cyngn.audiofx.preset; + + +import android.content.Context; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; + +/** + * A {@link ViewPager} that allows pseudo-infinite paging with a wrap-around effect. Should be used with an {@link + * InfinitePagerAdapter}. + */ +public class InfiniteViewPager extends ViewPager { + + private final EqualizerManager mEqManager; + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + public InfiniteViewPager(Context context) { + super(context); + mEqManager = MasterConfigControl.getInstance(context).getEqualizerManager(); + } + + public InfiniteViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + mEqManager = MasterConfigControl.getInstance(context).getEqualizerManager(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mEqManager.isAnimatingToCustom()) { + return false; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int textSize = getResources().getDimensionPixelSize(R.dimen.preset_text_size) + + getResources().getDimensionPixelSize(R.dimen.preset_text_padding); + super.onMeasure(widthMeasureSpec, + MeasureSpec.makeMeasureSpec(textSize, MeasureSpec.EXACTLY)); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mEqManager.isAnimatingToCustom()) { + return false; + } + boolean result; + try { + result = super.onTouchEvent(ev); + } catch (IllegalArgumentException e) { + /* There's a bug with the support library where it doesn't check + * the proper pointer index, so when multi touching the container, + * this can sometimes be thrown. Supposedly there are no downsides to just + * catching the exception and moving along, so let's do that.... + */ + result = false; + } + return result; + } + + @Override + public void setAdapter(PagerAdapter adapter) { + super.setAdapter(adapter); + // offset first element so that we can scroll to the left + setCurrentItem(0); + } + + @Override + public void setCurrentItem(int item) { + // offset the current item to ensure there is space to scroll + item = getOffsetAmount() + (item % getAdapter().getCount()); + super.setCurrentItem(item); + } + + public void setCurrentItemAbsolute(int item) { + super.setCurrentItem(item); + } + + private int getOffsetAmount() { + if (getAdapter() instanceof InfinitePagerAdapter) { + InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter(); + // allow for 100 back cycles from the beginning + // should be enough to create an illusion of infinity + // warning: scrolling to very high values (1,000,000+) results in + // strange drawing behaviour + return infAdapter.getRealCount() * 100; + } else { + return 0; + } + } + + public void setCurrentItemAbsolute(int newPage, boolean b) { + super.setCurrentItem(newPage, b); + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/preset/PresetPagerAdapter.java b/src/org/cyanogenmod/audiofx/audiofx/preset/PresetPagerAdapter.java new file mode 100644 index 0000000..f6875f7 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/preset/PresetPagerAdapter.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cyngn.audiofx.preset; + +import android.content.Context; +import android.support.v4.view.PagerAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.EqualizerManager; +import com.cyngn.audiofx.activity.MasterConfigControl; + +public class PresetPagerAdapter extends PagerAdapter { + + private final Context mContext; + private final EqualizerManager mEqManager; + + public PresetPagerAdapter(Context context) { + super(); + mContext = context; + mEqManager = MasterConfigControl.getInstance(mContext).getEqualizerManager(); + } + + @Override + public int getItemPosition(Object object) { + View v = (View) object; + int index = mEqManager.indexOf(((Preset) v.getTag())); + if (index == -1) { + return POSITION_NONE; + } else { + return index; + } + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + View view = LayoutInflater.from(mContext) + .inflate(R.layout.preset_adapter_row, container, false); + TextView tv = (TextView) view; + tv.setText(mEqManager.getLocalizedPresetName(position)); + tv.setTag(mEqManager.getPreset(position)); + container.addView(tv); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + if (object instanceof View) { + container.removeView((View) object); + } + } + + @Override + public int getCount() { + return mEqManager.getPresetCount(); + } + + @Override + public boolean isViewFromObject(View view, Object o) { + return view == o; + } + + + +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/receiver/QuickSettingsTileReceiver.java b/src/org/cyanogenmod/audiofx/audiofx/receiver/QuickSettingsTileReceiver.java new file mode 100644 index 0000000..c27d44a --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/receiver/QuickSettingsTileReceiver.java @@ -0,0 +1,36 @@ +package com.cyngn.audiofx.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.service.AudioFxService; + +/** + * Created by roman on 1/13/16. + */ +public class QuickSettingsTileReceiver extends BroadcastReceiver { + + private static final boolean DEBUG = false; + private static final String TAG = "QSTileReceiver"; + + public static final String ACTION_TOGGLE_CURRENT_DEVICE + = "com.cyngn.audiofx.action.TOGGLE_DEVICE"; + + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Log.i(TAG, "onReceive() called with " + "context = [" + context + "], intent = [" + intent + "]"); + } + if (ACTION_TOGGLE_CURRENT_DEVICE.equals(intent.getAction())) { + final MasterConfigControl config = MasterConfigControl.getInstance(context); + + config.setCurrentDeviceEnabled(!config.isCurrentDeviceEnabled()); + + // tell service explicitly to update the qs tile in case UI isn't up to let it know + context.startService(new Intent(AudioFxService.ACTION_UPDATE_TILE) + .setClass(context, AudioFxService.class)); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/ServiceDispatcher.java b/src/org/cyanogenmod/audiofx/audiofx/receiver/ServiceDispatcher.java index 7a838b1..7581ba2 100644 --- a/src/org/cyanogenmod/audiofx/ServiceDispatcher.java +++ b/src/org/cyanogenmod/audiofx/audiofx/receiver/ServiceDispatcher.java @@ -1,19 +1,5 @@ -/* - * Copyright (C) 2016 The CyanogenMod Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.cyanogenmod.audiofx; + +package com.cyngn.audiofx.receiver; import android.content.BroadcastReceiver; import android.content.Context; @@ -21,6 +7,8 @@ import android.content.Intent; import android.media.audiofx.AudioEffect; import android.util.Log; +import com.cyngn.audiofx.service.AudioFxService; + import cyanogenmod.media.AudioSessionInfo; import cyanogenmod.media.CMAudioManager; @@ -28,7 +16,7 @@ public class ServiceDispatcher extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Intent service = new Intent(context.getApplicationContext(), HeadsetService.class); + Intent service = new Intent(context.getApplicationContext(), AudioFxService.class); String action = intent.getAction(); // We can also get AUDIO_BECOMING_NOISY, which means a device change is @@ -39,10 +27,11 @@ public class ServiceDispatcher extends BroadcastReceiver { String pkg = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME); service.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId); service.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, pkg); + } else if (action.equals(CMAudioManager.ACTION_AUDIO_SESSIONS_CHANGED)) { // callback from CMAudioService - final AudioSessionInfo info = (AudioSessionInfo)intent.getParcelableExtra( + final AudioSessionInfo info = (AudioSessionInfo) intent.getParcelableExtra( CMAudioManager.EXTRA_SESSION_INFO); boolean added = intent.getBooleanExtra(CMAudioManager.EXTRA_SESSION_ADDED, false); service.putExtra(CMAudioManager.EXTRA_SESSION_INFO, info); @@ -51,5 +40,9 @@ public class ServiceDispatcher extends BroadcastReceiver { service.setAction(action); context.startService(service); + if (AudioFxService.DEBUG) { + Log.d("AudioFX-Dispatcher", "Received " + action); + } + } } diff --git a/src/org/cyanogenmod/audiofx/audiofx/service/AudioFxService.java b/src/org/cyanogenmod/audiofx/audiofx/service/AudioFxService.java new file mode 100644 index 0000000..ce158b5 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/service/AudioFxService.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2016 Cyanogen Inc. + * + * Proprietary and confidential. + */ +package com.cyngn.audiofx.service; + +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.content.res.Configuration; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.media.audiofx.AudioEffect; +import android.os.Binder; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.ActivityMusic; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.backends.EffectSet; +import com.cyngn.audiofx.receiver.QuickSettingsTileReceiver; + +import java.lang.ref.WeakReference; +import java.util.Locale; + +import cyanogenmod.app.CMStatusBarManager; +import cyanogenmod.app.CustomTile; +import cyanogenmod.media.AudioSessionInfo; +import cyanogenmod.media.CMAudioManager; + +/** + * This service is responsible for applying all requested effects from the AudioFX UI. + * + * Since the AudioFX UI allows for different configurations based on the current output device, + * the service is also responsible for applying the effects properly based on user configuration, + * and the current device output state. + */ +public class AudioFxService extends Service + implements AudioOutputChangeListener.AudioOutputChangedCallback { + + static final String TAG = AudioFxService.class.getSimpleName(); + + public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final boolean ENABLE_REVERB = false; + + public static final String ACTION_DEVICE_OUTPUT_CHANGED + = "org.cyanogenmod.audiofx.ACTION_DEVICE_OUTPUT_CHANGED"; + + public static final String ACTION_UPDATE_TILE = "com.cyngn.audiofx.action.UPDATE_TILE"; + + public static final String EXTRA_DEVICE = "device"; + + // flags for updateService to minimize DSP traffic + public static final int EQ_CHANGED = 0x1; + public static final int BASS_BOOST_CHANGED = 0x2; + public static final int VIRTUALIZER_CHANGED = 0x4; + public static final int TREBLE_BOOST_CHANGED = 0x8; + public static final int VOLUME_BOOST_CHANGED = 0x10; + public static final int REVERB_CHANGED = 0x20; + public static final int ALL_CHANGED = 0xFF; + + // flags from audio.h, used by session callbacks + static final int AUDIO_OUTPUT_FLAG_FAST = 0x4; + static final int AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8; + static final int AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10; + + private static final int TILE_ID = 555; + + private Locale mLastLocale; + + private CustomTile mTile; + private CustomTile.Builder mTileBuilder; + + private AudioOutputChangeListener mOutputListener; + private DevicePreferenceManager mDevicePrefs; + private SessionManager mSessionManager; + private Handler mHandler; + + private AudioDeviceInfo mCurrentDevice; + + public static class LocalBinder extends Binder { + + final WeakReference<AudioFxService> mService; + + public LocalBinder(AudioFxService service) {// added a constructor for Stub here + mService = new WeakReference<AudioFxService>(service); + } + + private boolean checkService() { + if (mService.get() == null) { + Log.e("AudioFx-LocalBinder", "Service was null!"); + } + return mService.get() != null; + } + + public void update(int flags) { + if (checkService()) { + mService.get().update(flags); + } + } + + public void setOverrideLevels(short band, float level) { + if (checkService()) { + mService.get().mSessionManager.setOverrideLevels(band, level); + } + } + + public EffectSet getEffect(Integer id) { + if (checkService()) { + return mService.get().mSessionManager.getEffectForSession(id); + } + return null; + } + } + + @Override + public void onCreate() { + super.onCreate(); + if (DEBUG) Log.i(TAG, "Starting service."); + + HandlerThread handlerThread = new HandlerThread("AudioFx-Backend"); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + + mOutputListener = new AudioOutputChangeListener(getApplicationContext(), mHandler); + mOutputListener.addCallback(this); + + mDevicePrefs = new DevicePreferenceManager(getApplicationContext(), mCurrentDevice); + if (!mDevicePrefs.initDefaults()) { + stopSelf(); + return; + } + + mSessionManager = new SessionManager(getApplicationContext(), mHandler, mDevicePrefs, + mCurrentDevice); + mOutputListener.addCallback(mDevicePrefs, mSessionManager); + + final CMAudioManager cma = CMAudioManager.getInstance(getApplicationContext()); + for (AudioSessionInfo asi : cma.listAudioSessions(AudioManager.STREAM_MUSIC)) { + mSessionManager.addSession(asi); + } + + updateQsTile(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) { + Log.i(TAG, "onStartCommand() called with " + "intent = [" + intent + "], flags = [" + + flags + "], startId = [" + startId + "]"); + } + if (intent != null && intent.getAction() != null) { + if (ACTION_UPDATE_TILE.equals(intent.getAction())) { + update(ALL_CHANGED); + } else { + String action = intent.getAction(); + int sessionId = intent.getIntExtra(AudioEffect.EXTRA_AUDIO_SESSION, 0); + String pkg = intent.getStringExtra(AudioEffect.EXTRA_PACKAGE_NAME); + int stream = mapContentTypeToStream( + intent.getIntExtra(AudioEffect.EXTRA_CONTENT_TYPE, + AudioEffect.CONTENT_TYPE_MUSIC)); + + if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION)) { + if (DEBUG) { + Log.i(TAG, String.format("New audio session: %d package: %s contentType=%d", + sessionId, pkg, stream)); + } + AudioSessionInfo info = new AudioSessionInfo(sessionId, stream, -1, -1, -1); + mSessionManager.addSession(info); + + } else if (action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) { + + AudioSessionInfo info = new AudioSessionInfo(sessionId, stream, -1, -1, -1); + mSessionManager.removeSession(info); + + } else if (action.equals(CMAudioManager.ACTION_AUDIO_SESSIONS_CHANGED)) { + + final AudioSessionInfo info = (AudioSessionInfo) intent.getParcelableExtra( + CMAudioManager.EXTRA_SESSION_INFO); + if (info != null && info.getSessionId() > 0) { + boolean added = intent.getBooleanExtra(CMAudioManager.EXTRA_SESSION_ADDED, + false); + if (added) { + mSessionManager.addSession(info); + } else { + mSessionManager.removeSession(info); + } + } + + } + } + } + return START_STICKY; + } + + /** + * maps {@link AudioEffect#EXTRA_CONTENT_TYPE} to an AudioManager.STREAM_* item + */ + private static int mapContentTypeToStream(int contentType) { + switch (contentType) { + case AudioEffect.CONTENT_TYPE_VOICE: + return AudioManager.STREAM_VOICE_CALL; + case AudioEffect.CONTENT_TYPE_GAME: + // explicitly don't support game effects right now + return -1; + case AudioEffect.CONTENT_TYPE_MOVIE: + case AudioEffect.CONTENT_TYPE_MUSIC: + default: + return AudioManager.STREAM_MUSIC; + } + } + + @Override + public synchronized void onAudioOutputChanged(boolean firstChange, + AudioDeviceInfo outputDevice) { + if (outputDevice == null) { + return; + } + + mCurrentDevice = outputDevice; + + if (DEBUG) + Log.d(TAG, "Broadcasting device changed event"); + + // Update the UI with the change + Intent intent = new Intent(ACTION_DEVICE_OUTPUT_CHANGED); + intent.putExtra("device", outputDevice.getId()); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); + + updateQsTile(); + } + + private void updateQsTile() { + if (mCurrentDevice == null || mDevicePrefs == null) { + // too early + return; + } + if (mTileBuilder == null) { + mTileBuilder = new CustomTile.Builder(this); + } + + mLastLocale = getResources().getConfiguration().locale; + final PendingIntent pi = PendingIntent.getBroadcast(this, 0, + new Intent(QuickSettingsTileReceiver.ACTION_TOGGLE_CURRENT_DEVICE) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .setClass(this, QuickSettingsTileReceiver.class), 0); + + final PendingIntent longPress = PendingIntent.getActivity(this, 0, + new Intent(this, ActivityMusic.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0); + + String label = getString(R.string.qs_tile_label, + MasterConfigControl.getDeviceDisplayString(this, mCurrentDevice)); + + mTileBuilder + .hasSensitiveData(false) + .setIcon(mDevicePrefs.isGlobalEnabled() ? R.drawable.ic_qs_visualizer_on + : R.drawable.ic_qs_visualizer_off) + .setLabel(label) + .setContentDescription(R.string.qs_tile_content_description) + .shouldCollapsePanel(false) + .setOnClickIntent(pi) + .setOnLongClickIntent(longPress); + + mTile = mTileBuilder.build(); + + CMStatusBarManager.getInstance(this).publishTile(TILE_ID, mTile); + } + + @Override + public void onDestroy() { + if (DEBUG) Log.i(TAG, "Stopping service."); + + mOutputListener.removeCallback(this, mSessionManager, mDevicePrefs); + mSessionManager.onDestroy(); + + CMStatusBarManager.getInstance(this).removeTile(TILE_ID); + + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return new LocalBinder(this); + } + + @Override + public void onTrimMemory(int level) { + if (DEBUG) Log.d(TAG, "onTrimMemory: level=" + level); + switch (level) { + case TRIM_MEMORY_BACKGROUND: + case TRIM_MEMORY_MODERATE: + case TRIM_MEMORY_RUNNING_MODERATE: + case TRIM_MEMORY_COMPLETE: + if (DEBUG) Log.d(TAG, "killing service if no effects active."); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + if (!mSessionManager.hasActiveSessions()) { + stopSelf(); + Log.w(TAG, "self destructing, no sessions active and nothing to do."); + } + } + }, 1000); + break; + } + } + + /** + * Queue up a backend update. + */ + private void update(int flags) { + mSessionManager.update(flags); + + if ((flags & ALL_CHANGED) == ALL_CHANGED) { + updateQsTile(); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (!mLastLocale.equals(newConfig.locale)) { + updateQsTile(); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/service/AudioOutputChangeListener.java b/src/org/cyanogenmod/audiofx/audiofx/service/AudioOutputChangeListener.java new file mode 100644 index 0000000..f85d9c1 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/service/AudioOutputChangeListener.java @@ -0,0 +1,131 @@ + +package com.cyngn.audiofx.service; + +import static android.media.AudioDeviceInfo.convertDeviceTypeToInternalDevice; + +import android.content.Context; +import android.media.AudioDeviceCallback; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.os.Handler; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class AudioOutputChangeListener extends AudioDeviceCallback { + + private static final String TAG = "AudioFx-" + AudioOutputChangeListener.class.getSimpleName(); + + private boolean mInitial = true; + + private final Context mContext; + private final AudioManager mAudioManager; + private final Handler mHandler; + private int mLastDevice = -1; + + private final ArrayList<AudioOutputChangedCallback> mCallbacks = new ArrayList<AudioOutputChangedCallback>(); + + public interface AudioOutputChangedCallback { + public void onAudioOutputChanged(boolean firstChange, AudioDeviceInfo outputDevice); + } + + public AudioOutputChangeListener(Context context, Handler handler) { + mContext = context; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mHandler = handler; + } + + public void addCallback(AudioOutputChangedCallback... callbacks) { + synchronized (mCallbacks) { + boolean initial = mCallbacks.size() == 0; + mCallbacks.addAll(Arrays.asList(callbacks)); + if (initial) { + mAudioManager.registerAudioDeviceCallback(this, mHandler); + } + } + } + + public void removeCallback(AudioOutputChangedCallback... callbacks) { + synchronized (mCallbacks) { + mCallbacks.removeAll(Arrays.asList(callbacks)); + if (mCallbacks.size() == 0) { + mAudioManager.unregisterAudioDeviceCallback(this); + } + } + } + + private void callback() { + synchronized (mCallbacks) { + final AudioDeviceInfo device = getCurrentDevice(); + + if (device == null) { + Log.w(TAG, "Unable to determine audio device!"); + return; + } + + if (mInitial || device.getId() != mLastDevice) { + Log.d(TAG, "onAudioOutputChanged id: " + device.getId() + + " type: " + device.getType() + + " name: " + device.getProductName() + + " address: " + device.getAddress() + + " [" + device.toString() + "]"); + mLastDevice = device.getId(); + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mCallbacks) { + for (AudioOutputChangedCallback callback : mCallbacks) { + callback.onAudioOutputChanged(mInitial, device); + } + } + } + }); + + if (mInitial) { + mInitial = false; + } + } + } + } + + public void refresh() { + callback(); + } + + @Override + public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { + callback(); + } + + @Override + public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { + callback(); + } + + public List<AudioDeviceInfo> getConnectedOutputs() { + final List<AudioDeviceInfo> outputs = new ArrayList<AudioDeviceInfo>(); + final int forMusic = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) { + if ((convertDeviceTypeToInternalDevice(ai.getType()) & forMusic) > 0) { + outputs.add(ai); + } + } + return outputs; + } + + public AudioDeviceInfo getCurrentDevice() { + final List<AudioDeviceInfo> devices = getConnectedOutputs(); + return devices.size() > 0 ? devices.get(0) : null; + } + + public AudioDeviceInfo getDeviceById(int id) { + for (AudioDeviceInfo ai : mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) { + if (ai.getId() == id) { + return ai; + } + } + return null; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/service/BootReceiver.java b/src/org/cyanogenmod/audiofx/audiofx/service/BootReceiver.java new file mode 100644 index 0000000..df853a5 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/service/BootReceiver.java @@ -0,0 +1,14 @@ +package com.cyngn.audiofx.service; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import com.cyngn.audiofx.Constants; + +public class BootReceiver extends BroadcastReceiver { + public void onReceive(Context context, Intent intent) { + final Intent service = new Intent(context.getApplicationContext(), AudioFxService.class); + context.startService(service); + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/service/DevicePreferenceManager.java b/src/org/cyanogenmod/audiofx/audiofx/service/DevicePreferenceManager.java new file mode 100644 index 0000000..928209e --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/service/DevicePreferenceManager.java @@ -0,0 +1,294 @@ +package com.cyngn.audiofx.service; + +import static com.cyngn.audiofx.Constants.AUDIOFX_GLOBAL_FILE; +import static com.cyngn.audiofx.Constants.AUDIOFX_GLOBAL_HAS_BASSBOOST; +import static com.cyngn.audiofx.Constants.AUDIOFX_GLOBAL_HAS_DTS; +import static com.cyngn.audiofx.Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO; +import static com.cyngn.audiofx.Constants.AUDIOFX_GLOBAL_HAS_VIRTUALIZER; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_BASS_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_BASS_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_EQ_PRESET; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_TREBLE_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_TREBLE_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_VIRTUALIZER_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_HEADSET; +import static com.cyngn.audiofx.Constants.DEVICE_SPEAKER; +import static com.cyngn.audiofx.Constants.EQUALIZER_BAND_LEVEL_RANGE; +import static com.cyngn.audiofx.Constants.EQUALIZER_CENTER_FREQS; +import static com.cyngn.audiofx.Constants.EQUALIZER_NUMBER_OF_BANDS; +import static com.cyngn.audiofx.Constants.EQUALIZER_NUMBER_OF_PRESETS; +import static com.cyngn.audiofx.Constants.EQUALIZER_PRESET; +import static com.cyngn.audiofx.Constants.EQUALIZER_PRESET_NAMES; +import static com.cyngn.audiofx.Constants.SAVED_DEFAULTS; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.media.AudioDeviceInfo; +import android.text.TextUtils; +import android.util.Log; + +import com.cyngn.audiofx.Constants; +import com.cyngn.audiofx.R; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.backends.EffectSet; +import com.cyngn.audiofx.backends.EffectsFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +class DevicePreferenceManager implements AudioOutputChangeListener.AudioOutputChangedCallback { + + private static final String TAG = AudioFxService.TAG; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final Context mContext; + + private AudioDeviceInfo mCurrentDevice; + + public DevicePreferenceManager(Context context, AudioDeviceInfo device) { + mContext = context; + mCurrentDevice = device; + } + + public boolean initDefaults() { + try { + saveAndApplyDefaults(false); + } catch (Exception e) { + SharedPreferences prefs = Constants.getGlobalPrefs(mContext); + prefs.edit().clear().commit(); + Log.e(TAG, "Failed to initialize defaults!", e); + return false; + } + return true; + } + + @Override + public void onAudioOutputChanged(boolean firstChange, AudioDeviceInfo outputDevice) { + mCurrentDevice = outputDevice; + } + + public SharedPreferences getCurrentDevicePrefs() { + return mContext.getSharedPreferences( + MasterConfigControl.getDeviceIdentifierString(mCurrentDevice), 0); + } + + public SharedPreferences prefsFor(final String name) { + return mContext.getSharedPreferences(name, 0); + } + + private boolean hasPrefs(final String name) { + return mContext.getSharedPrefsFile(name).exists(); + } + + public boolean isGlobalEnabled() { + return getCurrentDevicePrefs().getBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, false); + } + + /** + * This method sets some sane defaults for presets, device defaults, etc + * <p/> + * First we read presets from the system, then adjusts some setting values + * for some better defaults! + */ + private void saveAndApplyDefaults(boolean overridePrevious) { + if (DEBUG) { + Log.d(TAG, "saveAndApplyDefaults() called with overridePrevious = " + + "[" + overridePrevious + "]"); + } + SharedPreferences prefs = Constants.getGlobalPrefs(mContext); + + final int currentPrefVer = prefs.getInt(Constants.AUDIOFX_GLOBAL_PREFS_VERSION_INT, 0); + boolean needsPrefsUpdate = currentPrefVer < Constants.CURRENT_PREFS_INT_VERSION + || overridePrevious; + + if (needsPrefsUpdate) { + Log.d(TAG, "rebuilding presets due to preference upgrade from " + currentPrefVer + + " to " + Constants.CURRENT_PREFS_INT_VERSION); + } + + if (prefs.getBoolean(SAVED_DEFAULTS, false) && !needsPrefsUpdate) { + if (DEBUG) { + Log.e(TAG, "we've already saved defaults and don't need a pref update. aborting."); + } + return; + } + EffectSet temp = new EffectsFactory().createEffectSet(mContext, 0, null); + + final int numBands = temp.getNumEqualizerBands(); + final int numPresets = temp.getNumEqualizerPresets(); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(EQUALIZER_NUMBER_OF_PRESETS, String.valueOf(numPresets)); + editor.putString(EQUALIZER_NUMBER_OF_BANDS, String.valueOf(numBands)); + + // range + short[] rangeShortArr = temp.getEqualizerBandLevelRange(); + editor.putString(EQUALIZER_BAND_LEVEL_RANGE, rangeShortArr[0] + + ";" + rangeShortArr[1]); + + // center freqs + StringBuilder centerFreqs = new StringBuilder(); + // audiofx.global.centerfreqs + for (short i = 0; i < numBands; i++) { + centerFreqs.append(temp.getCenterFrequency(i)); + centerFreqs.append(";"); + + } + centerFreqs.deleteCharAt(centerFreqs.length() - 1); + editor.putString(EQUALIZER_CENTER_FREQS, centerFreqs.toString()); + + // populate preset names + StringBuilder presetNames = new StringBuilder(); + for (int i = 0; i < numPresets; i++) { + String presetName = temp.getEqualizerPresetName((short) i); + presetNames.append(presetName); + presetNames.append("|"); + + // populate preset band values + StringBuilder presetBands = new StringBuilder(); + temp.useEqualizerPreset((short) i); + + for (int j = 0; j < numBands; j++) { + // loop through preset bands + presetBands.append(temp.getEqualizerBandLevel((short) j)); + presetBands.append(";"); + } + presetBands.deleteCharAt(presetBands.length() - 1); + editor.putString(EQUALIZER_PRESET + i, presetBands.toString()); + } + if (presetNames.length() > 0) { + presetNames.deleteCharAt(presetNames.length() - 1); + } + editor.putString(EQUALIZER_PRESET_NAMES, presetNames.toString()); + + editor.putBoolean(AUDIOFX_GLOBAL_HAS_VIRTUALIZER, temp.hasVirtualizer()); + editor.putBoolean(AUDIOFX_GLOBAL_HAS_BASSBOOST, temp.hasBassBoost()); + editor.putBoolean(AUDIOFX_GLOBAL_HAS_MAXXAUDIO, temp.getBrand() == Constants.EFFECT_TYPE_MAXXAUDIO); + editor.putBoolean(AUDIOFX_GLOBAL_HAS_DTS, temp.getBrand() == Constants.EFFECT_TYPE_DTS); + editor.commit(); + temp.release(); + + applyDefaults(needsPrefsUpdate); + + prefs + .edit() + .putInt(Constants.AUDIOFX_GLOBAL_PREFS_VERSION_INT, + Constants.CURRENT_PREFS_INT_VERSION) + .putBoolean(Constants.SAVED_DEFAULTS, true) + .commit(); + } + + private static int findInList(String needle, List<String> haystack) { + for (int i = 0; i < haystack.size(); i++) { + if (haystack.get(i).equalsIgnoreCase(needle)) { + return i; + } + } + return -1; + } + + /** + * This method sets up some *persisted* defaults. + * Prereq: saveDefaults() must have been run before this can apply its defaults properly. + */ + private void applyDefaults(boolean overridePrevious) { + if (DEBUG) { + Log.d(TAG, "applyDefaults() called with overridePrevious = [" + overridePrevious + "]"); + } + + if (!(overridePrevious || !hasPrefs(DEVICE_SPEAKER) || + !hasPrefs(AUDIOFX_GLOBAL_FILE))) { + return; + } + + final SharedPreferences globalPrefs = Constants.getGlobalPrefs(mContext); + + // Nothing to see here for EFFECT_TYPE_DTS + if (globalPrefs.getBoolean(AUDIOFX_GLOBAL_HAS_DTS, false)) { + return; + } + + // set up the builtin speaker configuration + final String smallSpeakers = getNonLocalizedString(R.string.small_speakers); + final List<String> presetNames = new ArrayList<String>(Arrays.asList( + globalPrefs.getString(EQUALIZER_PRESET_NAMES, "").split("\\|"))); + final SharedPreferences speakerPrefs = prefsFor(DEVICE_SPEAKER); + + if (globalPrefs.getBoolean(AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false)) { + // MaxxAudio defaults for builtin speaker: + // maxxvolume: on maxxbass: 40% maxxtreble: 32% + speakerPrefs.edit() + .putBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, true) + .putBoolean(DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, true) + .putBoolean(DEVICE_AUDIOFX_BASS_ENABLE, true) + .putString(DEVICE_AUDIOFX_BASS_STRENGTH, "400") + .putBoolean(DEVICE_AUDIOFX_TREBLE_ENABLE, true) + .putString(DEVICE_AUDIOFX_TREBLE_STRENGTH, "32") + .commit(); + + // Defaults for headphones + // maxxvolume: on maxxbass: 20% maxxtreble: 40% maxxspace: 20% + prefsFor(DEVICE_HEADSET).edit() + .putBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, true) + .putBoolean(DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, true) + .putBoolean(DEVICE_AUDIOFX_BASS_ENABLE, true) + .putString(DEVICE_AUDIOFX_BASS_STRENGTH, "200") + .putBoolean(DEVICE_AUDIOFX_TREBLE_ENABLE, true) + .putString(DEVICE_AUDIOFX_TREBLE_STRENGTH, "40") + .putBoolean(DEVICE_AUDIOFX_VIRTUALIZER_ENABLE, true) + .putString(DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH, "200") + .commit(); + } else { + // Defaults for headphones + // bass boost: 15% virtualizer: 20% preset: FLAT + int flat = findInList(getNonLocalizedString(R.string.flat), presetNames); + prefsFor(DEVICE_HEADSET).edit() + .putBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, true) + .putBoolean(DEVICE_AUDIOFX_BASS_ENABLE, true) + .putString(DEVICE_AUDIOFX_BASS_STRENGTH, "150") + .putBoolean(DEVICE_AUDIOFX_VIRTUALIZER_ENABLE, true) + .putString(DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH, "200") + .putString(DEVICE_AUDIOFX_EQ_PRESET, (flat >= 0 ? String.valueOf(flat) : "0")) + .commit(); + } + + // for 5 band configs, let's add a `Small Speaker` configuration if one + // doesn't exist ( from oss AudioFX: -170;270;50;-220;200 ) + if (Integer.parseInt(globalPrefs.getString(EQUALIZER_NUMBER_OF_BANDS, "0")) == 5 && + findInList(smallSpeakers, presetNames) < 0) { + + int currentPresets = Integer.parseInt( + globalPrefs.getString(EQUALIZER_NUMBER_OF_PRESETS, "0")); + + presetNames.add(smallSpeakers); + String newPresetNames = TextUtils.join("|", presetNames); + globalPrefs.edit() + .putString(EQUALIZER_PRESET + currentPresets, "-170;270;50;-220;200") + .putString(EQUALIZER_PRESET_NAMES, newPresetNames) + .putString(EQUALIZER_NUMBER_OF_PRESETS, Integer.toString(++currentPresets)) + .commit(); + + } + + // set the small speakers preset as the default + int idx = findInList(smallSpeakers, presetNames); + if (idx >= 0) { + speakerPrefs.edit() + .putBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, true) + .putString(DEVICE_AUDIOFX_EQ_PRESET, String.valueOf(idx)) + .commit(); + } + } + + private String getNonLocalizedString(int res) { + Configuration config = new Configuration(mContext.getResources().getConfiguration()); + config.setLocale(Locale.ROOT); + return mContext.createConfigurationContext(config).getString(res); + } +} + diff --git a/src/org/cyanogenmod/audiofx/audiofx/service/SessionManager.java b/src/org/cyanogenmod/audiofx/audiofx/service/SessionManager.java new file mode 100644 index 0000000..c744686 --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/service/SessionManager.java @@ -0,0 +1,422 @@ +package com.cyngn.audiofx.service; + +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_BASS_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_BASS_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_EQ_PRESET_LEVELS; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_GLOBAL_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_REVERB_PRESET; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_TREBLE_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_TREBLE_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_VIRTUALIZER_ENABLE; +import static com.cyngn.audiofx.Constants.DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH; +import static com.cyngn.audiofx.Constants.DEVICE_DEFAULT_GLOBAL_ENABLE; +import static com.cyngn.audiofx.activity.MasterConfigControl.getDeviceIdentifierString; +import static com.cyngn.audiofx.service.AudioFxService.ALL_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.BASS_BOOST_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.ENABLE_REVERB; +import static com.cyngn.audiofx.service.AudioFxService.EQ_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.REVERB_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.TREBLE_BOOST_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.VIRTUALIZER_CHANGED; +import static com.cyngn.audiofx.service.AudioFxService.VOLUME_BOOST_CHANGED; + +import android.content.Context; +import android.content.SharedPreferences; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.media.audiofx.PresetReverb; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.SparseArray; + +import com.cyngn.audiofx.backends.EffectSet; +import com.cyngn.audiofx.backends.EffectsFactory; +import com.cyngn.audiofx.eq.EqUtils; + +import cyanogenmod.media.AudioSessionInfo; +import cyanogenmod.media.CMAudioManager; + +class SessionManager implements AudioOutputChangeListener.AudioOutputChangedCallback { + + private static final String TAG = AudioFxService.TAG; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final Context mContext; + private final Handler mHandler; + private final DevicePreferenceManager mDevicePrefs; + private final CMAudioManager mCMAudio; + + /** + * All fields ending with L should be locked on {@link #mAudioSessionsL} + */ + private final SparseArray<EffectSet> mAudioSessionsL = new SparseArray<EffectSet>(); + + + private AudioDeviceInfo mCurrentDevice = null; + + // audio priority handler messages + private static final int MSG_UPDATE_DSP = 100; + private static final int MSG_ADD_SESSION = 101; + private static final int MSG_REMOVE_SESSION = 102; + private static final int MSG_UPDATE_FOR_SESSION = 103; + private static final int MSG_UPDATE_EQ_OVERRIDE = 104; + + public SessionManager(Context context, Handler handler, DevicePreferenceManager devicePrefs, + AudioDeviceInfo outputDevice) { + mContext = context; + mCMAudio = CMAudioManager.getInstance(context); + mDevicePrefs = devicePrefs; + mCurrentDevice = outputDevice; + mHandler = new Handler(handler.getLooper(), new AudioServiceHandler()); + } + + public void onDestroy() { + synchronized (mAudioSessionsL) { + mHandler.removeCallbacksAndMessages(null); + mHandler.getLooper().quit(); + } + } + + public void update(int flags) { + if (mHandler == null) { + return; + } + synchronized (mAudioSessionsL) { + mHandler.obtainMessage(MSG_UPDATE_DSP, flags, 0).sendToTarget(); + } + } + + public void setOverrideLevels(short band, float level) { + synchronized (mAudioSessionsL) { + mHandler.obtainMessage(MSG_UPDATE_EQ_OVERRIDE, band, 0, level).sendToTarget(); + } + } + + /** + * Callback which listens for session updates from AudioPolicyManager. This is a + * feature added by CM which notifies when sessions are created or + * destroyed on a particular stream. This is independent of the standard control + * intents and should not conflict with them. This feature may not be available on + * all devices. + * + * Default logic is to do our best to only attach to music streams. We never attach + * to low-latency streams automatically, and we don't attach to mono streams by default + * either since these are usually notifications/ringtones/etc. + */ + public boolean shouldHandleSession(AudioSessionInfo info) { + final boolean music = info.getStream() == AudioManager.STREAM_MUSIC; + final boolean offloaded = (info.getFlags() < 0) + || (info.getFlags() & AudioFxService.AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) > 0 + || (info.getFlags() & AudioFxService.AUDIO_OUTPUT_FLAG_DEEP_BUFFER) > 0; + final boolean stereo = info.getChannelMask() < 0 || info.getChannelMask() > 1; + + return music && offloaded && stereo && info.getSessionId() > 0; + } + + public void addSession(AudioSessionInfo info) { + synchronized (mAudioSessionsL) { + // Never auto-attach is someone is recording! We don't want to interfere + // with any sort of loopback mechanisms. + final boolean recording = AudioSystem.isSourceActive(0) || AudioSystem.isSourceActive(6); + if (recording) { + Log.w(TAG, "Recording in progress, not performing auto-attach!"); + return; + } + if (shouldHandleSession(info) && + !mHandler.hasMessages(MSG_ADD_SESSION, info.getSessionId())) { + mHandler.removeMessages(MSG_REMOVE_SESSION, info.getSessionId()); + mHandler.obtainMessage(MSG_ADD_SESSION, info.getSessionId()).sendToTarget(); + if (DEBUG) Log.i(TAG, "New audio session: " + info.toString()); + } + } + } + + public void removeSession(AudioSessionInfo info) { + synchronized (mAudioSessionsL) { + if (shouldHandleSession(info) && + !mHandler.hasMessages(MSG_REMOVE_SESSION, info.getSessionId())) { + int sid = info.getSessionId(); + final EffectSet effects = mAudioSessionsL.get(sid); + if (effects != null) { + effects.setMarkedForDeath(true); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_REMOVE_SESSION, sid), + effects.getReleaseDelay()); + if (DEBUG) Log.i(TAG, "Audio session queued for removal: " + info.toString()); + } + } + } + } + + public String getCurrentDeviceIdentifier() { + return getDeviceIdentifierString(mCurrentDevice); + } + + public boolean hasActiveSessions() { + synchronized (mAudioSessionsL) { + return mAudioSessionsL.size() > 0; + } + } + + EffectSet getEffectForSession(int sessionId) { + synchronized (mAudioSessionsL) { + return mAudioSessionsL.get(sessionId); + } + } + + /** + * Update the backend with our changed preferences. + * + * This must only be called from the HandlerThread! + */ + private void updateBackendLocked(int flags, EffectSet session) { + if (Looper.getMainLooper().equals(Looper.myLooper())) { + throw new IllegalStateException("updateBackend must not be called on the UI thread!"); + } + + final SharedPreferences prefs = mDevicePrefs.getCurrentDevicePrefs(); + + if (DEBUG) { + Log.i(TAG, "+++ updateBackend() called with flags=[" + flags + "], session=[" + session + "]"); + } + + if (session == null) { + return; + } + + final boolean globalEnabled = prefs.getBoolean(DEVICE_AUDIOFX_GLOBAL_ENABLE, + DEVICE_DEFAULT_GLOBAL_ENABLE); + + if ((flags & ALL_CHANGED) > 0) { + // global bypass toggle + session.setGlobalEnabled(globalEnabled); + } + + if (globalEnabled) { + // tell the backend it's time to party + if (!session.beginUpdate()) { + Log.e(TAG, "session " + session + " failed to beginUpdate()"); + return; + } + + // equalizer + try { + if ((flags & EQ_CHANGED) > 0) { + // equalizer is always on unless bypassed + session.enableEqualizer(true); + String savedPreset = prefs.getString(DEVICE_AUDIOFX_EQ_PRESET_LEVELS, null); + if (savedPreset != null) { + session.setEqualizerLevelsDecibels(EqUtils.stringBandsToFloats(savedPreset)); + } + } + } catch (Exception e) { + Log.e(TAG, "Error enabling equalizer!", e); + } + + // bass + try { + if ((flags & BASS_BOOST_CHANGED) > 0 && session.hasBassBoost()) { + boolean enable = prefs.getBoolean(DEVICE_AUDIOFX_BASS_ENABLE, false); + session.enableBassBoost(enable); + session.setBassBoostStrength(Short.valueOf(prefs + .getString(DEVICE_AUDIOFX_BASS_STRENGTH, "0"))); + } + } catch (Exception e) { + Log.e(TAG, "Error enabling bass boost!", e); + } + + // reverb + if (ENABLE_REVERB) { + try { + if ((flags & REVERB_CHANGED) > 0 && session.hasReverb()) { + short preset = Short.decode(prefs.getString(DEVICE_AUDIOFX_REVERB_PRESET, + String.valueOf(PresetReverb.PRESET_NONE))); + session.enableReverb(preset > 0); + session.setReverbPreset(preset); + } + } catch (Exception e) { + Log.e(TAG, "Error enabling reverb preset", e); + } + } + + // virtualizer + try { + if ((flags & VIRTUALIZER_CHANGED) > 0 && session.hasVirtualizer()) { + boolean enable = prefs.getBoolean(DEVICE_AUDIOFX_VIRTUALIZER_ENABLE, false); + session.enableVirtualizer(enable); + session.setVirtualizerStrength(Short.valueOf(prefs.getString( + DEVICE_AUDIOFX_VIRTUALIZER_STRENGTH, "0"))); + } + } catch (Exception e) { + Log.e(TAG, "Error enabling virtualizer!"); + } + + // extended audio effects + try { + if ((flags & TREBLE_BOOST_CHANGED) > 0 && session.hasTrebleBoost()) { + // treble + boolean enable = prefs.getBoolean(DEVICE_AUDIOFX_TREBLE_ENABLE, false); + session.enableTrebleBoost(enable); + session.setTrebleBoostStrength(Short.valueOf( + prefs.getString(DEVICE_AUDIOFX_TREBLE_STRENGTH, "0"))); + } + } catch (Exception e) { + Log.e(TAG, "Error enabling treble boost!", e); + } + + try { + if ((flags & VOLUME_BOOST_CHANGED) > 0 && session.hasVolumeBoost()) { + // maxx volume + session.enableVolumeBoost(prefs.getBoolean(DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, false)); + } + } catch (Exception e) { + Log.e(TAG, "Error enabling volume boost!", e); + } + + // mic drop + if (!session.commitUpdate()) { + Log.e(TAG, "session " + session + " failed to commitUpdate()"); + } + } + if (DEBUG) { + Log.i(TAG, "--- updateBackend() called with flags=[" + flags + "], session=[" + session + "]"); + } + } + + private class AudioServiceHandler implements Handler.Callback { + + @Override + public boolean handleMessage(Message msg) { + synchronized (mAudioSessionsL) { + EffectSet session = null; + Integer sessionId = 0; + int flags = 0; + + switch (msg.what) { + case MSG_ADD_SESSION: + /** + * msg.obj = sessionId + */ + sessionId = (Integer) msg.obj; + if (sessionId == null || sessionId <= 0) { + break; + } + + session = mAudioSessionsL.get(sessionId); + if (session == null) { + try { + session = new EffectsFactory() + .createEffectSet(mContext, sessionId, mCurrentDevice); + } catch (Exception e) { + Log.e(TAG, "couldn't create effects for session id: " + sessionId, e); + break; + } + mAudioSessionsL.put(sessionId, session); + if (DEBUG) Log.w(TAG, "added new EffectSet for sessionId=" + sessionId); + updateBackendLocked(ALL_CHANGED, session); + } else { + session.setMarkedForDeath(false); + } + break; + + case MSG_REMOVE_SESSION: + /** + * msg.obj = sessionId + */ + sessionId = (Integer) msg.obj; + if (sessionId == null || sessionId <= 0) { + break; + } + + session = mAudioSessionsL.get(sessionId); + if (session != null && session.isMarkedForDeath()) { + mHandler.removeMessages(MSG_UPDATE_FOR_SESSION, sessionId); + session.release(); + mAudioSessionsL.remove(sessionId); + if (DEBUG) Log.w(TAG, "removed and released sessionId=" + sessionId); + } + + break; + + case MSG_UPDATE_DSP: + /** + * msg.arg1 = update what flags + */ + flags = msg.arg1; + + final String mode = getCurrentDeviceIdentifier(); + if (DEBUG) Log.i(TAG, "Updating to configuration: " + mode); + + final int N = mAudioSessionsL.size(); + for (int i = 0; i < N; i++) { + sessionId = mAudioSessionsL.keyAt(i); + mHandler.obtainMessage(MSG_UPDATE_FOR_SESSION, flags, 0, sessionId).sendToTarget(); + } + break; + + case MSG_UPDATE_FOR_SESSION: + /** + * msg.arg1 = update what flags + * msg.arg2 = unused + * msg.obj = session id integer (for consistency) + */ + sessionId = (Integer) msg.obj; + flags = msg.arg1; + + if (sessionId == null || sessionId <= 0) { + break; + } + + String device = getCurrentDeviceIdentifier(); + if (DEBUG) Log.i(TAG, "updating DSP for sessionId=" + sessionId + + ", device=" + device + " flags=" + flags); + + session = mAudioSessionsL.get(sessionId); + if (session != null) { + updateBackendLocked(flags, session); + } + break; + + case MSG_UPDATE_EQ_OVERRIDE: + for (int i = 0; i < mAudioSessionsL.size(); i++) { + sessionId = mAudioSessionsL.keyAt(i); + session = mAudioSessionsL.get(sessionId); + if (session != null) { + session.setEqualizerBandLevel((short) msg.arg1, (float) msg.obj); + } + } + break; + } + return true; + } + } + } + + /** + * Updates the backend and notifies the frontend when the output device has changed + */ + @Override + public void onAudioOutputChanged(boolean firstChange, AudioDeviceInfo outputDevice) { + synchronized (mAudioSessionsL) { + if (mCurrentDevice == null || + (outputDevice != null && mCurrentDevice.getId() != outputDevice.getId())) { + mCurrentDevice = outputDevice; + } + + EffectSet session = null; + + // Update all the sessions for this output which are moving + final int N = mAudioSessionsL.size(); + for (int i = 0; i < N; i++) { + session = mAudioSessionsL.valueAt(i); + + session.setDevice(mCurrentDevice); + updateBackendLocked(ALL_CHANGED, session); + } + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/stats/AppState.java b/src/org/cyanogenmod/audiofx/audiofx/stats/AppState.java new file mode 100644 index 0000000..5417bcd --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/stats/AppState.java @@ -0,0 +1,41 @@ +package com.cyngn.audiofx.stats; + +import com.cyanogen.ambient.analytics.Event; +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.activity.MasterConfigControl; +import com.cyngn.audiofx.eq.EqUtils; +import com.cyngn.audiofx.knobs.KnobCommander; + +/** + * Created by roman on 9/29/15. + */ +public class AppState { + public static void appendState(MasterConfigControl control, + KnobCommander knobs, Event.Builder builder) { + // what's the current output device? + builder.addField("state_current_device", control.getCurrentDeviceIdentifier()); + + // what preset? if custom, what name/values? + builder.addField("state_preset_name", control.getEqualizerManager().getCurrentPreset().getName()); + + if (control.getEqualizerManager().getCurrentPreset() instanceof Preset.CustomPreset) { + builder.addField("state_custom_preset_values", + EqUtils.floatLevelsToString(control.getEqualizerManager().getCurrentPreset().getLevels())); + } + + // knob states + if (control.hasMaxxAudio()) { + builder.addField("state_maxx_volume", control.getMaxxVolumeEnabled()); + } + + if (knobs.hasBassBoost()) { + builder.addField("state_knob_bass", knobs.getBassStrength()); + } + if (knobs.hasTreble()) { + builder.addField("state_knob_treble", knobs.getTrebleStrength()); + } + if (knobs.hasVirtualizer()) { + builder.addField("state_knob_virtualizer", knobs.getVirtualizerStrength()); + } + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/stats/UserSession.java b/src/org/cyanogenmod/audiofx/audiofx/stats/UserSession.java new file mode 100644 index 0000000..bb8787c --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/stats/UserSession.java @@ -0,0 +1,202 @@ +package com.cyngn.audiofx.stats; + +import android.os.Parcel; +import android.os.Parcelable; +import com.cyanogen.ambient.analytics.Event; +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.knobs.KnobCommander; +import org.json.JSONException; +import org.json.JSONObject; + +public class UserSession implements Parcelable { + + private static final String SOURCE_NONE = "none"; + + private static UserSession sSession; + public static final UserSession getInstance() { + return sSession; + } + + private String mSource; + private int mDevicesChanged; + private int mEnabledDisabledToggles; + private int mPresetsSelected; + private int mPresetsCreated; + private int mPresetsRemoved; + private int mPresetsRenamed; + private int mMaxxVolumeToggled; + private int mTrebleKnobAdjusted; + private int mBassKnobAdjusted; + private int mVirtualizerKnobAdjusted; + + public UserSession(String incomingPackageSource) { + if (incomingPackageSource == null) { + mSource = SOURCE_NONE; + } else { + mSource = incomingPackageSource; + } + sSession = this; + } + + public void deviceChanged() { + mDevicesChanged++; + } + + public void deviceEnabledDisabled() { + mEnabledDisabledToggles++; + } + + public void presetSelected() { + mPresetsSelected++; + } + + public void presetRemoved() { + mPresetsRemoved++; + } + + public void presetRenamed() { + mPresetsRenamed++; + } + + public void presetCreated() { + mPresetsCreated++; + } + + public void maxxVolumeToggled() { + mMaxxVolumeToggled++; + } + + public void knobOptionsAdjusted(int knob) { + switch (knob) { + case KnobCommander.KNOB_BASS: + mBassKnobAdjusted++; + break; + case KnobCommander.KNOB_TREBLE: + mTrebleKnobAdjusted++; + break; + case KnobCommander.KNOB_VIRTUALIZER: + mVirtualizerKnobAdjusted++; + break; + } + } + + public void append(Event.Builder builder) { + builder.addField("session_source", mSource); + if (mDevicesChanged > 0) + builder.addField("session_devices_changed_count", mDevicesChanged); + if (mEnabledDisabledToggles > 0) + builder.addField("session_devices_enabled_disabled_count", mEnabledDisabledToggles); + if (mPresetsSelected > 0) + builder.addField("session_presets_changed_count", mPresetsSelected); + if (mPresetsCreated > 0) + builder.addField("session_presets_created_count", mPresetsCreated); + if (mPresetsRemoved > 0) + builder.addField("session_presets_removed_count", mPresetsRemoved); + if (mPresetsRenamed > 0) + builder.addField("session_presets_renamed_count", mPresetsRenamed); + if (mMaxxVolumeToggled > 0) + builder.addField("session_maxx_volume_toggled", mMaxxVolumeToggled); + if (mBassKnobAdjusted > 0) + builder.addField("session_knobs_bass_adjusted_count", mBassKnobAdjusted); + if (mVirtualizerKnobAdjusted > 0) + builder.addField("session_knobs_virtualizer_adjusted_count", mVirtualizerKnobAdjusted); + if (mTrebleKnobAdjusted > 0) + builder.addField("session_knobs_treble_adjusted_count", mTrebleKnobAdjusted); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(getClass().getName() + "["); + if (mSource != null) { + s.append("mSource=").append(mSource).append(", "); + } + if (mDevicesChanged > 0) { + s.append("mDevicesChanged=").append(mDevicesChanged).append(", "); + } + if (mEnabledDisabledToggles > 0) { + s.append("mEnabledDisabledToggles=").append(mEnabledDisabledToggles).append(", "); + } + if (mPresetsSelected > 0) { + s.append("mPresetsSelected=").append(mPresetsSelected).append(", "); + } + if (mPresetsCreated > 0) { + s.append("mPresetsCreated=").append(mPresetsCreated).append(", "); + } + if (mPresetsRemoved > 0) { + s.append("mPresetsRemoved=").append(mPresetsRemoved).append(", "); + } + if (mPresetsRenamed > 0) { + s.append("mPresetsRenamed=").append(mPresetsRenamed).append(", "); + } + if (mMaxxVolumeToggled > 0) { + s.append("mMaxxVolumeToggled=").append(mMaxxVolumeToggled).append(", "); + } + if (mBassKnobAdjusted > 0) { + s.append("mBassKnobAdjusted=").append(mBassKnobAdjusted).append(", "); + } + if (mVirtualizerKnobAdjusted > 0) { + s.append("mVirtualizerKnobAdjusted=").append(mVirtualizerKnobAdjusted).append(", "); + } + if (mTrebleKnobAdjusted > 0) { + s.append("mTrebleKnobAdjusted=").append(mTrebleKnobAdjusted).append(", "); + } + if (s.charAt(s.length() - 2) == ',') { + s.delete(s.length() - 2, s.length()); + } + s.append("]"); + + return s.toString(); + } + + public static final Creator<UserSession> CREATOR = new Creator<UserSession>() { + @Override + public UserSession createFromParcel(Parcel in) { + return new UserSession(in); + } + + @Override + public UserSession[] newArray(int size) { + return new UserSession[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + protected UserSession(Parcel in) { + mSource = in.readString(); + mDevicesChanged = in.readInt(); + mEnabledDisabledToggles = in.readInt(); + mPresetsSelected = in.readInt(); + mPresetsCreated = in.readInt(); + mPresetsRemoved = in.readInt(); + mPresetsRenamed = in.readInt(); + mBassKnobAdjusted = in.readInt(); + mVirtualizerKnobAdjusted = in.readInt(); + mTrebleKnobAdjusted = in.readInt(); + mMaxxVolumeToggled = in.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mSource); + dest.writeInt(mDevicesChanged); + dest.writeInt(mEnabledDisabledToggles); + dest.writeInt(mPresetsSelected); + dest.writeInt(mPresetsCreated); + dest.writeInt(mPresetsRemoved); + dest.writeInt(mPresetsRenamed); + dest.writeInt(mBassKnobAdjusted); + dest.writeInt(mVirtualizerKnobAdjusted); + dest.writeInt(mTrebleKnobAdjusted); + dest.writeInt(mMaxxVolumeToggled); + } + + private static class State { + private String mOutputDevice; + private Preset mPreset; + private String mKnobsOpts; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/CirclePageIndicator.java b/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/CirclePageIndicator.java new file mode 100644 index 0000000..b4b4a6b --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/CirclePageIndicator.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2011 Patrik Akerfeldt + * Copyright (C) 2011 Jake Wharton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.cyngn.audiofx.viewpagerindicator; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.drawable.Drawable; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.ViewConfigurationCompat; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import com.cyngn.audiofx.R; + +import static android.graphics.Paint.ANTI_ALIAS_FLAG; +import static android.widget.LinearLayout.HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; + +/** + * Draws circles (one for each view). The current view position is filled and + * others are only stroked. + */ +public class CirclePageIndicator extends View implements PageIndicator { + private static final int INVALID_POINTER = -1; + + private float mRadius; + private final Paint mPaintPageFill = new Paint(ANTI_ALIAS_FLAG); + private final Paint mPaintStroke = new Paint(ANTI_ALIAS_FLAG); + private final Paint mPaintFill = new Paint(ANTI_ALIAS_FLAG); + private ViewPager mViewPager; + private ViewPager.OnPageChangeListener mListener; + private int mCurrentPage; + private int mSnapPage; + private float mPageOffset; + private int mScrollState; + private int mOrientation; + private boolean mCentered; + private boolean mSnap; + + private int mTouchSlop; + private float mLastMotionX = -1; + private int mActivePointerId = INVALID_POINTER; + private boolean mIsDragging; + + + public CirclePageIndicator(Context context) { + this(context, null); + } + + public CirclePageIndicator(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.vpiCirclePageIndicatorStyle); + } + + public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + if (isInEditMode()) return; + + //Load defaults from resources + final Resources res = getResources(); + final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color); + final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color); + final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation); + final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color); + final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width); + final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius); + final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered); + final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap); + + //Retrieve styles attributes + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, 0); + + mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered); + mOrientation = a.getInt(R.styleable.CirclePageIndicator_android_orientation, defaultOrientation); + mPaintPageFill.setStyle(Style.FILL); + mPaintPageFill.setColor(a.getColor(R.styleable.CirclePageIndicator_pageColor, defaultPageColor)); + mPaintStroke.setStyle(Style.STROKE); + mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor)); + mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth)); + mPaintFill.setStyle(Style.FILL); + mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor)); + mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius); + mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap); + + Drawable background = a.getDrawable(R.styleable.CirclePageIndicator_android_background); + if (background != null) { + setBackgroundDrawable(background); + } + + a.recycle(); + + final ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); + } + + + public void setCentered(boolean centered) { + mCentered = centered; + invalidate(); + } + + public boolean isCentered() { + return mCentered; + } + + public void setPageColor(int pageColor) { + mPaintPageFill.setColor(pageColor); + invalidate(); + } + + public int getPageColor() { + return mPaintPageFill.getColor(); + } + + public void setFillColor(int fillColor) { + mPaintFill.setColor(fillColor); + invalidate(); + } + + public int getFillColor() { + return mPaintFill.getColor(); + } + + public void setOrientation(int orientation) { + switch (orientation) { + case HORIZONTAL: + case VERTICAL: + mOrientation = orientation; + requestLayout(); + break; + + default: + throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL."); + } + } + + public int getOrientation() { + return mOrientation; + } + + public void setStrokeColor(int strokeColor) { + mPaintStroke.setColor(strokeColor); + invalidate(); + } + + public int getStrokeColor() { + return mPaintStroke.getColor(); + } + + public void setStrokeWidth(float strokeWidth) { + mPaintStroke.setStrokeWidth(strokeWidth); + invalidate(); + } + + public float getStrokeWidth() { + return mPaintStroke.getStrokeWidth(); + } + + public void setRadius(float radius) { + mRadius = radius; + invalidate(); + } + + public float getRadius() { + return mRadius; + } + + public void setSnap(boolean snap) { + mSnap = snap; + invalidate(); + } + + public boolean isSnap() { + return mSnap; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mViewPager == null) { + return; + } + final int count = mViewPager.getAdapter().getCount(); + if (count == 0) { + return; + } + + if (mCurrentPage >= count) { + setCurrentItem(count - 1); + return; + } + + int longSize; + int longPaddingBefore; + int longPaddingAfter; + int shortPaddingBefore; + if (mOrientation == HORIZONTAL) { + longSize = getWidth(); + longPaddingBefore = getPaddingLeft(); + longPaddingAfter = getPaddingRight(); + shortPaddingBefore = getPaddingTop(); + } else { + longSize = getHeight(); + longPaddingBefore = getPaddingTop(); + longPaddingAfter = getPaddingBottom(); + shortPaddingBefore = getPaddingLeft(); + } + + final float threeRadius = mRadius * 3; + final float shortOffset = shortPaddingBefore + mRadius; + float longOffset = longPaddingBefore + mRadius; + if (mCentered) { + longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f); + } + + float dX; + float dY; + + float pageFillRadius = mRadius; + if (mPaintStroke.getStrokeWidth() > 0) { + pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f; + } + + //Draw stroked circles + for (int iLoop = 0; iLoop < count; iLoop++) { + float drawLong = longOffset + (iLoop * threeRadius); + if (mOrientation == HORIZONTAL) { + dX = drawLong; + dY = shortOffset; + } else { + dX = shortOffset; + dY = drawLong; + } + // Only paint fill if not completely transparent + if (mPaintPageFill.getAlpha() > 0) { + canvas.drawCircle(dX, dY, (float) (pageFillRadius/1.5f), mPaintPageFill); + } + + // Only paint stroke if a stroke width was non-zero + if (pageFillRadius != mRadius) { + canvas.drawCircle(dX, dY, mRadius, mPaintStroke); + } + } + + //Draw the filled circle according to the current scroll + float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; + if (!mSnap) { + cx += mPageOffset * threeRadius; + } + if (mOrientation == HORIZONTAL) { + dX = longOffset + cx; + dY = shortOffset; + } else { + dX = shortOffset; + dY = longOffset + cx; + } + canvas.drawCircle(dX, dY, mRadius, mPaintFill); + } + + public boolean onTouchEvent(android.view.MotionEvent ev) { + if (super.onTouchEvent(ev)) { + return true; + } + if ((mViewPager == null) || (mViewPager.getAdapter().getCount() == 0)) { + return false; + } + + final int action = ev.getAction() & MotionEventCompat.ACTION_MASK; + switch (action) { + case MotionEvent.ACTION_DOWN: + mActivePointerId = MotionEventCompat.getPointerId(ev, 0); + mLastMotionX = ev.getX(); + break; + + case MotionEvent.ACTION_MOVE: { + final int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); + final float x = MotionEventCompat.getX(ev, activePointerIndex); + final float deltaX = x - mLastMotionX; + + if (!mIsDragging) { + if (Math.abs(deltaX) > mTouchSlop) { + mIsDragging = true; + } + } + + if (mIsDragging) { + mLastMotionX = x; + if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) { + mViewPager.fakeDragBy(deltaX); + } + } + + break; + } + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + if (!mIsDragging) { + final int count = mViewPager.getAdapter().getCount(); + final int width = getWidth(); + final float halfWidth = width / 2f; + final float sixthWidth = width / 6f; + + if ((mCurrentPage > 0) && (ev.getX() < halfWidth - sixthWidth)) { + if (action != MotionEvent.ACTION_CANCEL) { + mViewPager.setCurrentItem(mCurrentPage - 1); + } + return true; + } else if ((mCurrentPage < count - 1) && (ev.getX() > halfWidth + sixthWidth)) { + if (action != MotionEvent.ACTION_CANCEL) { + mViewPager.setCurrentItem(mCurrentPage + 1); + } + return true; + } + } + + mIsDragging = false; + mActivePointerId = INVALID_POINTER; + if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag(); + break; + + case MotionEventCompat.ACTION_POINTER_DOWN: { + final int index = MotionEventCompat.getActionIndex(ev); + mLastMotionX = MotionEventCompat.getX(ev, index); + mActivePointerId = MotionEventCompat.getPointerId(ev, index); + break; + } + + case MotionEventCompat.ACTION_POINTER_UP: + final int pointerIndex = MotionEventCompat.getActionIndex(ev); + final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); + if (pointerId == mActivePointerId) { + final int newPointerIndex = pointerIndex == 0 ? 1 : 0; + mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); + } + mLastMotionX = MotionEventCompat.getX(ev, MotionEventCompat.findPointerIndex(ev, mActivePointerId)); + break; + } + + return true; + } + + @Override + public void setViewPager(ViewPager view) { + if (mViewPager == view) { + return; + } + if (mViewPager != null) { + mViewPager.setOnPageChangeListener(null); + } + if (view.getAdapter() == null) { + throw new IllegalStateException("ViewPager does not have adapter instance."); + } + mViewPager = view; + mViewPager.setOnPageChangeListener(this); + invalidate(); + } + + @Override + public void setViewPager(ViewPager view, int initialPosition) { + setViewPager(view); + setCurrentItem(initialPosition); + } + + @Override + public void setCurrentItem(int item) { + if (mViewPager == null) { + throw new IllegalStateException("ViewPager has not been bound."); + } + mViewPager.setCurrentItem(item); + mCurrentPage = item; + invalidate(); + } + + @Override + public void notifyDataSetChanged() { + invalidate(); + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mListener != null) { + mListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + mCurrentPage = position; + mPageOffset = positionOffset; + invalidate(); + + if (mListener != null) { + mListener.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + } + + @Override + public void onPageSelected(int position) { + if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mCurrentPage = position; + mSnapPage = position; + invalidate(); + } + + if (mListener != null) { + mListener.onPageSelected(position); + } + } + + @Override + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mListener = listener; + } + + /* + * (non-Javadoc) + * + * @see android.view.View#onMeasure(int, int) + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mOrientation == HORIZONTAL) { + setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); + } else { + setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); + } + } + + /** + * Determines the width of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The width of the view, honoring constraints from measureSpec + */ + private int measureLong(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) { + //We were told how big to be + result = specSize; + } else { + //Calculate the width according the views count + final int count = mViewPager.getAdapter().getCount(); + result = (int)(getPaddingLeft() + getPaddingRight() + + (count * 2 * mRadius) + (count - 1) * mRadius + 1); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + /** + * Determines the height of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The height of the view, honoring constraints from measureSpec + */ + private int measureShort(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + //We were told how big to be + result = specSize; + } else { + //Measure the height + result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } +} diff --git a/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/PageIndicator.java b/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/PageIndicator.java new file mode 100644 index 0000000..131d53f --- /dev/null +++ b/src/org/cyanogenmod/audiofx/audiofx/viewpagerindicator/PageIndicator.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Patrik Akerfeldt + * Copyright (C) 2011 Jake Wharton + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyngn.audiofx.viewpagerindicator; + +import android.support.v4.view.ViewPager; + +/** + * A PageIndicator is responsible to show an visual indicator on the total views + * number and the current visible view. + */ +public interface PageIndicator extends ViewPager.OnPageChangeListener { + /** + * Bind the indicator to a ViewPager. + * + * @param view + */ + void setViewPager(ViewPager view); + + /** + * Bind the indicator to a ViewPager. + * + * @param view + * @param initialPosition + */ + void setViewPager(ViewPager view, int initialPosition); + + /** + * <p>Set the current page of both the ViewPager and indicator.</p> + * + * <p>This <strong>must</strong> be used if you need to set the page before + * the views are drawn on screen (e.g., default start page).</p> + * + * @param item + */ + void setCurrentItem(int item); + + /** + * Set a page change listener which will receive forwarded events. + * + * @param listener + */ + void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); + + /** + * Notify the indicator that the fragment list has changed. + */ + void notifyDataSetChanged(); +} diff --git a/src/org/cyanogenmod/audiofx/widget/Biquad.java b/src/org/cyanogenmod/audiofx/audiofx/widget/Biquad.java index f90153d..4486dfe 100644 --- a/src/org/cyanogenmod/audiofx/widget/Biquad.java +++ b/src/org/cyanogenmod/audiofx/audiofx/widget/Biquad.java @@ -1,14 +1,14 @@ -package org.cyanogenmod.audiofx.widget; +package com.cyngn.audiofx.widget; /** * Evaluate transfer functions of biquad filters in direct form 1. * * @author alankila */ -class Biquad { +public class Biquad { private Complex mB0, mB1, mB2, mA0, mA1, mA2; - protected void setHighShelf(double centerFrequency, double samplingFrequency, + public void setHighShelf(double centerFrequency, double samplingFrequency, double dbGain, double slope) { double w0 = 2 * Math.PI * centerFrequency / samplingFrequency; double a = Math.pow(10, dbGain/40); @@ -22,7 +22,7 @@ class Biquad { mA2 = new Complex((a+1) - (a-1) *Math.cos(w0) - 2*Math.sqrt(a)*alpha, 0); } - protected Complex evaluateTransfer(Complex z) { + public Complex evaluateTransfer(Complex z) { Complex zSquared = z.mul(z); Complex nom = mB0.add(mB1.div(z)).add(mB2.div(zSquared)); Complex den = mA0.add(mA1.div(z)).add(mA2.div(zSquared)); diff --git a/src/org/cyanogenmod/audiofx/widget/Complex.java b/src/org/cyanogenmod/audiofx/audiofx/widget/Complex.java index b4691a3..dbbaaf2 100644 --- a/src/org/cyanogenmod/audiofx/widget/Complex.java +++ b/src/org/cyanogenmod/audiofx/audiofx/widget/Complex.java @@ -1,14 +1,14 @@ -package org.cyanogenmod.audiofx.widget; +package com.cyngn.audiofx.widget; /** * Java support for complex numbers. * * @author alankila */ -class Complex { +public class Complex { private final double mReal, mIm; - protected Complex(double real, double im) { + public Complex(double real, double im) { mReal = real; mIm = im; } @@ -18,7 +18,7 @@ class Complex { * * @return length */ - protected double rho() { + public double rho() { return Math.sqrt(mReal * mReal + mIm * mIm); } @@ -27,7 +27,7 @@ class Complex { * * @return angle in radians */ - protected double theta() { + public double theta() { return Math.atan2(mIm, mReal); } @@ -36,7 +36,7 @@ class Complex { * * @return conjugate */ - protected Complex con() { + public Complex con() { return new Complex(mReal, -mIm); } @@ -46,7 +46,7 @@ class Complex { * @param other * @return sum */ - protected Complex add(Complex other) { + public Complex add(Complex other) { return new Complex(mReal + other.mReal, mIm + other.mIm); } @@ -56,7 +56,7 @@ class Complex { * @param other * @return multiplication result */ - protected Complex mul(Complex other) { + public Complex mul(Complex other) { return new Complex(mReal * other.mReal - mIm * other.mIm, mReal * other.mIm + mIm * other.mReal); } @@ -67,7 +67,7 @@ class Complex { * @param a * @return multiplication result */ - protected Complex mul(double a) { + public Complex mul(double a) { return new Complex(mReal * a, mIm * a); } @@ -77,7 +77,7 @@ class Complex { * @param other * @return division result */ - protected Complex div(Complex other) { + public Complex div(Complex other) { double lengthSquared = other.mReal * other.mReal + other.mIm * other.mIm; return mul(other.con()).div(lengthSquared); } @@ -88,7 +88,7 @@ class Complex { * @param a * @return division result */ - protected Complex div(double a) { + public Complex div(double a) { return new Complex(mReal / a, mIm / a); } } diff --git a/src/org/cyanogenmod/audiofx/widget/EqualizerSurface.java b/src/org/cyanogenmod/audiofx/audiofx/widget/EqualizerSurface.java index ff44c43..2b3c637 100644 --- a/src/org/cyanogenmod/audiofx/widget/EqualizerSurface.java +++ b/src/org/cyanogenmod/audiofx/audiofx/widget/EqualizerSurface.java @@ -17,10 +17,9 @@ * - Modified extensively by cyanogen for multi-band support */ -package org.cyanogenmod.audiofx.widget; +package com.cyngn.audiofx.widget; import android.animation.Animator; -import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; @@ -38,10 +37,8 @@ import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; -import org.cyanogenmod.audiofx.R; +import com.cyngn.audiofx.R; import java.util.Arrays; @@ -77,7 +74,7 @@ public class EqualizerSurface extends SurfaceView implements ValueAnimator.Anima setWillNotDraw(false); mWhite = new Paint(); - mWhite.setColor(getResources().getColor(R.color.white)); + mWhite.setColor(getResources().getColor(R.color.color_grey)); mWhite.setStyle(Style.STROKE); mWhite.setTextSize(mTextSize = context.getResources().getDimensionPixelSize(R.dimen.eq_label_text_size)); mWhite.setTypeface(Typeface.DEFAULT_BOLD); @@ -386,13 +383,17 @@ public class EqualizerSurface extends SurfaceView implements ValueAnimator.Anima for (float dB = mMinDB + 3; dB <= mMaxDB - 3; dB += 3) { float y = projectY(dB) * mHeight; // canvas.drawLine(0, y, mWidth - 1, y, mGridLines); - canvas.drawText(String.format("%+d", (int)dB), 1, (y - 1), mWhite); +// canvas.drawText(String.format("%+d", (int)dB), 1, (y - 1), mWhite); } for (int i = 0; i < mNumBands; i ++) { float freq = mCenterFreqs[i]; float x = projectX(freq) * mWidth; + float y = projectY(mLevels[i]) * (mHeight); + + Log.i("eqsurface", i + " level: " + mLevels[i] + ", y: " + y); + String frequencyText = String.format(freq < 1000 ? "%.0f" : "%.0fk", freq < 1000 ? freq : freq / 1000); diff --git a/src/org/cyanogenmod/audiofx/widget/InterceptableLinearLayout.java b/src/org/cyanogenmod/audiofx/audiofx/widget/InterceptableLinearLayout.java index c8d838a..ae0e8a0 100644 --- a/src/org/cyanogenmod/audiofx/widget/InterceptableLinearLayout.java +++ b/src/org/cyanogenmod/audiofx/audiofx/widget/InterceptableLinearLayout.java @@ -26,7 +26,7 @@ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package org.cyanogenmod.audiofx.widget; +package com.cyngn.audiofx.widget; import android.content.Context; import android.util.AttributeSet; @@ -53,6 +53,11 @@ public class InterceptableLinearLayout extends LinearLayout { return mIntercept; } + @Override + public boolean hasOverlappingRendering() { + return false; + } + public void setInterception(boolean intercept) { mIntercept = intercept; } diff --git a/src/org/cyanogenmod/audiofx/widget/Gallery.java b/src/org/cyanogenmod/audiofx/widget/Gallery.java deleted file mode 100644 index 8bb5f2a..0000000 --- a/src/org/cyanogenmod/audiofx/widget/Gallery.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of The Linux Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.cyanogenmod.audiofx.widget; - -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.View; -import android.widget.AdapterView; -import android.widget.TextView; - -import org.cyanogenmod.audiofx.R; - -public class Gallery extends android.widget.Gallery { - public interface OnItemSelectedListener { - public void onItemSelected(int position); - } - - private boolean mEnabled = false; - - private int mHighlightColor; - private int mLowlightColor; - private int mDisabledColor; - - private TextView mLastView = null; - private OnItemSelectedListener mOnItemSelectedListener = null; - - public Gallery(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - Resources res = getResources(); - mHighlightColor = res.getColor(R.color.highlight); - mLowlightColor = res.getColor(R.color.grey); - mDisabledColor = res.getColor(R.color.disabled_gallery); - - setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - TextView tv = (TextView) view; - if (tv != null) { - tv.setTextColor(mEnabled ? mHighlightColor : mDisabledColor); - } - if (mLastView != null && mLastView != tv) { - mLastView.setTextColor(mEnabled ? mLowlightColor : mDisabledColor); - } - mLastView = tv; - if (mEnabled && mOnItemSelectedListener != null) { - mOnItemSelectedListener.onItemSelected(position); - } - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - } - }); - } - - public Gallery(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public Gallery(Context context) { - this(context, null); - } - - public void setOnItemSelectedListener(OnItemSelectedListener listener) { - mOnItemSelectedListener = listener; - } - - @Override - public void setEnabled(boolean enabled) { - mEnabled = enabled; - final int count = getChildCount(); - for (int i = 0; i < count; ++i) { - final View view = getChildAt(i); - if (view instanceof TextView) { - ((TextView) view).setTextColor(enabled ? mLowlightColor : mDisabledColor); - } - } - - if (enabled) { - final TextView tv = (TextView) getSelectedView(); - if (tv != null) { - tv.setTextColor(mHighlightColor); - } - } - } - - @Override - public boolean onDown(MotionEvent e) { - return mEnabled ? super.onDown(e) : false; - } -} diff --git a/src/org/cyanogenmod/audiofx/widget/Knob.java b/src/org/cyanogenmod/audiofx/widget/Knob.java deleted file mode 100644 index ec3312d..0000000 --- a/src/org/cyanogenmod/audiofx/widget/Knob.java +++ /dev/null @@ -1,434 +0,0 @@ -/* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. - * Copyright (c) 2014, The CyanogenMod Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of The Linux Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE - * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN - * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.cyanogenmod.audiofx.widget; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import java.lang.Math; - -import org.cyanogenmod.audiofx.R; - -public class Knob extends FrameLayout { - private static final String TAG = Knob.class.getSimpleName(); - - private static final int STROKE_WIDTH = 35; - private static final float TEXT_SIZE = 0.20f; - private static final float TEXT_PADDING = 0.31f; - private static final float LABEL_PADDING = 0.02f; - private static final float LABEL_SIZE = 0.08f; - private static final float LABEL_WIDTH = 0.45f; - private static final float INDICATOR_RADIUS = 0.38f; - private ValueAnimator mAnimator; - - public interface OnKnobChangeListener { - void onValueChanged(Knob knob, int value, boolean fromUser); - - boolean onSwitchChanged(Knob knob, boolean on); - - void onAnimationFinished(boolean endValue); - } - - private OnKnobChangeListener mOnKnobChangeListener = null; - - private float mOriginalProgress = 0.0f; - private float mProgress = 0.0f; - private int mMax = 100; - private boolean mOn = false; - private boolean mEnabled = false; - - private int mHighlightColor; - private int mLowlightColor; - private int mDisabledColor; - - private final Paint mPaint; - - private final TextView mLabelTV; - private final TextView mProgressTV; - - private final ImageView mKnobOn; - - private float mLastX; - private float mLastY; - private boolean mMoved; - - private int mWidth = 0; - private int mIndicatorWidth = 0; - - private RectF mRectF; - - public Knob(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Knob, 0, 0); - - String label; - int foreground; - try { - label = a.getString(R.styleable.Knob_label); - foreground = a.getResourceId(R.styleable.Knob_foreground, R.drawable.knob); - } finally { - a.recycle(); - } - - LayoutInflater li = (LayoutInflater) - context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - li.inflate(R.layout.knob, this, true); - - Resources res = getResources(); - mHighlightColor = res.getColor(R.color.highlight); - mLowlightColor = res.getColor(R.color.lowlight); - mDisabledColor = res.getColor(R.color.disabled_knob); - - ImageView fg = (ImageView) findViewById(R.id.knob_foreground); - fg.setImageResource(R.drawable.knob); - - mLabelTV = (TextView) findViewById(R.id.knob_label); - mLabelTV.setText(label); - mProgressTV = (TextView) findViewById(R.id.knob_value); - - mKnobOn = (ImageView) findViewById(R.id.knob_toggle_on); - - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mPaint.setColor(mHighlightColor); - mPaint.setStrokeWidth(65); - mPaint.setStrokeCap(Paint.Cap.BUTT); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setShadowLayer(2, 1, -2, getResources().getColor(R.color.black)); - - setWillNotDraw(false); - } - - public Knob(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public Knob(Context context) { - this(context, null); - } - - public void setOnKnobChangeListener(OnKnobChangeListener l) { - mOnKnobChangeListener = l; - } - - public void setValue(int value) { - if (mMax != 0) { - mOriginalProgress = ((float) value) / mMax; - if (mOriginalProgress > 100) { - mOriginalProgress = 100; - } else if (mOriginalProgress < 0) { - mOriginalProgress = 0; - } - - setProgress(mOriginalProgress); - } - } - - public void setProgress(float progress) { - setProgress(progress, false); - } - - public void updateProgressText(boolean showText, float progress) { - if (showText) { - mProgressTV.setText((int) (progress * 100) + "%"); - } else { - mProgressTV.setText("--%"); - } - } - - public void setProgress(float progress, boolean fromUser) { - if (progress > 1.0f) { - progress = 1.0f; - } else if (progress < 0.0f) { - progress = 0.0f; - } - - mProgress = progress; - - updateProgressText(mEnabled && mOn, progress); - - invalidate(); - - if (mOnKnobChangeListener != null) { - mOnKnobChangeListener.onValueChanged(this, (int) (progress * mMax), fromUser); - } - } - - public void setMax(int max) { - mMax = max; - } - - public float getProgress() { - return mProgress; - } - - private void drawIndicator() { - float r = mWidth * INDICATOR_RADIUS; -// ImageView view = mEnabled ? mKnobOn : mKnobOff; - mKnobOn.setTranslationX((float) Math.sin(mProgress * 2 * Math.PI) * r - mIndicatorWidth / 2); - mKnobOn.setTranslationY((float) -Math.cos(mProgress * 2 * Math.PI) * r - mIndicatorWidth / 2); - } - - @Override - public void setEnabled(boolean enabled) { - mEnabled = enabled; - - mLabelTV.setTextColor(mEnabled ? mHighlightColor : mDisabledColor); - mProgressTV.setTextColor(mEnabled ? mHighlightColor : mDisabledColor); - mPaint.setColor(mEnabled ? mHighlightColor : mDisabledColor); - -// if (enabled) { -// mOn = true; -// } - if (enabled) { - setOn(mOn, false); - } -// updateProgressText(mEnabled && mOn, mOriginalProgress); -// } else { -// } -// invalidate(); - } - - public void setOn(final boolean on, boolean animate) { - mOn = on; - - if (mAnimator != null) { - mAnimator.cancel(); - } - if (mOriginalProgress > 1) { - mOriginalProgress = 1; - } - if (animate) { - if (on) { - mAnimator = ValueAnimator.ofFloat(mProgress, mOriginalProgress); - } else { - mAnimator = ValueAnimator.ofFloat(mProgress, 0f); - } - mAnimator.setDuration(500); - mAnimator.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - mAnimator = null; - updateProgressText(mOn && mEnabled, mOriginalProgress); - if (mOnKnobChangeListener != null) { - mOnKnobChangeListener.onAnimationFinished(on); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - }); - mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float progress = (Float) animation.getAnimatedValue(); - if (progress < 0) { - progress = 0; - } else if (progress > 1) { - progress = 1; - } - mProgress = progress; - if (mOnKnobChangeListener != null) { - mOnKnobChangeListener.onValueChanged(Knob.this, (int) (progress * mMax), true); - } - updateProgressText(true, mProgress); - invalidate(); - } - }); - mAnimator.start(); - } else { - updateProgressText(mEnabled && mOn, mOriginalProgress); - - // make progress correct value - mProgress = mOn ? mOriginalProgress : 0f; - - invalidate(); - - if (mOnKnobChangeListener != null) { - mOnKnobChangeListener.onAnimationFinished(on); - } - } - - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - drawIndicator(); - if (mEnabled) { - canvas.drawArc(mRectF, -90, mProgress * 360, false, mPaint); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldW, int oldH) { - int size = w > h ? h : w; - mWidth = size; - mIndicatorWidth = mKnobOn.getWidth(); - - int diff; - if (w > h) { - diff = (w - h) / 2; - mRectF = new RectF(STROKE_WIDTH + diff, STROKE_WIDTH, - w - STROKE_WIDTH - diff, h - STROKE_WIDTH); - } else { - diff = (h - w) / 2; - mRectF = new RectF(STROKE_WIDTH, STROKE_WIDTH + diff, - w - STROKE_WIDTH, h - STROKE_WIDTH - diff); - } - - mProgressTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, size * TEXT_SIZE); - mProgressTV.setPadding(0, (int) (size * TEXT_PADDING), 0, 0); - mProgressTV.setVisibility(View.VISIBLE); - mLabelTV.setTextSize(TypedValue.COMPLEX_UNIT_PX, size * LABEL_SIZE); - mLabelTV.setPadding(0, (int) (size * LABEL_PADDING), 0, 0); - mLabelTV.setLayoutParams(new LinearLayout.LayoutParams((int) (w * LABEL_WIDTH), - LayoutParams.WRAP_CONTENT)); - mLabelTV.setVisibility(View.VISIBLE); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return true; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (mOn) { - mLastX = event.getX(); - mLastY = event.getY(); - getParent().requestDisallowInterceptTouchEvent(true); - } - break; - case MotionEvent.ACTION_MOVE: - if (mOn) { - float x = event.getX(); - float y = event.getY(); - float center = mWidth / 2; - if (mMoved || (x - center) * (x - center) + (y - center) * (y - center) - > center * center / 4) { - float delta = getDelta(x, y); - mOriginalProgress = mProgress + delta / 360; - if (mOriginalProgress < 0) { - mOriginalProgress = 0; - } else if (mOriginalProgress > 100) { - mOriginalProgress = 100; - } - setProgress(mOriginalProgress, true); - mMoved = true; - } - mLastX = x; - mLastY = y; - } - break; - case MotionEvent.ACTION_UP: - if (!mMoved) { - if (mOnKnobChangeListener == null - || mOnKnobChangeListener.onSwitchChanged(this, !mOn)) { - if (mEnabled) { - setOn(!mOn, true); - invalidate(); - } - } - } - mMoved = false; - break; - default: - break; - } - return true; - } - - private float getDelta(float x, float y) { - float angle = angle(x, y); - float oldAngle = angle(mLastX, mLastY); - float delta = angle - oldAngle; - if (delta >= 180.0f) { - delta = -oldAngle; - } else if (delta <= -180.0f) { - delta = 360 - oldAngle; - } - return delta; - } - - private float angle(float x, float y) { - float center = mWidth / 2.0f; - x -= center; - y -= center; - - if (x == 0.0f) { - if (y > 0.0f) { - return 180.0f; - } else { - return 0.0f; - } - } - - float angle = (float) (Math.atan(y / x) / Math.PI * 180.0); - if (x > 0.0f) { - angle += 90; - } else { - angle += 270; - } - return angle; - } -} diff --git a/src_effects/com/cyngn/audiofx/backends/EffectsFactory.java b/src_effects/com/cyngn/audiofx/backends/EffectsFactory.java new file mode 100644 index 0000000..e33f5f5 --- /dev/null +++ b/src_effects/com/cyngn/audiofx/backends/EffectsFactory.java @@ -0,0 +1,35 @@ +package com.cyngn.audiofx.backends; + +import com.cyngn.audiofx.Constants; + +import android.content.Context; +import android.media.AudioDeviceInfo; + +/** + * Creates an EffectSet appropriate for the current device + */ +public class EffectsFactory implements IEffectFactory { + + private static final String TAG = "AudioFx-EffectsFactory"; + + private static int sBrand = -1; // cached value to not hit io every time we need a new effect + + public EffectSet createEffectSet(Context context, int sessionId, + AudioDeviceInfo currentDevice) { + // if this throws, we're screwed, don't bother to recover. these + // are the standard effects that every android device must have, + // and if they don't exist we have bigger problems. + return new AndroidEffects(sessionId, currentDevice); + } + + public static int getBrand() { + if (sBrand == -1) { + sBrand = getBrandInternal(); + } + return sBrand; + } + + private static int getBrandInternal() { + return Constants.EFFECT_TYPE_ANDROID; + } +} diff --git a/tests/Android.mk b/tests/Android.mk new file mode 100644 index 0000000..a8dc5b9 --- /dev/null +++ b/tests/Android.mk @@ -0,0 +1,36 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := ModioFXTests +LOCAL_INSTRUMENTATION_FOR := ModioFX + +LOCAL_STATIC_JAVA_LIBRARIES := \ + audiofx-android-support-test + +LOCAL_JAVA_LIBRARIES := \ + android-support-v4 \ + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_JACK_ENABLED := disabled + +# Sign the package when not using test-keys +ifneq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/target/product/security/testkey) +LOCAL_CERTIFICATE := cyngn-app +else +$(warning *** SIGNING MODIOFX WITH TEST KEY ***) +endif + +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) + +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \ + audiofx-android-support-test:lib/rules-0.3-release.jar + +include $(BUILD_MULTI_PREBUILT)
\ No newline at end of file diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml new file mode 100644 index 0000000..151bd07 --- /dev/null +++ b/tests/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.cyngn.audiofx.tests"> + + <application android:label="@string/app_name"> + <uses-library android:name="android.test.runner" /> + + <activity android:name=".DebugActivity" + android:label="AudioFX Debug"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.cyngn.audiofx" /> +</manifest> diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..24f8815 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,5 @@ +## AudioFX Tests + +To run the tests (on a live device): + + adb shell am instrument -w com.cyngn.audiofx.tests/android.support.test.runner.AndroidJUnitRunner
\ No newline at end of file diff --git a/tests/lib/rules-0.3-release.jar b/tests/lib/rules-0.3-release.jar Binary files differnew file mode 100644 index 0000000..3302862 --- /dev/null +++ b/tests/lib/rules-0.3-release.jar diff --git a/tests/res/raw/testmp3.mp3 b/tests/res/raw/testmp3.mp3 Binary files differnew file mode 100755 index 0000000..657faf7 --- /dev/null +++ b/tests/res/raw/testmp3.mp3 diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml new file mode 100644 index 0000000..6c8112c --- /dev/null +++ b/tests/res/values/strings.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" ?> +<resources> + <string name="app_name">AudioFX Tests</string> +</resources> diff --git a/tests/src/com/cyngn/audiofx/PresetParcelTests.java b/tests/src/com/cyngn/audiofx/PresetParcelTests.java new file mode 100644 index 0000000..8ea0c68 --- /dev/null +++ b/tests/src/com/cyngn/audiofx/PresetParcelTests.java @@ -0,0 +1,94 @@ +package com.cyngn.audiofx; + +import android.os.Parcel; +import android.support.test.runner.AndroidJUnit4; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import com.cyngn.audiofx.Preset; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Created by roman on 9/29/15. + */ +@RunWith(AndroidJUnit4.class) +public class PresetParcelTests { + + private Preset.PermCustomPreset permPreset; + private Preset.PermCustomPreset permPresetCopy; + private Preset.CustomPreset customPreset; + + @Before + public void setUp() throws Exception { + permPreset = new Preset.PermCustomPreset("test perm preset", new float[]{10, 50, 20, 30, 10000}); + permPresetCopy = new Preset.PermCustomPreset("test perm preset", new float[]{10, 50, 20, 30, 10000}); + customPreset = new Preset.CustomPreset("test custom preset", new float[]{10, 50, 20, 30, 10000}, false); + } + + @Test + public void testPresetsEqual() { + assertNotEquals(permPreset, customPreset); + assertEquals(permPreset, permPresetCopy); + } + + @Test + public void testPresetsFromString() { + final String permPresetTest = permPreset.toString(); + final Preset.PermCustomPreset fromString = Preset.PermCustomPreset.fromString(permPresetTest); + + assertEquals(permPreset, fromString); + } + + @Test + public void testPermPresetParcelable() { + Parcel parcel = Parcel.obtain(); + + // write the parcel + permPreset.writeToParcel(parcel, 0); + + // reset position for reading + parcel.setDataPosition(0); + + // reconstruct object + final Preset.PermCustomPreset fromParcel = Preset.PermCustomPreset.CREATOR.createFromParcel(parcel); + + assertNotNull(fromParcel); + + assertEquals(permPreset.getName(), fromParcel.getName()); + assertPresetLevels(permPreset, fromParcel); + + assertEquals(permPreset, fromParcel); + } + + @Test + public void testCustomPresetParcelable() { + Parcel parcel = Parcel.obtain(); + + // write the parcel + customPreset.writeToParcel(parcel, 0); + + // reset position for reading + parcel.setDataPosition(0); + + // reconstruct object + final Preset.CustomPreset fromParcel = Preset.CustomPreset.CREATOR.createFromParcel(parcel); + + assertNotNull(fromParcel); + + assertEquals(customPreset.getName(), fromParcel.getName()); + assertPresetLevels(customPreset, fromParcel); + assertEquals(customPreset.isLocked(), fromParcel.isLocked()); + + assertEquals(customPreset, fromParcel); + } + + private void assertPresetLevels(Preset p1, Preset p2) { + for(int i = 0; i < p1.getLevels().length; i++) { + assertEquals(p1.getLevels()[i], p2.getLevels()[i], 0); + } + } +} diff --git a/tests/src/com/cyngn/audiofx/eq/EqUtilTests.java b/tests/src/com/cyngn/audiofx/eq/EqUtilTests.java new file mode 100644 index 0000000..bca4b85 --- /dev/null +++ b/tests/src/com/cyngn/audiofx/eq/EqUtilTests.java @@ -0,0 +1,40 @@ +package com.cyngn.audiofx.eq; + +import android.test.suitebuilder.annotation.SmallTest; +import com.cyngn.audiofx.Preset; +import com.cyngn.audiofx.eq.EqUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Created by roman on 9/29/15. + */ +public class EqUtilTests { + + private Preset.PermCustomPreset permPreset; + private Preset.PermCustomPreset permPresetCopy; + private Preset.CustomPreset customPreset; + + @Before + public void setUp() throws Exception { + permPreset = new Preset.PermCustomPreset("test perm preset", new float[]{10, 50, 20, 30, 10000}); + permPresetCopy = new Preset.PermCustomPreset("test perm preset", new float[]{10, 50, 20, 30, 10000}); + customPreset = new Preset.CustomPreset("test custom preset", new float[]{10, 50, 20, 30, 10000}, false); + } + + @Test + public void testConvertDecibelsToMillibels() { + final float[] convertedMillibels = EqUtils.convertDecibelsToMillibels(permPreset.getLevels()); + + float[] manualMillibels = new float[permPreset.getLevels().length]; + for (int i = 0; i < manualMillibels.length; i++) { + manualMillibels[i] = permPreset.getLevels()[i] * 100; + } + + for (int i = 0; i < manualMillibels.length; i++) { + Assert.assertEquals(manualMillibels[i], convertedMillibels[i], 0); + } + } + +} diff --git a/tests/src/com/cyngn/audiofx/service/AudioFxServiceTests.java b/tests/src/com/cyngn/audiofx/service/AudioFxServiceTests.java new file mode 100644 index 0000000..1bf531c --- /dev/null +++ b/tests/src/com/cyngn/audiofx/service/AudioFxServiceTests.java @@ -0,0 +1,178 @@ +package com.cyngn.audiofx.service; + +import android.content.Intent; +import android.media.audiofx.AudioEffect; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.LargeTest; + +import android.util.Log; +import com.cyngn.audiofx.util.BaseAudioFxServiceInstrumentationTest; +import com.cyngn.audiofx.util.TestMediaPlayer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import com.cyngn.audiofx.tests.R; + +import static org.junit.Assert.*; + +/** + * Created by roman on 3/1/16. + */ +@RunWith(AndroidJUnit4.class) +public class AudioFxServiceTests extends BaseAudioFxServiceInstrumentationTest { + + private static final String TAG = "AudioFxServiceTests"; + + private static final int SANE_MAX_LOOP_TIME = 1000 * 20; // 20 seconds !? + private static final int LOOP_SLEEP_TIME = 25; + + TestMediaPlayer mPlayer; + + @Before + public void setUp() throws Exception { + mPlayer = new TestMediaPlayer(getContext(), R.raw.testmp3); + assertNotNull(mPlayer); + } + + @After + public void tearDown() throws Exception { + if (mPlayer != null) { + mPlayer.release(); + mPlayer = null; + } + } + + @Test + public void testServiceCreatesEffectsDirect() throws Exception { + setupService(); // this might be reused + + Intent intent = new Intent(getTargetContext(), AudioFxService.class); + intent.setAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mPlayer.getAudioSessionId()); + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getTargetContext().getPackageName()); + + mServiceRule.startService(intent); + Thread.sleep(100); + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + } + + @Test + public void testServiceDestroysEffectsDirect() throws Exception { + testServiceCreatesEffectsDirect(); + + Intent intent = new Intent(getTargetContext(), AudioFxService.class); + intent.setAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mPlayer.getAudioSessionId()); + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getTargetContext().getPackageName()); + + mServiceRule.startService(intent); + + // sleep for 10s to let effects die + Thread.sleep(10100); + assertNull(mService.getEffect(mPlayer.getAudioSessionId())); + } + + @Test + public void testServiceCreatesEffects() throws Exception { + setupService(); + + openFxSession(true); + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + } + + @Test + @LargeTest + public void testServiceDestroysEffects() throws Exception { + testServiceCreatesEffects(); + + closeFxSession(true); + // sleep for 10s to let effects die + assertNull(mService.getEffect(mPlayer.getAudioSessionId())); + } + + @Test + public void testServiceDoesNotDestroyDeferredEffect() throws Exception { + setupService(); + assertNull(mService.getEffect(mPlayer.getAudioSessionId())); + + openFxSession(false); + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + + closeFxSession(false); + // shouldn't go away immediately after we close it + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + + Thread.sleep(8000); + + // it should *still* be there not destroyed + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + + // reopen the session + openFxSession(false); + + // session should still be there + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + + Thread.sleep(10000); + + // it's been more than 10s (deferred destroy time) and we re-opened it, so it should be + // alive still + assertNotNull(mService.getEffect(mPlayer.getAudioSessionId())); + } + + private void openFxSession(boolean block) throws InterruptedException { + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mPlayer.getAudioSessionId()); + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getTargetContext().getPackageName()); + getContext().sendBroadcast(intent); + } + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + if (block) { + int cnt = 0; + while (mService.getEffect(mPlayer.getAudioSessionId()) == null + && (cnt * LOOP_SLEEP_TIME < SANE_MAX_LOOP_TIME)) { + cnt++; + Thread.sleep(LOOP_SLEEP_TIME); + } + Log.d(TAG, "took " + (cnt * LOOP_SLEEP_TIME) + "ms to open effect"); + } else { + // TODO have a timeout here for failure based on time limits? + Thread.sleep(300); + } + } + + private void closeFxSession(boolean block) throws InterruptedException { + InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + Intent intent = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); + intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, mPlayer.getAudioSessionId()); + intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, getTargetContext().getPackageName()); + getContext().sendBroadcast(intent); + } + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + if (block) { + int cnt = 0; + while (mService.getEffect(mPlayer.getAudioSessionId()) != null + && (cnt * LOOP_SLEEP_TIME < SANE_MAX_LOOP_TIME)) { + cnt++; + Thread.sleep(LOOP_SLEEP_TIME); + } + Log.d(TAG, "took " + (cnt * LOOP_SLEEP_TIME) + "ms to close effect"); + } else { + // TODO have a timeout here for failure based on time limits? + Thread.sleep(300); + } + } + +} diff --git a/tests/src/com/cyngn/audiofx/tests/DebugActivity.java b/tests/src/com/cyngn/audiofx/tests/DebugActivity.java new file mode 100644 index 0000000..9c98f59 --- /dev/null +++ b/tests/src/com/cyngn/audiofx/tests/DebugActivity.java @@ -0,0 +1,87 @@ +package com.cyngn.audiofx.tests; + +import android.media.AudioManager; +import android.media.AudioSystem; +import android.media.RingtoneManager; +import android.os.AsyncTask; +import android.util.Log; + +import com.cyngn.audiofx.util.TestDuckingMediaPlayer; + +/** + * Created by roman on 3/8/16. + */ +public class DebugActivity extends TestActivity { + + @Override + protected String tag() { + return DebugActivity.class.getSimpleName(); + } + + @Override + protected Test[] tests() { + return new Test[]{ + testAudioDucking() + }; + } + + private Test testAudioDucking() { + return new Test("Test Audio Ducking") { + @Override + protected void run() { + try { + final TestDuckingMediaPlayer songMediaPlayer = new TestDuckingMediaPlayer(getApplication()); + songMediaPlayer.setAudioSessionId(AudioSystem.newAudioSessionId()); + songMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + songMediaPlayer.setDataSource(getApplication(), + RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)); + songMediaPlayer.prepare(); + + final TestDuckingMediaPlayer interruptPlayer = new TestDuckingMediaPlayer( + DebugActivity.this, R.raw.testmp3); + + final Integer focusResult = songMediaPlayer.start(AudioManager.AUDIOFOCUS_GAIN); + Log.d(tag(), "requestFocus returns: " + focusResult); + + new AsyncTask<Void, Void, Void>() { + + @Override + protected Void doInBackground(Void... params) { + try { + Thread.sleep(400); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + interruptPlayer.start(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + interruptPlayer.stop(); + + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + songMediaPlayer.stop(); + + interruptPlayer.release(); + songMediaPlayer.release(); + + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } catch (Exception e) { + e.printStackTrace(); + finish(); + } + } + }; + } +} diff --git a/tests/src/com/cyngn/audiofx/tests/TestActivity.java b/tests/src/com/cyngn/audiofx/tests/TestActivity.java new file mode 100644 index 0000000..61a6b34 --- /dev/null +++ b/tests/src/com/cyngn/audiofx/tests/TestActivity.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.cyngn.audiofx.tests; + +import android.app.ListActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public abstract class TestActivity extends ListActivity +{ + Test[] mTests; + + protected abstract String tag(); + protected abstract Test[] tests(); + + protected abstract class Test { + protected String name; + protected Test(String n) { + name = n; + } + protected abstract void run(); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mTests = tests(); + + String[] labels = new String[mTests.length]; + for (int i=0; i<mTests.length; i++) { + labels[i] = mTests[i].name; + } + + setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, labels)); + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) + { + Test t = mTests[position]; + android.util.Log.d(tag(), "Test: " + t.name); + t.run(); + } + +}
\ No newline at end of file diff --git a/tests/src/com/cyngn/audiofx/util/BaseAudioFxServiceInstrumentationTest.java b/tests/src/com/cyngn/audiofx/util/BaseAudioFxServiceInstrumentationTest.java new file mode 100644 index 0000000..e882c9d --- /dev/null +++ b/tests/src/com/cyngn/audiofx/util/BaseAudioFxServiceInstrumentationTest.java @@ -0,0 +1,59 @@ +package com.cyngn.audiofx.util; + +import android.content.Context; +import android.content.Intent; +import android.media.audiofx.AudioEffect; +import android.os.IBinder; +import android.support.test.InstrumentationRegistry; +import android.support.test.rule.ServiceTestRule; +import android.support.test.runner.AndroidJUnit4; +import com.cyngn.audiofx.service.AudioFxService; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.*; + +/** + * Created by roman on 3/1/16. + */ +@RunWith(AndroidJUnit4.class) +public abstract class BaseAudioFxServiceInstrumentationTest { + + private static final int MAX_ITERATION = 100; + + protected AudioFxService.LocalBinder mService; + + @Rule + public ServiceTestRule mServiceRule = new ServiceTestRule(); + + protected void setupService() throws TimeoutException { + int it = 0; + + /** + * lol + * https://code.google.com/p/android/issues/detail?id=180396 + */ + IBinder binder; + while((binder = mServiceRule.bindService( + new Intent(InstrumentationRegistry.getTargetContext(), + AudioFxService.class))) == null && it < MAX_ITERATION){ + it++; + } + mService = (AudioFxService.LocalBinder) binder; + assertNotNull(mService); + } + + protected final Context getContext() { + return InstrumentationRegistry.getContext(); + } + + protected final Context getTargetContext() { + return InstrumentationRegistry.getTargetContext(); + } + +} diff --git a/tests/src/com/cyngn/audiofx/util/TestDuckingMediaPlayer.java b/tests/src/com/cyngn/audiofx/util/TestDuckingMediaPlayer.java new file mode 100644 index 0000000..fc67cc2 --- /dev/null +++ b/tests/src/com/cyngn/audiofx/util/TestDuckingMediaPlayer.java @@ -0,0 +1,59 @@ +package com.cyngn.audiofx.util; + +import android.content.Context; +import android.media.AudioManager; +import android.util.Log; + +import com.cyngn.audiofx.tests.R; + +import static junit.framework.Assert.assertNotNull; + +/** + * Created by roman on 3/4/16. + */ +public class TestDuckingMediaPlayer extends TestMediaPlayer { + + private static final String TAG = TestDuckingMediaPlayer.class.getSimpleName(); + private AudioManager mAudioManager; + + private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener + = new AudioManager.OnAudioFocusChangeListener() { + public void onAudioFocusChange(int focusChange) { + Log.i(TAG, "onAudioFocusChange() called with " + "focusChange = [" + focusChange + "]"); + switch(focusChange) { + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + setVolume(0.4f); + break; + + case AudioManager.AUDIOFOCUS_GAIN: + setVolume(1.f); + break; + } + } + }; + + public int start(int focus) throws IllegalStateException { + int result = mAudioManager.requestAudioFocus(mAudioFocusChangeListener, AudioManager.STREAM_MUSIC, + focus); + super.start(); + return result; + } + + @Override + public void stop() throws IllegalStateException { + mAudioManager.abandonAudioFocus(mAudioFocusChangeListener); + super.stop(); + } + + public TestDuckingMediaPlayer(Context context) { + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + } + + public TestDuckingMediaPlayer(Context context, int withResource) throws Exception { + super(context, withResource); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + } + + + +} diff --git a/tests/src/com/cyngn/audiofx/util/TestMediaPlayer.java b/tests/src/com/cyngn/audiofx/util/TestMediaPlayer.java new file mode 100644 index 0000000..ef08d1c --- /dev/null +++ b/tests/src/com/cyngn/audiofx/util/TestMediaPlayer.java @@ -0,0 +1,31 @@ +package com.cyngn.audiofx.util; + +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.MediaPlayer; + +import com.cyngn.audiofx.tests.R; + +import static junit.framework.Assert.assertNotNull; + +/** + * Created by roman on 3/4/16. + */ +public class TestMediaPlayer extends MediaPlayer { + + public TestMediaPlayer() { + setAudioStreamType(AudioManager.STREAM_MUSIC); + } + + public TestMediaPlayer(Context testContext, int withResource) throws Exception { + this(); + AssetFileDescriptor afd = testContext.getResources().openRawResourceFd(withResource); + assertNotNull(afd); + setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + afd.close(); + prepare(); + } + +} |