diff options
author | Venkateshwarlu Domakonda <Venkateshwarlu@codeaurora.org> | 2013-07-01 14:38:07 +0530 |
---|---|---|
committer | Venkateshwarlu Domakonda <Venkateshwarlu@codeaurora.org> | 2013-07-04 20:41:09 +0530 |
commit | 47abd376fa60845965192080fdb63606b8738892 (patch) | |
tree | 32c36c48052c07588eb6e477311ef5c7f8e3bd2c /fmapp | |
parent | b71d82ac0e5f6735d8cb8071266d3b5e9b0bf02b (diff) | |
download | android_hardware_qcom_fm-47abd376fa60845965192080fdb63606b8738892.tar.gz android_hardware_qcom_fm-47abd376fa60845965192080fdb63606b8738892.tar.bz2 android_hardware_qcom_fm-47abd376fa60845965192080fdb63606b8738892.zip |
fm: Move FM APP source tree to vendor project
- FM APP source moved from packages/apps to vendor/qcom-opensource/
repository
- Remove META data change intent handler
Change-Id: I669ee82b5ab2a3a4717387e4ce0c1452423497e9
Diffstat (limited to 'fmapp')
84 files changed, 20451 insertions, 0 deletions
diff --git a/fmapp/Android.mk b/fmapp/Android.mk new file mode 100644 index 0000000..8692b8c --- /dev/null +++ b/fmapp/Android.mk @@ -0,0 +1,24 @@ +ifeq ($(BOARD_HAVE_QCOM_FM),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/codeaurora/fmradio) \ + src/com/codeaurora/fmradio/IFMRadioServiceCallbacks.aidl \ + src/com/codeaurora/fmradio/IFMRadioService.aidl \ + src/com/codeaurora/fmradio/IFMTransmitterServiceCallbacks.aidl \ + src/com/codeaurora/fmradio/IFMTransmitterService.aidl \ + + +ifeq ($(call is-android-codename-in-list,JELLY_BEAN ICECREAM_SANDWICH HONEYCOMB),true) +LOCAL_SRC_FILES += $(call all-java-files-under, src/com/codeaurora/hc_utils) +else +LOCAL_SRC_FILES += $(call all-java-files-under, src/com/codeaurora/utils) +endif +LOCAL_PACKAGE_NAME := FM +LOCAL_CERTIFICATE := platform +LOCAL_JNI_SHARED_LIBRARIES := libqcomfm_jni +LOCAL_JAVA_LIBRARIES := qcom.fmradio +include $(BUILD_PACKAGE) + +endif diff --git a/fmapp/AndroidManifest.xml b/fmapp/AndroidManifest.xml new file mode 100644 index 0000000..d0232bc --- /dev/null +++ b/fmapp/AndroidManifest.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.codeaurora.fmradio" + android:sharedUserId="android.uid.system" > + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> + <uses-permission android:name="android.permission.BLUETOOTH" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> + + <application + android:icon="@drawable/ic_launcher_fmradio" + android:label="@string/app_name" + android:name=".FMAdapterApp"> + <activity android:icon="@drawable/ic_launcher_fmradio" + android:name=".FMRadio" + android:label="@string/app_name" + android:clearTaskOnLaunch="true" + android:taskAffinity="" + android:launchMode="singleTop" + android:excludeFromRecents="true" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <intent-filter> + <action android:name="com.codeaurora.fmradio.FMRADIO_ACTIVITY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <receiver android:name="com.codeaurora.fmradio.FMMediaButtonIntentReceiver"> + <intent-filter> + <action android:name="android.intent.action.MEDIA_BUTTON" /> + </intent-filter> + </receiver> + + <activity android:name=".Settings" + android:label="@string/settings_menu"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + + <activity android:icon="@drawable/ic_launcher_fm_tx" + android:name=".FMTransmitterActivity" + android:label="@string/tx_app_name" + android:clearTaskOnLaunch="true" + android:taskAffinity="" + android:excludeFromRecents="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + + <intent-filter> + <action android:name="com.codeaurora.fmradio.FMTRANSMITTER_ACTIVITY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <receiver android:name="FMTransmitterConfigReceiver"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> + + <service android:name=".FMTransmitterService" android:exported="true" /> + <activity android:name=".FMStats" + android:label="@string/test_menu"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <activity android:name=".FmTags" + android:label="@string/rt_plus_tags"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <service android:name=".FMRadioService" android:exported="true" /> + + </application> +</manifest> diff --git a/fmapp/fmtransmitter.xml b/fmapp/fmtransmitter.xml new file mode 100644 index 0000000..3331f6f --- /dev/null +++ b/fmapp/fmtransmitter.xml @@ -0,0 +1,223 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- + This LinearLayout contains Station Information display and the + function buttons + --> + <LinearLayout android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="horizontal"> + + <LinearLayout android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + android:gravity="top|fill_vertical"> + + <RelativeLayout android:id="@+id/station_layout" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This included layout contains Station Information to display --> + <LinearLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5px" + android:layout_marginLeft="5px" + android:layout_marginRight="5px" + android:orientation="vertical" + android:gravity="fill_vertical"> + + <LinearLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="25px" + android:paddingBottom="25px" + android:gravity="fill_vertical|center"> + + <TextView android:id="@+id/transmit_msg_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="15px" + android:layout_centerHorizontal="true" + android:layout_alignWithParentIfMissing="true" + android:text="@string/transmit_msg_string" /> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:layout_alignWithParentIfMissing="true" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:textSize="56px" + android:text="@string/frequency_string" /> + </LinearLayout> + + <!-- Station Radio Text information display --> + <TextView android:id="@+id/radio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingBottom="2px" + android:paddingLeft="5px" + android:paddingRight="5px" + android:gravity="center_horizontal" + android:text="@string/radio_text_string" /> + <!-- android:gravity="center_horizontal" --> + </LinearLayout> + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:paddingLeft="10px" + android:paddingRight="2px" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left" /> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:paddingLeft="2px" + android:paddingRight="10px" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right" /> + + </RelativeLayout> + </LinearLayout> + <LinearLayout android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_gravity="center" + android:layout_marginLeft="5px" + android:layout_marginRight="5px" + android:layout_marginBottom="2px"> + <!-- On-Off button --> + <ImageButton android:id="@+id/btn_onoff" + android:layout_width="75px" + android:layout_height="75px" + android:src="@drawable/ic_btn_onoff" + android:layout_marginTop="15px" + android:layout_marginBottom="15px" /> + </LinearLayout> + + </LinearLayout> + <View + android:layout_width="fill_parent" + android:layout_height="1px" + android:background="#ffffffff" /> + + <!-- + Layout contains the Next/Previous Presets and Tune buttons and status + msg text + --> + <LinearLayout android:id="@+id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_weight="0" + android:layout_gravity="center" + android:layout_marginTop="15dip" + android:layout_marginBottom="10dip" + android:gravity="center_horizontal"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_marginLeft="20dip" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip" /> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip" /> + + <Button android:id="@+id/presets_button_6" + android:text="@string/default_blank" + android:layout_marginRight="20dip" + android:textSize="24sp" + android:singleLine="true" + android:layout_width="110dip" + android:layout_height="65dip"/> + + </LinearLayout> + + +</LinearLayout> diff --git a/fmapp/res/anim/preset_select.xml b/fmapp/res/anim/preset_select.xml new file mode 100644 index 0000000..8fcf768 --- /dev/null +++ b/fmapp/res/anim/preset_select.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<scale xmlns:android="http://schemas.android.com/apk/res/android" + android:fromXScale="0.75" + android:toXScale="1.5" + android:fromYScale="0.75" + android:toYScale="1.5" + android:pivotX="50%" + android:pivotY="50%" + android:startOffset="0" + android:duration="500" + android:fillBefore="true" /> diff --git a/fmapp/res/drawable/album_border_large.1.png b/fmapp/res/drawable/album_border_large.1.png Binary files differnew file mode 100644 index 0000000..e5ffbc7 --- /dev/null +++ b/fmapp/res/drawable/album_border_large.1.png diff --git a/fmapp/res/drawable/alert_dialog_icon.png b/fmapp/res/drawable/alert_dialog_icon.png Binary files differnew file mode 100644 index 0000000..0a7de04 --- /dev/null +++ b/fmapp/res/drawable/alert_dialog_icon.png diff --git a/fmapp/res/drawable/application_border.xml b/fmapp/res/drawable/application_border.xml new file mode 100644 index 0000000..479dd75 --- /dev/null +++ b/fmapp/res/drawable/application_border.xml @@ -0,0 +1,33 @@ +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke android:width="1dp" android:color="#A0000000" /> + <padding android:left="1dp" android:top="1dp" + android:right="1dp" android:bottom="1dp" /> + <corners android:radius="1dp" /> +</shape> diff --git a/fmapp/res/drawable/btn_arrow_left.xml b/fmapp/res/drawable/btn_arrow_left.xml new file mode 100644 index 0000000..260c62b --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_left.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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_pressed="true" + android:drawable="@drawable/btn_arrow_left_press" /> + + <item android:state_window_focused="true" android:state_focused="true" + android:drawable="@drawable/btn_arrow_left_selected" /> + + <item + android:drawable="@drawable/btn_arrow_left_default" /> + +</selector> diff --git a/fmapp/res/drawable/btn_arrow_left_default.png b/fmapp/res/drawable/btn_arrow_left_default.png Binary files differnew file mode 100644 index 0000000..2a35952 --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_left_default.png diff --git a/fmapp/res/drawable/btn_arrow_left_press.png b/fmapp/res/drawable/btn_arrow_left_press.png Binary files differnew file mode 100644 index 0000000..8b72f0a --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_left_press.png diff --git a/fmapp/res/drawable/btn_arrow_left_selected.png b/fmapp/res/drawable/btn_arrow_left_selected.png Binary files differnew file mode 100644 index 0000000..6cd3c9f --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_left_selected.png diff --git a/fmapp/res/drawable/btn_arrow_right.xml b/fmapp/res/drawable/btn_arrow_right.xml new file mode 100644 index 0000000..1e37d0a --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_right.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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_pressed="true" + android:drawable="@drawable/btn_arrow_right_press" /> + + <item android:state_window_focused="true" android:state_focused="true" + android:drawable="@drawable/btn_arrow_right_selected" /> + + <item + android:drawable="@drawable/btn_arrow_right_default" /> + +</selector> diff --git a/fmapp/res/drawable/btn_arrow_right_default.png b/fmapp/res/drawable/btn_arrow_right_default.png Binary files differnew file mode 100644 index 0000000..ae6fd53 --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_right_default.png diff --git a/fmapp/res/drawable/btn_arrow_right_press.png b/fmapp/res/drawable/btn_arrow_right_press.png Binary files differnew file mode 100644 index 0000000..a4ae45b --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_right_press.png diff --git a/fmapp/res/drawable/btn_arrow_right_selected.png b/fmapp/res/drawable/btn_arrow_right_selected.png Binary files differnew file mode 100644 index 0000000..93bcc77 --- /dev/null +++ b/fmapp/res/drawable/btn_arrow_right_selected.png diff --git a/fmapp/res/drawable/ic_btn_forward.png b/fmapp/res/drawable/ic_btn_forward.png Binary files differnew file mode 100644 index 0000000..0936fac --- /dev/null +++ b/fmapp/res/drawable/ic_btn_forward.png diff --git a/fmapp/res/drawable/ic_btn_forward_dis.png b/fmapp/res/drawable/ic_btn_forward_dis.png Binary files differnew file mode 100755 index 0000000..cd9a2f5 --- /dev/null +++ b/fmapp/res/drawable/ic_btn_forward_dis.png diff --git a/fmapp/res/drawable/ic_btn_onoff.png b/fmapp/res/drawable/ic_btn_onoff.png Binary files differnew file mode 100644 index 0000000..4405b43 --- /dev/null +++ b/fmapp/res/drawable/ic_btn_onoff.png diff --git a/fmapp/res/drawable/ic_btn_preset.png b/fmapp/res/drawable/ic_btn_preset.png Binary files differnew file mode 100644 index 0000000..35449b3 --- /dev/null +++ b/fmapp/res/drawable/ic_btn_preset.png diff --git a/fmapp/res/drawable/ic_btn_search.png b/fmapp/res/drawable/ic_btn_search.png Binary files differnew file mode 100644 index 0000000..7eea584 --- /dev/null +++ b/fmapp/res/drawable/ic_btn_search.png diff --git a/fmapp/res/drawable/ic_btn_search_stop.png b/fmapp/res/drawable/ic_btn_search_stop.png Binary files differnew file mode 100644 index 0000000..0b8c4e8 --- /dev/null +++ b/fmapp/res/drawable/ic_btn_search_stop.png diff --git a/fmapp/res/drawable/ic_launcher_fm_tx.png b/fmapp/res/drawable/ic_launcher_fm_tx.png Binary files differnew file mode 100644 index 0000000..c6e41b2 --- /dev/null +++ b/fmapp/res/drawable/ic_launcher_fm_tx.png diff --git a/fmapp/res/drawable/ic_launcher_fmradio.png b/fmapp/res/drawable/ic_launcher_fmradio.png Binary files differnew file mode 100644 index 0000000..2b1250c --- /dev/null +++ b/fmapp/res/drawable/ic_launcher_fmradio.png diff --git a/fmapp/res/drawable/ic_menu_record.png b/fmapp/res/drawable/ic_menu_record.png Binary files differnew file mode 100644 index 0000000..60f7a01 --- /dev/null +++ b/fmapp/res/drawable/ic_menu_record.png diff --git a/fmapp/res/drawable/ic_signal_2.png b/fmapp/res/drawable/ic_signal_2.png Binary files differnew file mode 100644 index 0000000..efe4b5f --- /dev/null +++ b/fmapp/res/drawable/ic_signal_2.png diff --git a/fmapp/res/drawable/ic_silent_mode.png b/fmapp/res/drawable/ic_silent_mode.png Binary files differnew file mode 100644 index 0000000..c89291a --- /dev/null +++ b/fmapp/res/drawable/ic_silent_mode.png diff --git a/fmapp/res/drawable/ic_silent_mode_off.png b/fmapp/res/drawable/ic_silent_mode_off.png Binary files differnew file mode 100644 index 0000000..4748b9e --- /dev/null +++ b/fmapp/res/drawable/ic_silent_mode_off.png diff --git a/fmapp/res/drawable/ic_status_fm_tx.png b/fmapp/res/drawable/ic_status_fm_tx.png Binary files differnew file mode 100644 index 0000000..29383c9 --- /dev/null +++ b/fmapp/res/drawable/ic_status_fm_tx.png diff --git a/fmapp/res/drawable/ic_stereo.png b/fmapp/res/drawable/ic_stereo.png Binary files differnew file mode 100644 index 0000000..b9bfec6 --- /dev/null +++ b/fmapp/res/drawable/ic_stereo.png diff --git a/fmapp/res/drawable/icon.png b/fmapp/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000..7502484 --- /dev/null +++ b/fmapp/res/drawable/icon.png diff --git a/fmapp/res/drawable/image_border_bg_normal.9.png b/fmapp/res/drawable/image_border_bg_normal.9.png Binary files differnew file mode 100644 index 0000000..18e3607 --- /dev/null +++ b/fmapp/res/drawable/image_border_bg_normal.9.png diff --git a/fmapp/res/drawable/preset_page_button.xml b/fmapp/res/drawable/preset_page_button.xml new file mode 100644 index 0000000..f208ae5 --- /dev/null +++ b/fmapp/res/drawable/preset_page_button.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2012,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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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/ic_btn_forward_dis" /> <!-- disabled --> + <item android:state_enabled="true" + android:drawable="@drawable/ic_btn_forward" /> <!-- enabled --> + <item android:drawable="@drawable/ic_btn_forward" /> <!-- default --> + </selector> diff --git a/fmapp/res/drawable/stat_notify_fm.png b/fmapp/res/drawable/stat_notify_fm.png Binary files differnew file mode 100644 index 0000000..16ed9bc --- /dev/null +++ b/fmapp/res/drawable/stat_notify_fm.png diff --git a/fmapp/res/drawable/station_border.xml b/fmapp/res/drawable/station_border.xml new file mode 100644 index 0000000..df1e28a --- /dev/null +++ b/fmapp/res/drawable/station_border.xml @@ -0,0 +1,33 @@ +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <stroke android:width="1dp" android:color="#FFFFFFFF" /> + <padding android:left="1dp" android:top="1dp" + android:right="1dp" android:bottom="1dp" /> + <corners android:radius="1dp" /> +</shape> diff --git a/fmapp/res/layout-800x480/fmradio.xml b/fmapp/res/layout-800x480/fmradio.xml new file mode 100644 index 0000000..b3dbccb --- /dev/null +++ b/fmapp/res/layout-800x480/fmradio.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fmradio" + android:background="@drawable/application_border" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This LinearLayout contains Station Information display and the function buttons --> + + <RelativeLayout android:id="@+id/stationinfo_layout" + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + </RelativeLayout> + + <RelativeLayout android:id="@+id/preset_full_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/stationinfo_layout" + android:layout_alignParentLeft="true"> + + <!-- Layout contains the Next/Previous Presets and Tune buttons and status msg text--> + <include layout="@layout/navigate" /> + </RelativeLayout> + + <RelativeLayout android:id="@+id/on_off_separator_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_below="@id/preset_full_layout"> + + <View android:id="@+id/on_off_separator" + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="#ffffffff"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:layout_centerInParent="true" + android:layout_below="@id/on_off_separator_layout"> + + <ImageButton android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_width="120dp" + android:layout_height="65dp" /> + + <ImageButton android:id="@+id/btn_onoff" + android:src="@drawable/ic_btn_onoff" + android:layout_toRightOf="@id/btn_silent" + android:layout_width="120dp" + android:layout_height="65dp" /> + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout-800x480/fmtags.xml b/fmapp/res/layout-800x480/fmtags.xml new file mode 100644 index 0000000..352320f --- /dev/null +++ b/fmapp/res/layout-800x480/fmtags.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="fill_parent" + android:layout_width="fill_parent"> + + <ListView + android:id="@+id/TagListView" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="30sp"/> + +</RelativeLayout> diff --git a/fmapp/res/layout-800x480/fmtransmitter.xml b/fmapp/res/layout-800x480/fmtransmitter.xml new file mode 100644 index 0000000..1cffca9 --- /dev/null +++ b/fmapp/res/layout-800x480/fmtransmitter.xml @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fmtransmitter" + android:background="@drawable/application_border" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This RelativeLayout contains Station Information display and the function buttons --> + <RelativeLayout android:id="@+id/stationinfo_layout" + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="20dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <RelativeLayout android:id="@+id/transmit_msg_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" > + + <TextView android:id="@+id/transmit_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="15dp" + android:layout_centerInParent="true" + android:layout_alignWithParentIfMissing="true" + android:text="@string/transmit_msg_string"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/frequency_layout" + android:layout_below="@id/transmit_msg_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_alignParentLeft="true" + android:paddingLeft="2dip" + android:visibility="visible" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/btn_arrow_left"/> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="60dip" + android:layout_centerInParent="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/frequency_string"/> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:paddingRight="2dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + <!-- Station Radio Text information display --> + <RelativeLayout android:id="@+id/radio_text_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/frequency_layout"> + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="20dp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dp" + android:paddingRight="2dp" + android:paddingBottom="5dp" + android:layout_marginTop="20dp" + android:layout_marginBottom="10dp" + android:layout_centerInParent="true" + android:text="@string/radio_text_string"/> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/stationinfo_layout"> + + <RelativeLayout android:id="@+id/presets_layout_center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true"> + + <!-- Upper Presets --> + <RelativeLayout android:id="@+id/presets_upper_layout" + android:gravity="top" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="120dp" + android:layout_height="65dp"/> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_1" + android:layout_alignTop="@id/presets_button_1" + android:layout_alignBottom="@id/presets_button_1" + android:layout_width="120dp" + android:layout_height="65dp"/> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_2" + android:layout_alignTop="@id/presets_button_2" + android:layout_alignBottom="@id/presets_button_2" + android:layout_width="120dp" + android:layout_height="65dp"/> + + </RelativeLayout> + + <!-- Lower Presets --> + <RelativeLayout android:id="@+id/presets_lower_layout" + android:layout_below="@id/presets_upper_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" > + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="120dp" + android:layout_height="65dp"/> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_4" + android:layout_alignTop="@id/presets_button_4" + android:layout_alignBottom="@id/presets_button_4" + android:layout_width="120dp" + android:layout_height="65dp"/> + + <Button android:id="@+id/presets_button_6" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_5" + android:layout_alignTop="@id/presets_button_5" + android:layout_alignBottom="@id/presets_button_5" + android:layout_width="120dp" + android:layout_height="65dp"/> + + </RelativeLayout> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/on_off_separator_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_below="@id/presets_layout"> + + <View android:id="@+id/on_off_separator" + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="#ffffffff"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/on_off_separator_layout" + android:layout_marginBottom="20dp"> + + <ImageButton android:id="@+id/btn_onoff" + android:src="@drawable/ic_btn_onoff" + android:layout_below="@id/on_off_separator" + android:layout_centerInParent="true" + android:layout_width="120dp" + android:layout_height="65dp"/> + + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout-800x480/navigate.xml b/fmapp/res/layout-800x480/navigate.xml new file mode 100644 index 0000000..7e60d32 --- /dev/null +++ b/fmapp/res/layout-800x480/navigate.xml @@ -0,0 +1,188 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <RelativeLayout android:id="@+id/message_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:paddingTop="5dp" + android:paddingBottom="5dp"> + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:textSize="28dp" + android:textStyle="bold" + android:gravity="fill" + android:text="@string/default_record" /> + + <Button android:id="@+id/btn_presets_list" + android:text="@string/default_preset_list_name" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="24dp" + android:textStyle="bold" + android:singleLine="true" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="true" + android:layout_gravity="center" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:layout_marginBottom="10dp" + android:gravity="fill" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@+id/btn_presets_list" + android:textSize="28dp" + android:textStyle="bold" + android:gravity="fill" + android:text="@string/default_sleep" /> + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/message_row" + android:layout_centerInParent="true"> + + <!-- Upper Presets --> + <RelativeLayout android:id="@+id/presets_upper_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="top"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:gravity="fill" + android:layout_width="120dp" + android:layout_height="65dp" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_1" + android:layout_alignTop="@id/presets_button_1" + android:layout_alignBottom="@id/presets_button_1" + android:gravity="fill" + android:layout_width="120dp" + android:layout_height="65dp" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_2" + android:layout_alignTop="@id/presets_button_2" + android:layout_alignBottom="@id/presets_button_2" + android:layout_width="120dp" + android:gravity="fill" + android:layout_height="65dp" /> + </RelativeLayout> + +<!-- Lower Presets --> + <RelativeLayout android:id="@+id/presets_lower_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/presets_upper_layout" + android:layout_alignLeft="@id/presets_upper_layout" + android:layout_alignRight="@id/presets_upper_layout"> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="120dp" + android:gravity="fill" + android:layout_alignLeft="@id/presets_button_1" + android:layout_alignRight="@id/presets_button_1" + android:layout_height="65dp" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_4" + android:layout_alignTop="@id/presets_button_4" + android:layout_alignBottom="@id/presets_button_4" + android:layout_alignLeft="@id/presets_button_2" + android:layout_alignRight="@id/presets_button_2" + android:gravity="fill" + android:layout_width="120dp" + android:layout_height="65dp" /> + + <ImageButton android:id="@+id/btn_preset_page" + android:src="@drawable/ic_btn_forward" + android:layout_toRightOf="@id/presets_button_5" + android:layout_alignTop="@id/presets_button_5" + android:layout_alignBottom="@id/presets_button_5" + android:layout_alignLeft="@id/presets_button_3" + android:layout_alignRight="@id/presets_button_3" + android:gravity="fill" + android:layout_width="120dp" + android:layout_height="65dp" /> + </RelativeLayout> + </RelativeLayout> + + <RelativeLayout android:id="@+id/scanning_stoplayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:gravity="center"> + + <Button android:id="@+id/btn_scanning_stop" + android:clickable="true" + android:text="@string/stop_scanning" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="32dip" + android:singleLine="true" + android:padding="20dip" + android:gravity="fill" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + </RelativeLayout> + +</merge> diff --git a/fmapp/res/layout-800x480/station_info.xml b/fmapp/res/layout-800x480/station_info.xml new file mode 100644 index 0000000..475d42a --- /dev/null +++ b/fmapp/res/layout-800x480/station_info.xml @@ -0,0 +1,182 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> + +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + +<RelativeLayout android:id="@+id/station_info" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:gravity="top"> + + <RelativeLayout android:id="@+id/top_most" + android:layout_width="fill_parent" + android:gravity="top" + android:layout_height="wrap_content"> + + <ImageView android:id="@+id/signal_level" + android:src="@drawable/ic_signal_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string" /> + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="16dip" + android:text="@string/default_audio" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/station_id_row" + android:layout_width="fill_parent" + android:layout_below="@id/top_most" + android:layout_height="wrap_content"> + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="16dip" + android:layout_centerInParent="true" + android:text="@string/station_id_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_below="@id/station_id_row" + android:layout_height="wrap_content" + android:gravity="center" > + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="50dip" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:text="@string/frequency_string"/> + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="100dip" + android:paddingLeft="2dip" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_left" /> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="100dip" + android:paddingRight="2dip" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right" /> + </RelativeLayout> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignLeft="@id/station_frequency_row" + android:layout_alignRight="@id/station_frequency_row" + android:layout_below="@id/station_frequency_row" > + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="16dip" + android:layout_centerInParent="true" + android:text="@string/pty_string" /> + </RelativeLayout> + + <RelativeLayout android:id="@+id/third_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:layout_alignLeft="@id/second_layout" + android:layout_alignRight="@id/second_layout" + android:layout_below="@id/second_layout" > + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:gravity="center" + android:text="@string/radio_text_string" /> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:layout_below="@id/radio_text_tv" + android:gravity="center_horizontal" + android:text="@string/eradio_text_string"/> + </RelativeLayout> + <!-- Station Radio Text information display --> + </RelativeLayout> +</merge> diff --git a/fmapp/res/layout-land-finger-800x480/fmradio.xml b/fmapp/res/layout-land-finger-800x480/fmradio.xml new file mode 100644 index 0000000..f2289fd --- /dev/null +++ b/fmapp/res/layout-land-finger-800x480/fmradio.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <!-- This LinearLayout contains Station Information display and the function buttons --> + <RelativeLayout android:id="@+id/station_info_wrapper_layout" + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <include layout="@layout/station_info" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/navigate_wrapper_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/station_info_wrapper_layout"> + + <!-- Layout contains the Next/Previous Presets and Tune buttons and status msg text--> + <include layout="@layout/navigate"/> + + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout-land-finger-800x480/fmtransmitter.xml b/fmapp/res/layout-land-finger-800x480/fmtransmitter.xml new file mode 100644 index 0000000..3d6014c --- /dev/null +++ b/fmapp/res/layout-land-finger-800x480/fmtransmitter.xml @@ -0,0 +1,214 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" > + + <!-- + This LinearLayout contains Station Information display and the + function buttons + --> + <RelativeLayout android:id="@+id/station_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:weightSum="1.0"> + + <!-- This included layout contains Station Information to display --> + <RelativeLayout android:id="@+id/stationinfo_layout" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_marginTop="5dp" + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" > + + <RelativeLayout android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <RelativeLayout android:id="@+id/transmit_msg_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="5dp" + android:paddingBottom="5dp" > + + <TextView android:id="@+id/transmit_msg_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="15dp" + android:text="@string/transmit_msg_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/frequency_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/transmit_msg_layout" > + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="10dp" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left" /> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="56dp" + android:text="@string/frequency_string" /> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="10dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right" /> + + </RelativeLayout> + + <!-- Station Radio Text information display --> + <RelativeLayout android:id="@+id/radio_text_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/frequency_layout" > + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_centerInParent="true" + android:text="@string/radio_text_string" /> + + </RelativeLayout> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/stationinfo_layout" + android:layout_marginTop="15dp" + android:layout_marginBottom="10dp" > + + <RelativeLayout android:layout_width="wrap_content" + android:layout_centerInParent="true" + android:layout_height="wrap_content" > + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_marginLeft="10dp" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_6" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_5" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_below="@id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" + android:layout_marginBottom="2dp"> + + <!-- On-Off button --> + <ImageButton android:id="@+id/btn_onoff" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_btn_onoff" + android:layout_marginTop="5dp" + android:layout_centerInParent="true" + android:layout_marginBottom="5dp" /> + + </RelativeLayout> + + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout-land-finger-800x480/navigate.xml b/fmapp/res/layout-land-finger-800x480/navigate.xml new file mode 100644 index 0000000..fab276a --- /dev/null +++ b/fmapp/res/layout-land-finger-800x480/navigate.xml @@ -0,0 +1,152 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <RelativeLayout android:id="@+id/message_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:textSize="16dip" + android:textStyle="bold" + android:text="@string/default_record"/> + + <ImageButton android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/btn_presets_list"/> + + <Button android:id="@+id/btn_presets_list" + android:text= "@string/default_preset_list_name" + android:textSize="24dip" + android:singleLine="true" + android:textStyle="bold" + android:layout_centerHorizontal="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageButton android:id="@+id/btn_onoff" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/btn_presets_list" + android:src="@drawable/ic_btn_onoff"/> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:textSize="16dip" + android:textStyle="bold" + androidtext="@string/default_sleep"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_upper_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/message_row" + android:layout_marginTop="1dip" + android:layout_marginBottom="5dip"> + + <RelativeLayout android:id="@+id/presets_upper_layout1" + android:layout_width="wrap_content" + android:layout_centerInParent="true" + android:layout_height="wrap_content"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_marginLeft="10dip" + android:layout_marginRight="25dip" + android:textSize="24dip" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:layout_marginRight="25dip" + android:textSize="24dip" + android:singleLine="true" + android:layout_alignTop="@id/presets_button_1" + android:layout_alignBottom="@id/presets_button_1" + android:layout_toRightOf="@id/presets_button_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:layout_marginRight="25dip" + android:textSize="24dip" + android:singleLine="true" + android:layout_alignTop="@id/presets_button_2" + android:layout_alignBottom="@id/presets_button_2" + android:layout_toRightOf="@id/presets_button_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:layout_marginRight="25dip" + android:textSize="24dip" + android:singleLine="true" + android:layout_alignTop="@id/presets_button_3" + android:layout_alignBottom="@id/presets_button_3" + android:layout_toRightOf="@id/presets_button_3" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:layout_marginRight="25dip" + android:textSize="24dip" + android:singleLine="true" + android:layout_alignTop="@id/presets_button_4" + android:layout_alignBottom="@id/presets_button_4" + android:layout_toRightOf="@id/presets_button_4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageButton android:id="@+id/btn_preset_page" + android:src="@drawable/ic_btn_forward" + android:layout_marginRight="10dip" + android:layout_alignTop="@id/presets_button_5" + android:layout_alignBottom="@id/presets_button_5" + android:layout_toRightOf="@id/presets_button_5" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + </RelativeLayout> + </RelativeLayout> +</merge> diff --git a/fmapp/res/layout-land-finger-800x480/station_info.xml b/fmapp/res/layout-land-finger-800x480/station_info.xml new file mode 100644 index 0000000..e2304d5 --- /dev/null +++ b/fmapp/res/layout-land-finger-800x480/station_info.xml @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <RelativeLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="1dip" + android:layout_marginLeft="1dip" + android:layout_marginRight="1dip"> + + <RelativeLayout android:id="@+id/top_most" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="5dip" + android:layout_alignParentTop="true" + android:layout_marginRight="5dip"> + + <ImageView android:id="@+id/signal_level" + android:src="@drawable/ic_signal_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="16dip" + android:text="@string/default_audio"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/station_id_row" + android:layout_below="@id/top_most" + android:layout_width="fill_parent" + android:layout_height="wrap_content" > + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:layout_alignParentTop="true" + android:textSize="16dip" + android:paddingRight="5dip" + android:paddingLeft="5dip" + android:text="@string/station_id_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/frequency_layout" + android:layout_width="fill_parent" + android:layout_below="@id/station_id_row" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left"/> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="50dip" + android:text="@string/frequency_string" /> + + <ImageView + android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="2dip" + android:paddingRight="10dip" + android:layout_alignParentRight="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/pty_string_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/frequency_layout"> + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="16dip" + android:text="@string/pty_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/radio_text_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/pty_string_row"> + + <!-- Station Radio Text information display --> + <TextView android:id="@+id/radio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dp" + android:singleLine="true" + android:textStyle="bold" + android:gravity="center_horizontal" + android:text="@string/radio_text_string"/> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:gravity="center_horizontal" + android:layout_below ="@id/radio_text_tv" + android:text="@string/eradio_text_string"/> + + </RelativeLayout> + </RelativeLayout> +</merge> diff --git a/fmapp/res/layout-land-finger/fmradio.xml b/fmapp/res/layout-land-finger/fmradio.xml new file mode 100644 index 0000000..e55c239 --- /dev/null +++ b/fmapp/res/layout-land-finger/fmradio.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This LinearLayout contains Station Information display and the function buttons --> + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="horizontal"> + + <LinearLayout + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:layout_weight="1" + android:orientation="vertical" + android:gravity="top|fill_vertical"> + + <RelativeLayout android:id="@+id/station_layout" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + <ImageView + android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:paddingLeft="10dip" + android:paddingRight="2dip" + android:layout_alignParentLeft="true" + android:layout_centerVertical="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left"/> + + <ImageView + android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:paddingLeft="2dip" + android:paddingRight="10dip" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + </LinearLayout> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_gravity="center" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:layout_marginBottom="2dip"> + + <!-- On-Off button --> + <ImageButton android:id="@+id/btn_onoff" + android:layout_width="70dip" + android:layout_height="60dip" + android:src="@drawable/ic_btn_onoff" + android:layout_marginBottom="15dp"/> + + <ImageButton android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_width="70dip" + android:layout_height="60dip" + android:layout_marginBottom="15dip"/> + + </LinearLayout> + + </LinearLayout> + + <!-- Layout contains the Next/Previous Presets and Tune buttons and status msg text--> + <include layout="@layout/navigate" /> + +</LinearLayout> diff --git a/fmapp/res/layout-land-finger/fmtransmitter.xml b/fmapp/res/layout-land-finger/fmtransmitter.xml new file mode 100644 index 0000000..f9a9853 --- /dev/null +++ b/fmapp/res/layout-land-finger/fmtransmitter.xml @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. + */ + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <!-- + This LinearLayout contains Station Information display and the + function buttons + --> + <RelativeLayout android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="1" > + + <RelativeLayout android:id="@+id/station_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <!-- This included layout contains Station Information to display --> + <RelativeLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" > + + <RelativeLayout android:id="@+id/transmit_msg_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="20dp" + android:paddingBottom="20dp" > + + <TextView android:id="@+id/transmit_msg_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="15dp" + android:text="@string/transmit_msg_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/frequency_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/transmit_msg_layout" > + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="10dp" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left" /> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="56dp" + android:text="@string/frequency_string" /> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="10dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right" /> + + </RelativeLayout> + + <!-- Station Radio Text information display --> + <RelativeLayout android:id="@+id/radio_text_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/frequency_layout" > + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_centerInParent="true" + android:text="@string/radio_text_string" /> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/stationinfo_layout" + android:layout_marginTop="15dp" + android:layout_marginBottom="10dp" > + + <RelativeLayout android:layout_width="wrap_content" + android:layout_centerInParent="true" + android:layout_height="wrap_content" > + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_marginLeft="10dp" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_4" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <Button android:id="@+id/presets_button_6" + android:text="@string/default_blank" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_5" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_below="@id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="5dp" + android:layout_marginRight="5dp" + android:layout_marginBottom="2dp"> + + <!-- On-Off button --> + <ImageButton android:id="@+id/btn_onoff" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_btn_onoff" + android:layout_marginTop="5dp" + android:layout_centerInParent="true" + android:layout_marginBottom="5dp" /> + + </RelativeLayout> + + </RelativeLayout> + + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout-land-finger/navigate.xml b/fmapp/res/layout-land-finger/navigate.xml new file mode 100644 index 0000000..269793b --- /dev/null +++ b/fmapp/res/layout-land-finger/navigate.xml @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <View + android:layout_width="fill_parent" + android:layout_height="1dip" + android:background="#ffffffff" /> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dip" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:layout_marginBottom="2dip" + android:orientation="vertical" + android:gravity="bottom" + > + <RelativeLayout android:id="@+id/message_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + > + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_alignParentLeft="true" + android:textSize="14dip" + android:textStyle="bold" + android:text="@string/default_record" + /> + + <Button android:id="@+id/btn_presets_list" + android:text= "@string/default_preset_list_name" + android:textStyle="bold" + android:singleLine="true" + android:layout_gravity="center" + android:layout_centerHorizontal="true" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="140dip" + android:layout_height="45dip" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@+id/btn_presets_list" + android:layout_gravity="center_vertical" + android:textSize="14dip" + android:textStyle="bold" + android:text="@string/default_sleep" + /> + </RelativeLayout> + </LinearLayout> + + <LinearLayout android:id="@+id/presets_upper_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_marginBottom="5dip" + android:gravity="center_horizontal"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_width="80dip" + android:layout_height="50dip" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:layout_width="80dp" + android:layout_height="50dp" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:layout_width="80dip" + android:layout_height="50dip" + android:visibility="visible" /> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:layout_width="80dip" + android:layout_height="50dip" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:layout_width="80dip" + android:layout_height="50dip" /> + + <ImageButton android:id="@+id/btn_preset_page" + android:src="@drawable/ic_btn_forward" + android:layout_marginRight="2dp" + android:layout_width="80dip" + android:layout_height="50dip" + android:visibility="visible" /> + </LinearLayout> +</merge> diff --git a/fmapp/res/layout-land-finger/station_info.xml b/fmapp/res/layout-land-finger/station_info.xml new file mode 100644 index 0000000..48ce642 --- /dev/null +++ b/fmapp/res/layout-land-finger/station_info.xml @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <LinearLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dip" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:orientation="vertical" + android:gravity="fill_vertical"> + + <RelativeLayout android:id="@+id/top_most" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip"> + + <ImageView android:id="@+id/signal_level" + android:src="@drawable/ic_signal_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="20dip" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="14dip" + android:text="@string/default_audio"/> + + </RelativeLayout> + + <LinearLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="25dip" + android:paddingBottom="25dip" + android:gravity="fill_vertical|center"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:textSize="56dip" + android:text="@string/frequency_string" /> + + <RelativeLayout android:id="@+id/second_row" + android:layout_width="wrap_content" + android:layout_height="fill_parent" + android:orientation="vertical" + android:layout_toRightOf="@id/prog_frequency_tv"> + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:textSize="14dip" + android:paddingRight="5dip" + android:paddingBottom="2dip" + android:paddingLeft="5dip" + android:text="@string/station_id_string" /> + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentBottom="true" + android:textSize="16dip" + android:paddingTop="5dip" + android:paddingRight="5dip" + android:paddingLeft="5dip" + android:text="@string/pty_string" /> + </RelativeLayout> + </LinearLayout> + + <!-- Station Radio Text information display --> + <TextView android:id="@+id/radio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="16dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingBottom="2dip" + android:paddingLeft="5dip" + android:paddingRight="5dip" + android:gravity="center_horizontal" + android:text="@string/radio_text_string"/> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="20dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:gravity="center_horizontal" + android:layout_below="@id/radio_text_tv" + android:text="@string/eradio_text_string"/> + + </LinearLayout> +</merge> diff --git a/fmapp/res/layout/alert_dialog_list.xml b/fmapp/res/layout/alert_dialog_list.xml new file mode 100644 index 0000000..5d20985 --- /dev/null +++ b/fmapp/res/layout/alert_dialog_list.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <ListView android:id="@+id/list" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + </ListView> + +</LinearLayout> diff --git a/fmapp/res/layout/alert_dialog_text_entry.xml b/fmapp/res/layout/alert_dialog_text_entry.xml new file mode 100644 index 0000000..828e914 --- /dev/null +++ b/fmapp/res/layout/alert_dialog_text_entry.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <EditText + android:id="@+id/list_edit" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:layout_marginLeft="20dp" + android:layout_marginRight="20dp" + android:scrollHorizontally="true" + android:hint="Enter a name" + android:singleLine="true" + android:inputType="text" + android:autoText="false" + android:capitalize="none" + android:gravity="fill_horizontal" + android:textAppearance="?android:attr/textAppearanceMedium" android:saveEnabled="false"/> + + +</LinearLayout> diff --git a/fmapp/res/layout/fmradio.xml b/fmapp/res/layout/fmradio.xml new file mode 100644 index 0000000..e67bde1 --- /dev/null +++ b/fmapp/res/layout/fmradio.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fmradio" + android:background="@drawable/application_border" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <!-- This LinearLayout contains Station Information display and the function buttons --> + <LinearLayout android:id="@+id/stationinfo_layout" + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dip" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + </LinearLayout> + + <LinearLayout android:id="@+id/preset_full_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_alignParentLeft="true" + android:layout_below="@+id/stationinfo_layout" + android:layout_weight="1" + android:gravity="center"> + + <!-- Layout contains the Next/Previous Presets and Tune buttons and status msg text--> + <include layout="@layout/navigate" /> + + </LinearLayout> + + <LinearLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_marginTop="2dip" + android:layout_marginBottom="2dip" + android:layout_alignParentLeft="true" + android:layout_alignParentBottom="true" + android:layout_weight="0" + android:gravity="center_horizontal"> + + <ImageButton android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_marginLeft="10dip" + android:layout_marginRight="20dip" + android:layout_width="100dip" + android:layout_height="50dip"/> + + <ImageButton android:id="@+id/btn_onoff" + android:src="@drawable/ic_btn_onoff" + android:layout_marginLeft="20dip" + android:layout_marginRight="10dip" + android:layout_width="100dip" + android:layout_height="50dip"/> + + </LinearLayout> + + <View + android:layout_width="fill_parent" + android:layout_height="1dip" + android:background="#ffffffff" + android:layout_above="@+id/layout_on_off"/> + +</RelativeLayout> diff --git a/fmapp/res/layout/fmstats.xml b/fmapp/res/layout/fmstats.xml new file mode 100644 index 0000000..2501a51 --- /dev/null +++ b/fmapp/res/layout/fmstats.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2011-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> + +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:scrollbars="vertical" + android:layout_height="fill_parent" + android:layout_width="fill_parent"> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <Spinner + android:id="@+id/spinner" + android:layout_width="fill_parent" + android:layout_height="75dp" + android:prompt="@string/test_prompt" + android:textSize="25sp"/> + + <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="100dp"> + + <Button + android:id= "@+id/Runbutton" + android:text="@string/test_run" + android:layout_width="wrap_content" + android:layout_height="100dp" + android:textSize="25sp" + android:visibility="gone"/> + + <ProgressBar android:id="@+id/progressbar" + android:layout_width="100dp" + android:layout_height="100dp" + android:layout_toRightOf="@id/Runbutton" + android:visibility="gone"/> + + <TextView + android:id="@+id/label" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="15sp" + android:text="@string/enter_rssi" + android:visibility="gone"/> + + <EditText + android:id="@+id/txtbox1" + android:layout_width="100dp" + android:layout_height="50dp" + android:text="@string/type_rd" + android:textSize="15sp" + android:layout_alignParentRight="true" + android:visibility="gone"/> + + <Button + android:id= "@+id/Setbutton" + android:text="@string/set_rmmsi_delta" + android:layout_width="wrap_content" + android:layout_height="100dp" + android:layout_below="@id/txtbox1" + android:textSize="15sp" + android:visibility="gone"/> + + <Button + android:id="@+id/SearchMpxDcc" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/search_algo_mpx" + android:visibility="gone"/> + + <Button + android:id="@+id/SearchSinrInt" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/search_algo_sinrint" + android:layout_below="@id/SearchMpxDcc" + android:visibility="gone"/> + + </RelativeLayout> + + <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/maintable" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:scrollbars="vertical"> + + <TableRow + android:id="@+id/mainrow"> + </TableRow> + + </TableLayout> + + </LinearLayout> + +</ScrollView> diff --git a/fmapp/res/layout/fmtags.xml b/fmapp/res/layout/fmtags.xml new file mode 100644 index 0000000..352320f --- /dev/null +++ b/fmapp/res/layout/fmtags.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012, 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 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="fill_parent" + android:layout_width="fill_parent"> + + <ListView + android:id="@+id/TagListView" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textSize="30sp"/> + +</RelativeLayout> diff --git a/fmapp/res/layout/fmtransmitter.xml b/fmapp/res/layout/fmtransmitter.xml new file mode 100644 index 0000000..f9e7b08 --- /dev/null +++ b/fmapp/res/layout/fmtransmitter.xml @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fmtransmitter" + android:background="@drawable/application_border" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <!-- This RelativeLayout contains Station Information display and the function buttons --> + <RelativeLayout android:id="@+id/stationinfo_layout" + android:background="@drawable/station_border" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="5dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true"> + + <RelativeLayout android:id="@+id/transmit_msg_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" > + + <TextView android:id="@+id/transmit_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="15dp" + android:layout_centerInParent="true" + android:text="@string/transmit_msg_string"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/frequency_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_below="@id/transmit_msg_layout" + android:layout_marginBottom="20dp"> + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="2dp" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_left"/> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="60dp" + android:layout_centerVertical="true" + android:layout_toRightOf="@id/btn_back" + android:text="@string/frequency_string"/> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="2dp" + android:layout_toRightOf="@id/prog_frequency_tv" + android:layout_alignParentRight="true" + android:visibility="visible" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + <!-- Station Radio Text information display --> + <RelativeLayout android:id="@+id/radio_text_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/frequency_layout"> + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="20dp" + android:singleLine="true" + android:textStyle="bold" + android:layout_centerInParent="true" + android:text="@string/radio_text_string"/> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/presets_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_alignParentLeft="true" + android:layout_below="@+id/stationinfo_layout" + android:layout_weight="1" + android:layout_marginTop = "15dp" + android:layout_marginBottom="15dp" + android:gravity="center"> + + <!-- Upper Presets --> + <RelativeLayout android:id="@+id/presets_upper_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="90dp" + android:layout_height="55dp"/> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_1" + android:layout_alignTop="@id/presets_button_1" + android:layout_alignBottom="@id/presets_button_1" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_2" + android:layout_alignTop="@id/presets_button_2" + android:layout_alignBottom="@id/presets_button_2" + android:layout_width="90dp" + android:layout_height="55dp"/> + + </RelativeLayout> + + <!-- Lower Presets --> + <RelativeLayout android:id="@+id/presets_lower_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/presets_upper_layout" + android:layout_centerVertical="true"> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_width="90dp" + android:layout_height="55dp"/> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_4" + android:layout_alignTop="@id/presets_button_4" + android:layout_alignBottom="@id/presets_button_4" + android:layout_width="90dp" + android:layout_height="55dp"/> + + <Button android:id="@+id/presets_button_6" + android:text="@string/default_blank" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="24dp" + android:singleLine="true" + android:layout_toRightOf="@id/presets_button_5" + android:layout_alignTop="@id/presets_button_5" + android:layout_alignBottom="@id/presets_button_5" + android:layout_width="90dp" + android:layout_height="55dp"/> + + </RelativeLayout> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/presets_layout" + android:layout_marginTop="20dp" + android:layout_marginBottom="20dp" + android:layout_alignParentLeft="true" + android:layout_alignParentBottom="true" + android:layout_weight="0"> + + <View android:id="@+id/on_off_separator" + android:layout_width="fill_parent" + android:layout_height="1dp" + android:background="#ffffffff"/> + + <ImageButton android:id="@+id/btn_onoff" + android:src="@drawable/ic_btn_onoff" + android:layout_marginLeft="25dp" + android:layout_marginRight="25dp" + android:layout_below="@id/on_off_separator" + android:layout_centerVertical="true" + android:layout_width="110dp" + android:layout_height="65dp"/> + + </RelativeLayout> + +</RelativeLayout> diff --git a/fmapp/res/layout/frequency_picker.xml b/fmapp/res/layout/frequency_picker.xml new file mode 100644 index 0000000..2d89a62 --- /dev/null +++ b/fmapp/res/layout/frequency_picker.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <!-- MHz --> + <android.widget.NumberPicker + android:id="@+id/mhz" + android:layout_width="80dp" + android:layout_height="wrap_content" + android:layout_marginLeft="1dp" + android:layout_marginRight="1dp" + android:focusable="true" + android:focusableInTouchMode="true" /> + + <!-- KHz --> + <android.widget.NumberPicker + android:id="@+id/khz" + android:layout_width="80dp" + android:layout_height="wrap_content" + android:layout_marginLeft="1dp" + android:layout_marginRight="1dp" + android:focusable="true" + android:focusableInTouchMode="true" + /> +</LinearLayout> diff --git a/fmapp/res/layout/frequency_picker_dialog.xml b/fmapp/res/layout/frequency_picker_dialog.xml new file mode 100644 index 0000000..89a6e4f --- /dev/null +++ b/fmapp/res/layout/frequency_picker_dialog.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<com.codeaurora.utils.FrequencyPicker xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/frequencyPicker" + android:padding="5dp" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> diff --git a/fmapp/res/layout/navigate.xml b/fmapp/res/layout/navigate.xml new file mode 100644 index 0000000..30cff7e --- /dev/null +++ b/fmapp/res/layout/navigate.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <RelativeLayout android:id="@+id/message_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="5dip" + android:orientation="horizontal"> + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:textSize="14dip" + android:textStyle="bold" + android:text="@string/default_record"/> + + <Button android:id="@+id/btn_presets_list" + android:text="@string/default_preset_list_name" + android:textStyle="bold" + android:singleLine="true" + android:layout_centerHorizontal="true" + android:layout_alignParentTop="true" + android:layout_gravity="center" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="140dip" + android:layout_height="45dip" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@+id/btn_presets_list" + android:textSize="14dip" + android:textStyle="bold" + android:text="@string/default_sleep" /> + + </RelativeLayout> + + <LinearLayout android:id="@+id/presets_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerHorizontal="true" + android:orientation="vertical"> + + <!-- Upper Presets --> + <LinearLayout android:id="@+id/presets_upper_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_marginBottom="5dip" + android:gravity="center_horizontal"> + + <Button android:id="@+id/presets_button_1" + android:text="@string/default_station" + android:layout_marginLeft="10dip" + android:layout_width="90dip" + android:layout_height="55dip" /> + + <Button android:id="@+id/presets_button_2" + android:text="@string/default_station" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="90dip" + android:layout_height="55dip" /> + + <Button android:id="@+id/presets_button_3" + android:text="@string/default_station" + android:layout_marginRight="10dip" + android:layout_width="90dip" + android:layout_height="55dip" + android:visibility="visible" /> + + </LinearLayout> + + <!-- Lower Presets --> + <LinearLayout android:id="@+id/presets_lower_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_gravity="center" + android:layout_marginBottom="5dip" + android:gravity="center_horizontal"> + + <Button android:id="@+id/presets_button_4" + android:text="@string/default_blank" + android:layout_marginLeft="10dip" + android:layout_width="90dip" + android:layout_height="55dip" /> + + <Button android:id="@+id/presets_button_5" + android:text="@string/default_blank" + android:layout_marginLeft="10dip" + android:layout_marginRight="10dip" + android:layout_width="90dip" + android:layout_height="55dip" /> + + <ImageButton android:id="@+id/btn_preset_page" + android:src="@drawable/preset_page_button" + android:layout_marginRight="10dip" + android:layout_width="90dip" + android:layout_height="55dip" + android:visibility="visible" /> + + </LinearLayout> + + </LinearLayout> + + <LinearLayout android:id="@+id/scanning_stoplayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:visibility="gone" + android:gravity="center"> + + <Button android:id="@+id/btn_scanning_stop" + android:clickable="true" + android:text="@string/stop_scanning" + android:singleLine="true" + android:padding="20dip" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + + </LinearLayout> + +</merge> diff --git a/fmapp/res/layout/station_info.xml b/fmapp/res/layout/station_info.xml new file mode 100644 index 0000000..876026a --- /dev/null +++ b/fmapp/res/layout/station_info.xml @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> +<merge xmlns:android="http://schemas.android.com/apk/res/android"> + + <LinearLayout android:id="@+id/station_info" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_weight="0" + android:orientation="vertical" + android:gravity="top"> + + <RelativeLayout android:id="@+id/top_most" + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + + <ImageView android:id="@+id/signal_level" + android:src="@drawable/ic_signal_2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="20dip" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="5dip" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="14dip" + android:text="@string/default_audio"/> + + </RelativeLayout> + + <LinearLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="5dip" + android:paddingBottom="5dip" + android:layout_alignWithParentIfMissing="true" + android:gravity="center"> + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14dip" + android:paddingTop="2dip" + android:paddingRight="2dip" + android:paddingBottom="2dip" + android:paddingLeft="2dip" + android:text="@string/station_id_string" /> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dip" + android:layout_marginBottom="2dip" + android:layout_marginLeft="2dip" + android:layout_marginRight="2dip"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="64dip" + android:layout_centerHorizontal="true" + android:text="@string/frequency_string"/> + + <ImageView android:id="@+id/btn_back" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="100dip" + android:paddingLeft="2dip" + android:layout_alignParentLeft="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_left"/> + + <ImageView android:id="@+id/btn_forward" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="100dip" + android:paddingRight="2dip" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14dip" + android:paddingTop="2dip" + android:paddingRight="2dip" + android:paddingBottom="2dip" + android:paddingLeft="2dip" + android:text="@string/pty_string" /> + + </LinearLayout> + + <!-- Station Radio Text information display --> + <TextView android:id="@+id/radio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="20dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:gravity="center_horizontal" + android:text="@string/radio_text_string"/> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="20dip" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dip" + android:paddingRight="2dip" + android:paddingBottom="5dip" + android:gravity="center_horizontal" + android:text="@string/eradio_text_string"/> + </LinearLayout> + +</merge> diff --git a/fmapp/res/layout/statusbar.xml b/fmapp/res/layout/statusbar.xml new file mode 100644 index 0000000..b8a8189 --- /dev/null +++ b/fmapp/res/layout/statusbar.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <ImageView android:id="@+id/icon" + android:padding="4dp" + android:gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + </ImageView> + + <LinearLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <TextView android:id="@+id/frequency" + android:textAppearance="?android:attr/textAppearanceMediumInverse" + android:focusable="true" + android:ellipsize="marquee" + android:singleLine="true" + android:layout_gravity="left" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + </LinearLayout> +</LinearLayout> diff --git a/fmapp/res/values/arrays.xml b/fmapp/res/values/arrays.xml new file mode 100644 index 0000000..d7904be --- /dev/null +++ b/fmapp/res/values/arrays.xml @@ -0,0 +1,371 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2011-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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> + + <string-array name="ster_mon_entries"> + <item>Stereo</item> + <item>Mono</item> + </string-array> + + <string-array name="ster_mon_values"> + <item>0</item> + <item>1</item> + </string-array> + + <string-array name="record_durations_entries"> + <item>5 minutes</item> + <item>15 minutes</item> + <item>30 minutes</item> + <item>Until stopped</item> + </string-array> + + + <string-array name="record_duration_values"> + <item>5</item> + <item>15</item> + <item>30</item> + <item>-1</item> + </string-array> + + <string-array name="sleep_duration_values"> + <item>15 minutes</item> + <item>30 minutes</item> + <item>45 minutes</item> + <item>1 Hour</item> + </string-array> + + <!-- This array should be in seconds and should match the values above --> + <string-array name="sleep_duration_values_seconds"> + <item>900</item> + <item>1800</item> + <item>2700</item> + <item>3600</item> + </string-array> + + <string-array name="search_category_rbds_entries"> + <item>All Stations</item> + <item>Adult Hits</item> + <item>Classical</item> + <item>Classic Rock</item> + <item>College</item> + <item>Country</item> + <item>Emergency</item> + <item>Emergency Test</item> + <item>Foreign Language</item> + <item>Information</item> + <item>Jazz</item> + <item>News</item> + <item>Nostalgia</item> + <item>Oldies</item> + <item>Personality</item> + <item>Public</item> + <item>Religious Music</item> + <item>Religious Talk</item> + <item>Rhythm and Blues</item> + <item>Rock</item> + <item>Soft</item> + <item>Soft Rock</item> + <item>Soft Rhythm and Blues</item> + <item>Sports</item> + <item>Talk</item> + <item>Top 40</item> + <item>Weather</item> + </string-array> + + <string-array name="search_category_rbds_values"> + <item>0</item> + <item>7</item> + <item>15</item> + <item>6</item> + <item>23</item> + <item>10</item> + <item>31</item> + <item>30</item> + <item>18</item> + <item>2</item> + <item>14</item> + <item>1</item> + <item>13</item> + <item>11</item> + <item>21</item> + <item>22</item> + <item>19</item> + <item>20</item> + <item>16</item> + <item>5</item> + <item>12</item> + <item>8</item> + <item>17</item> + <item>3</item> + <item>4</item> + <item>9</item> + <item>29</item> + </string-array> + + + <string-array name="search_category_rds_entries"> + <item>All Stations</item> + <item>Culture</item> + <item>Current Affairs</item> + <item>Children programs</item> + <item>Country Music</item> + <item>Documentary</item> + <item>Drama</item> + <item>Easy Listening Music</item> + <item>Education</item> + <item>Emergency</item> + <item>Emergency Test</item> + <item>Finance</item> + <item>Folk Music</item> + <item>Information</item> + <item>Jazz Music</item> + <item>Light classical</item> + <item>Leisure</item> + <item>News</item> + <item>National Music</item> + <item>Other Music</item> + <item>Oldies Music</item> + <item>Phone In</item> + <item>Pop Music</item> + <item>Religion</item> + <item>Rock Music</item> + <item>Science</item> + <item>Serious classical</item> + <item>Social Affairs</item> + <item>Sport</item> + <item>Travel</item> + <item>Varied</item> + <item>Weather</item> + </string-array> + + <string-array name="search_category_rds_values"> + <item>0</item> + <item>7</item> + <item>2</item> + <item>18</item> + <item>25</item> + <item>29</item> + <item>6</item> + <item>12</item> + <item>5</item> + <item>31</item> + <item>30</item> + <item>17</item> + <item>28</item> + <item>3</item> + <item>24</item> + <item>13</item> + <item>23</item> + <item>1</item> + <item>26</item> + <item>15</item> + <item>27</item> + <item>21</item> + <item>10</item> + <item>20</item> + <item>11</item> + <item>8</item> + <item>14</item> + <item>19</item> + <item>4</item> + <item>22</item> + <item>9</item> + <item>16</item> + </string-array> + + <string-array name="presetlist_edit_category"> + <item>Rename</item> + <item>Auto-Select</item> + <item>Delete</item> + </string-array> + + + <string-array name="regional_band_entries"> + <item>North America</item> + <item>Europe</item> + <item>Japan</item> + <item>Japan (Wide)</item> + <item>Australia</item> + <item>Austria</item> + <item>Belgium</item> + <item>Brazil</item> + <item>China</item> + <item>Czech</item> + <item>Denmark</item> + <item>Finland</item> + <item>France</item> + <item>Germany</item> + <item>Greece</item> + <item>Hong Kong</item> + <item>India</item> + <item>Ireland</item> + <item>Italy</item> + <item>Korea</item> + <item>Mexico</item> + <item>Netherlands</item> + <item>New Zealand</item> + <item>Norway</item> + <item>Poland</item> + <item>Portugal</item> + <item>Russia</item> + <item>Singapore</item> + <item>Slovakia</item> + <item>Spain</item> + <item>Switzerland</item> + <item>Sweden</item> + <item>Taiwan</item> + <item>Turkey</item> + <item>United Kingdom </item> + <item>United States</item> + </string-array> + + <string-array name="regional_band_values"> + <item>0</item> + <item>1</item> + <item>2</item> + <item>3</item> + <item>4</item> + <item>5</item> + <item>6</item> + <item>7</item> + <item>8</item> + <item>9</item> + <item>10</item> + <item>11</item> + <item>12</item> + <item>13</item> + <item>14</item> + <item>15</item> + <item>16</item> + <item>17</item> + <item>18</item> + <item>19</item> + <item>20</item> + <item>21</item> + <item>22</item> + <item>23</item> + <item>24</item> + <item>25</item> + <item>26</item> + <item>27</item> + <item>28</item> + <item>29</item> + <item>30</item> + <item>31</item> + <item>32</item> + <item>33</item> + <item>34</item> + <item>35</item> + </string-array> + + <string-array name="regional_band_summary"> + <item>North America (87.5MHz To 108.0MHz In 200 Khz Steps)</item> + <item>Europe (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Japan (76.0MHz To 90.0MHz In 100 Khz Steps)</item> + <item>Japan (Wide) (90.0MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Australia (87.7MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Austria (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Belgium (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Brazil (87.8MHz To 108.0MHz In 200 Khz Step)</item> + <item>China (87.0MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Czech (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Denmark (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Finland (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>France (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Germany (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Greece (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Hong Kong (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>India (91.0MHz To 106.4MHz In 100 Khz Steps)</item> + <item>Ireland (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Italy (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Korea (87.5MHz To 108.0MHz In 200 Khz Steps)</item> + <item>Mexico (88.1MHz To 107.9MHz In 200 Khz Steps)</item> + <item>Netherlands (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>New Zealand (88.0MHz To 107.0MHz In 100 Khz Steps)</item> + <item>Norway (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Poland (88.0MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Portugal (87.5MHz To 108.0MHz In 50 Khz Steps)</item> + <item>Russia (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Singapore (88.0MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Slovakia (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Spain (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Switzerland (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Sweden (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Taiwan (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>Turkey (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>United Kingdom (87.5MHz To 108.0MHz In 100 Khz Steps)</item> + <item>United States (88.1MHz To 107.9MHz In 200 Khz Steps)</item> + </string-array> + + <string-array name="rf_cfg"> + <item> Get Current Station Parameters</item> + <item> Get Station Parameters Repeat 20 times</item> + <item> Get Search station parameters</item> + <item> Get FM Band sweep parameters</item> + <item> Configure Performance Parameters</item> + </string-array> + + <string-array name="cfg_rf1"> + <item> Set FM RSSI Delta</item> + <item> Set Signal Threshold</item> + <item> RF Statistics</item> + </string-array> + + <string-array name="cfg_rf2"> + <item> Set SINR Samples Count</item> + <item> Set SINR Threshold</item> + <item> Set IntfDetoutLow Threshold</item> + <item> Set IntfDetoutHigh Threshold</item> + <item> Set SinrFirstStage</item> + <item> Set RmssiFirstStage</item> + <item> Set CF0Th12</item> + <item> Set SearchAlgo Type</item> + <item> Get SINR Samples Count</item> + <item> Get SINR Threshold</item> + <item> Get SinrFirstStage</item> + <item> Get RmssiFirstStage</item> + <item> Get CF0Th12</item> + <item> Get Search Algo Type</item> + <item> Set AfJmpRmssi Threshold</item> + <item> Set GoodChRmssi Threshold</item> + <item> Set AfJmpRmssi Samples count</item> + <item> Get AfJmpRmssi Threshold</item> + <item> Get GoodChRmssi Threshold</item> + <item> Get AfJmpRmssi Samples count</item> + <item> RF Statistics</item> + <item> Set RXREPEAT Count</item> + </string-array> + + <string-array name="stats_options"> + <item> Select the option</item> + <item> Configure Performance parameters</item> + <item> RF Statistics</item> + </string-array> + +</resources> diff --git a/fmapp/res/values/strings.xml b/fmapp/res/values/strings.xml new file mode 100644 index 0000000..b1dd448 --- /dev/null +++ b/fmapp/res/values/strings.xml @@ -0,0 +1,265 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">FM Radio</string> + <string name="tx_app_name">FM Transmitter</string> + + <string name="default_sleep">Sleep 12:34</string> + <string name="menu_add_to_preset">Add to Presets</string> + <string name="menu_settings">Settings</string> + <string name="menu_record_start">Start Recording</string> + <string name="menu_record_stop">Stop Recording</string> + <string name="button_ok">OK</string> + <string name="error_sdcard_access">Unable to access SD card</string> + <string name="fm_service_started">FM Service</string> + <string name="menu_sleep">Sleep</string> + <string name="menu_sleep_cancel">Cancel Sleep</string> + <string name="menu_wired_headset">Wired Headset</string> + <string name="menu_speaker_on">Enable Speaker</string> + <string name="menu_display_tags">Show Tags</string> + <string name="menu_speaker_off">Disable Speaker</string> + <string name="menu_scan_start">Scan</string> + <string name="menu_scan_stop">Stop Searching</string> + <string name="menu_stats">FM RF Stats</string> + <string name="button_text_stop">Stop</string> + <string name="msg_seeking">Searching FM Station</string> + <string name="msg_scanning">Scanning for all FM Stations</string> + <string name="msg_scanning_pty">Scanning for "<xliff:g id="pty_name">%1$s</xliff:g>"</string> + <string name="msg_search_title">FM - <xliff:g id="frequency_string">%1$s</xliff:g></string> + <string name="msg_searching">Searching for Strong Stations to create a Preset List </string> + <string name="msg_searching_title">Auto-Select Presets</string> + <string name="msg_weak_searching">Searching for frequencies to transmit</string> + + <string name="msg_noantenna">Please plug in a Headset to use FM + Radio </string> + <string name="msg_headsetpluggedin">Disconnect all Headsets for using FM Tx</string> + <string name="msg_callactive">FM Tx is not supported when there is active voice call</string> + <string name="dialog_sleep_title">Select Auto-Off Sleep Time</string> + <string name="preset_select_name">Select "<xliff:g id="preset_name">%1$s</xliff:g>"</string> + <string name="presetlist_select_name">Select "<xliff:g id="presetlist_name">%1$s</xliff:g>"</string> + <string name="presetlist_add_new">Add New List</string> + <string name="dialog_presetlist_title">Preset Lists</string> + <string name="presetlist_prefix_name">FM - </string> + <string name="dialog_presetlist_rename_title">Enter a name </string> + <string name="dialog_preset_rename_title">Enter a name </string> + <string name="presetlist_delete_name">Delete "<xliff:g id="presetlist_name">%1$s</xliff:g>" and its Stations?</string> + <string name="presetlist_autoselect_title">Confirm Auto-Select</string> + <string name="presetlist_autoselect_name">Auto-Select will delete all the Presets in the list "<xliff:g id="presetlist_name">%1$s</xliff:g>", Continue?</string> + <string name="fmtx_autoselect_name">Auto-Select will delete all the Presets, Continue?</string> + + <string name="enter_frequency">Tune to Station</string> + <string name="enter_new_frequency_title">Enter Frequency </string> + + <string name="preset_now_playing">Now Playing </string> + <string name="preset_pref_title">"<xliff:g id="preset_name">%1$s</xliff:g>" + (<xliff:g id="preset_freq">%2$s</xliff:g>)</string> + <string name="preset_delete_title">Delete Preset</string> + + <string name="preset_delete_name">Delete "<xliff:g id="preset_name">%1$s</xliff:g>" + from "<xliff:g id="presetlist_name">%2$s</xliff:g>"</string> + + <string name="preset_confirm_delete_name">Delete "<xliff:g id="preset_name">%1$s</xliff:g>" + from "<xliff:g id="presetlist_name">%2$s</xliff:g>" ?</string> + + <string name="preset_name_dialog_title">Enter a name </string> + <string name="preset_name_summary">"<xliff:g id="preset_name">%1$s</xliff:g>" </string> + <string name="preset_name_title">Rename</string> + + <string name="preset_search_title">Search</string> + <string name="preset_search_name">Search for "<xliff:g id="preset_name">%1$s</xliff:g>"</string> + + <string name="preset_preference_title">FM Stations in "<xliff:g id="preset_name">%1$s</xliff:g>" </string> + + <string name="preset_empty_title">No Stations in the List</string> + <string name="preset_empty_add_summary">Add Stations</string> + + <string name="preset_add">Add</string> + <string name="preset_replace">Replace</string> + <string name="preset_tune">Tune</string> + <string name="preset_rename">Rename</string> + <string name="preset_delete">Delete</string> + <string name="preset_search">Search for "<xliff:g id="preset_pi">%1$s</xliff:g>" + </string> + + <string name="stat_notif_frequency">FM Radio Station "<xliff:g id="frequency">%1$s</xliff:g>"</string> + <string name="stat_notif_tx_frequency">FM Transmitting at: "<xliff:g id="frequency">%1$s</xliff:g>"</string> + + + <string name="sel_band_menu">Select Regional Band</string> + <string name="test_menu">Select Test</string> + + <string name="settings_menu">Settings</string> + <string name="regional_band">Regional Band</string> + + <string name="aud_output_mode">Audio Output Mode</string> + <string name="sel_audio_output">Select Audio Output</string> + + <string name="record_dur">Record Duration</string> + <string name="sel_rec_dur">Select Record Duration</string> + + <string name="auto_select_af">Alternate Frequency</string> + <string name="auto_select_af_enabled">Auto Select Enabled</string> + <string name="auto_select_af_disabled">Auto Select Disabled</string> + + <string name="fac_defaults">Revert to Factory Defaults</string> + <string name="revert_to_fac">Revert to Factory Defaults</string> + <string name="settings_back">Back</string> + + <string name="settings_revert_confirm_title">Confirm Reset Settings</string> + <string name="settings_revert_confirm_msg">This will delete all settings including Presets</string> + + <string name="settings_back_summary">Return to previous view</string> + <string name="settings_revert_defaults_title">Revert to Factory Defaults</string> + <string name="settings_revert_defaults_summary">Reset all settings</string> + + <string name="audio_type_stereo">Stereo</string> + <string name="audio_type_mono">Mono</string> + + <string name="default_band">United States</string> + <string name="default_audio">Stereo</string> + <string name="default_record_duration">5 minutes</string> + <string name="factory_reset_dialog_title">Confirm Reset</string> + <string name="factory_reset_dialog_message">This will delete all settings including Presets</string> + <string name="station_move">Move</string> + <string name="station_edit">Edit</string> + <!-- alert to the user that USB storage must be available before using FM recording [CHAR LIMIT=NONE] --> + <string name="no_storage">Mount SD card before start recording.</string> + <!-- alert to the user that the USB storage is being disk-checked [CHAR LIMIT=30] --> + <string name="preparing_sd">Preparing SD card</string> + <!-- alert to the user that the FM fails to read or write the USB storage. [CHAR LIMIT=NONE] --> + <string name="access_sd_fail">Couldn\'t access SD card.</string> + <!-- Low-memory dialog message [CHAR LIMT=NONE] --> + <string name="spaceIsLow_content">Your SD card storage is running out of space. Change the quality setting or delete some images or other files.</string> + <!-- The messsage shown when FM record reaches size limit. --> + <string name="FMRecording_reach_size_limit">Size limit reached.</string> + + <string name="delete_station_title">Delete</string> + <string name="find_af_station">Find</string> + <string name="menu_add_list">Add List</string> + <string name="menu_rename_list">Rename List</string> + <string name="menu_reorder_list">Reorder List</string> + <string name="menu_scan_for_preset">Scan for Presets</string> + <string name="menu_delete_list">Delete List</string> + <string name="alert_dialog_ok">Ok</string> + <string name="alert_dialog_cancel">Cancel</string> + + <string name="search_dialog_title">Scan Stations</string> + + <string name="search_category_key">search_category_type</string> + <string name="search_category">Select Search Type</string> + <string name="search_category_title">Search Type</string> + <string name="default_search_category">Seek</string> + <string name="search_category_tune">Tune</string> + + <string name="search_category_type_key">search_category_value_type</string> + <string name="search_category_type">Select Search by Program Type</string> + <string name="search_category_type_title">Search by Program Type</string> + <string name="default_search_category_type">All Stations</string> + + <string name="presets_back">Back</string> + <string name="presets_back_summary">Return to previous view</string> + + <string name="nav_mode_presets">Presets</string> + <string name="nav_mode_stations">Stations</string> + + <string name="prog_service_string"></string> + <string name="radio_text_string">This is the long Radio Text String Message that needs to scroll</string> + <string name="eradio_text_string">Extended Radio Text Message</string> + <string name="pty_string"></string> + <string name="frequency_string"></string> + <string name="station_id_string"></string> + + + <string name="fm_command_timeout_title">Timeout</string> + <string name="fm_tune_timeout_msg">No response received to Tune FM + Station. If problem persists, please turn off FM and turn it back + on.</string> + + <string name="fm_command_failed_title">FM Operation failed</string> + <string name="fm_cmd_failed_msg">FM operation failed. If problem + persists, please turn off FM and turn it back on. + </string> + <string name="fm_cmd_failed_msg_hdmi">FM operation failed. FM and HDMI concurrecny is not supported. + </string> + <string name="fm_cmd_failed_call_on">FM operation failed. FM audio is not allowed during call. </string> + + <skip/> + <!-- Do not translate. Duration format. --> + <string name="durationformat"><xliff:g id="format">%2$d:%5$02d</xliff:g></string> + + <string name="default_record"></string> + <string name="default_preset_list_name"> FM List </string> + <string name="default_station"> FM </string> + <string name="default_blank"> </string> + <string name="stop_scanning">Stop Scanning</string> + <string name="transmit_msg_string">Transmitting at:</string> + <!-- the name under which recordings will be visible in the media database is formatted like this --> + <string name="audio_db_title_format"><xliff:g id="format">yyyy-MM-dd HH:mm:ss</xliff:g></string> + <!-- all recordings will show up in the media database with this 'artist' name --> + <string name="audio_db_artist_name">My FM recordings</string> + <!-- all recordings will show up in the media database with this 'album' name --> + <string name="audio_db_album_name">FM recordings</string> + <!-- all recordings will show up in the media database in a playlist with this name --> + <string name="audio_db_playlist_name">FM recordings</string> + <string name="test_prompt">Choose a test</string> + <string name="test_run">Run Test</string> + <string name="set_rmmsi_delta">Configure RMSSI Delta</string> + <string name="set_sigth">Configure Signal Th</string> + <string name="type_rd"></string> + <string name="srch_params">Configure RMSSI Delta</string> + <string name="srch_params_1">Configure Signal Th </string> + <string name="enter_rssi">Enter RMSSI Delta</string> + <string name="enter_sigth">Enter Signal Th</string> + <string name="enter_SinrSmplsCnt">Enter SINR Samples Count</string> + <string name="set_SinrSmplsCnt">Configure SINR Samples</string> + <string name="enter_SinrTh">Enter SINR Threshold</string> + <string name="set_SinrTh">Configure SINR Threshold</string> + <string name="enter_IntfLowTh">Enter Intf Det Low Threshold</string> + <string name="set_IntfLowTh">Config Intf Det Low Threshold</string> + <string name="enter_IntfHighTh">Enter Intf Det High Threshold</string> + <string name="set_IntfHighTh">Config Intf Det High Threshold</string> + <string name="enter_SinrFirstStage">Enter Sinr First Stage</string> + <string name="set_SinrFirstStage">Config Sinr First Stage</string> + <string name="enter_RmssiFirstStage">Enter Rmssi First Stage</string> + <string name="set_RmssiFirstStage">Config Rmssi First Stage</string> + <string name="enter_CF0Th12">Enter CF0 Threshold</string> + <string name="set_CF0Th12">Config CF0 Threshold</string> + <string name="search_algo_mpx">MPX_DCC Algo</string> + <string name="search_algo_sinrint">SINR_INTF Algo</string> + <string name="enter_AfJmpRmssiTh">Enter Af Jump Rmssi Threshold</string> + <string name="set_AfJmpRmssiTh">Config Af Jump Rmssi Threshold</string> + <string name="enter_GdChRmssiTh">Enter Good Channel Rmssi Threshold</string> + <string name="set_GdChRmssiTh">Config Good Channel Rmssi Threshold</string> + <string name="enter_AfJmpRmssiSmplsCnt">Enter Af Jump Rmssi Samples count</string> + <string name="set_AfJmpRmssiSmplsCnt">Config Af Jump Rmssi Samples count</string> + <string name="rt_plus_tags">Tags</string> + <string name="enter_RxRePeatCount">Enter RxRePeat count</string> + <string name="set_RxRePeatCount">Config RxRePeat count</string> + +</resources> diff --git a/fmapp/res/values/styles.xml b/fmapp/res/values/styles.xml new file mode 100644 index 0000000..5119c09 --- /dev/null +++ b/fmapp/res/values/styles.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 2012,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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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> + <style name="FMStationSeekBar"> + <item name="android:indeterminateOnly">false</item> + <item name="android:progressDrawable">@android:drawable/progress_horizontal</item> + <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item> + <item name="android:minHeight">20dp</item> + <item name="android:maxHeight">20dp</item> + <item name="android:thumbOffset">8dp</item> + <item name="android:focusable">true</item> + </style> +</resources> diff --git a/fmapp/src/com/codeaurora/fmradio/FMAdapterApp.java b/fmapp/src/com/codeaurora/fmradio/FMAdapterApp.java new file mode 100644 index 0000000..87b0aeb --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMAdapterApp.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 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. + */ + +/** + * @hide + */ + +package com.codeaurora.fmradio; + +import android.app.Application; +import android.util.Log; + +public class FMAdapterApp extends Application { + private static final String TAG = "FMAdapterApp"; + private static final boolean DBG = true; + //For Debugging only + private static int sRefCount=0; + + static { + if (DBG) Log.d(TAG,"Loading FM-JNI Library"); + System.loadLibrary("qcomfm_jni"); + } + + public FMAdapterApp() { + super(); + if (DBG) { + synchronized (FMAdapterApp.class) { + sRefCount++; + Log.d(TAG, "REFCOUNT: Constructed "+ this + " Instance Count = " + sRefCount); + } + } + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + protected void finalize() { + if (DBG) { + synchronized (FMAdapterApp.class) { + sRefCount--; + Log.d(TAG, "REFCOUNT: Finalized: " + this +", Instance Count = " + sRefCount); + } + } + } +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMMediaButtonIntentReceiver.java b/fmapp/src/com/codeaurora/fmradio/FMMediaButtonIntentReceiver.java new file mode 100644 index 0000000..ca7f102 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMMediaButtonIntentReceiver.java @@ -0,0 +1,71 @@ +/* + * 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 com.codeaurora.fmradio; + +import android.content.Intent; +import android.content.IntentFilter; +import android.app.IntentService; +import android.content.BroadcastReceiver; +import android.content.pm.PackageManager; +import android.content.Context; +import android.content.ComponentName; +import android.util.Log; +import android.view.KeyEvent; +import android.os.Bundle; +import java.lang.Object; + +public class FMMediaButtonIntentReceiver extends BroadcastReceiver { + +private static final String TAG = "FMMediaButtonIntentReceiver"; +public static final String FM_MEDIA_BUTTON = "com.codeaurora.fmradio.action.MEDIA_BUTTON"; +public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if ((action != null) && action.equals("android.intent.action.MEDIA_BUTTON")) { + KeyEvent event = (KeyEvent) + intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + + if (event == null) { + return; + } + int keycode = event.getKeyCode(); + int key_action = event.getAction(); + if (((KeyEvent.KEYCODE_HEADSETHOOK == keycode) && + (key_action == KeyEvent.ACTION_DOWN)) || + (KeyEvent.KEYCODE_MEDIA_PAUSE == keycode) || + (KeyEvent.KEYCODE_MEDIA_PLAY == keycode)) { + + Log.d(TAG, "ACTION_MEDIA_BUTTON intent received for ACTION_DOWN"); + Intent i = new Intent(FM_MEDIA_BUTTON); + i.putExtra(Intent.EXTRA_KEY_EVENT, event); + context.sendBroadcast(i); + } + } + } +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMRadio.java b/fmapp/src/com/codeaurora/fmradio/FMRadio.java new file mode 100644 index 0000000..d592bc2 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMRadio.java @@ -0,0 +1,3595 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothDevice; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnKeyListener; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.media.AudioSystem; +import android.media.AudioManager; +import android.media.MediaRecorder; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.KeyEvent; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import android.text.TextUtils; + +import java.util.*; +import java.io.File; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Formatter; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.ArrayList; + +import com.codeaurora.utils.FrequencyPicker; +import com.codeaurora.utils.FrequencyPickerDialog; +import android.content.ServiceConnection; +import android.media.MediaRecorder; + +import qcom.fmradio.FmConfig; +import android.os.ServiceManager; + + +public class FMRadio extends Activity +{ + public static final String LOGTAG = "FMRadio"; + + public static final boolean RECORDING_ENABLE = true; + MediaRecorder mRecorder = null; + + /* menu Identifiers */ + private static final int MENU_SCAN_START = Menu.FIRST + 2; + private static final int MENU_SCAN_STOP = Menu.FIRST + 3; + private static final int MENU_RECORD_START = Menu.FIRST + 4; + private static final int MENU_RECORD_STOP = Menu.FIRST + 5; + private static final int MENU_SLEEP = Menu.FIRST + 6; + private static final int MENU_SLEEP_CANCEL = Menu.FIRST + 7; + private static final int MENU_SETTINGS = Menu.FIRST + 8; + private static final int MENU_SPEAKER = Menu.FIRST + 9; + private static final int MENU_TAGS = Menu.FIRST + 10; + private static final int MENU_STAT_TEST = Menu.FIRST + 11; + + /* Dialog Identifiers */ + private static final int DIALOG_SEARCH = 1; + private static final int DIALOG_SLEEP = 2; + private static final int DIALOG_SELECT_PRESET_LIST = 3; + private static final int DIALOG_PRESETS_LIST = 4; + private static final int DIALOG_PRESET_LIST_RENAME = 5; + private static final int DIALOG_PRESET_LIST_DELETE = 6; + private static final int DIALOG_PRESET_LIST_AUTO_SET = 7; + private static final int DIALOG_PICK_FREQUENCY = 8; + private static final int DIALOG_PROGRESS_PROGRESS = 9; + private static final int DIALOG_PRESET_OPTIONS = 10; + private static final int DIALOG_PRESET_RENAME = 11; + private static final int DIALOG_CMD_TIMEOUT = 12; + private static final int DIALOG_CMD_FAILED = 13; + private static final int DIALOG_CMD_FAILED_HDMI_ON = 14; + private static final int DIALOG_CMD_FAILED_CALL_ON = 15; + private static final int DIALOG_TAGS = 16; + + /* Activity Return ResultIdentifiers */ + private static final int ACTIVITY_RESULT_SETTINGS = 1; + + /* Activity Return ResultIdentifiers */ + private static final int MAX_PRESETS_PER_PAGE = 5; + + + /* Station's Audio is Stereo */ + private static final int FMRADIO_UI_STATION_AUDIO_STEREO = 1; + /* Station's Audio is Mono */ + private static final int FMRADIO_UI_STATION_AUDIO_MONO = 2; + + /* The duration during which the "Sleep: xx:xx" string will be toggling + */ + private static final int SLEEP_TOGGLE_SECONDS = 60; + + /* The number of Preset Stations to create. + * The hardware supports a maximum of 12. + */ + private static final int NUM_AUTO_PRESETS_SEARCH= 12; + /* + * Command time out: For asynchonous operations, if no response + * is received with int this duration, a timeout msg will be displayed. + */ + private static final int CMD_TIMEOUT_DELAY_MS = 5000; + private static final int MSG_CMD_TIMEOUT = 101; + + private static final int CMD_NONE = 0; + private static final int CMD_TUNE = 1; + private static final int CMD_FMON = 2; + private static final int CMD_FMOFF = 3; + private static final int CMD_FMCONFIGURE = 4; + private static final int CMD_MUTE = 5; + private static final int CMD_SEEK = 6; + private static final int CMD_SCAN = 7; + private static final int CMD_SEEKPI = 8; + private static final int CMD_SEARCHLIST = 9; + private static final int CMD_CANCELSEARCH = 10; + private static final int CMD_SET_POWER_MODE = 11; + private static final int CMD_SET_AUDIO_MODE = 12; + private static final int CMD_SET_AUTOAF = 13; + private static final int CMD_GET_INTERNALANTENNA_MODE = 14; + + private static final int PRESETS_OPTIONS_TUNE = 0; + private static final int PRESETS_OPTIONS_REPLACE = 1; + private static final int PRESETS_OPTIONS_RENAME = 2; + private static final int PRESETS_OPTIONS_DELETE = 3; + private static final int PRESETS_OPTIONS_SEARCHPI = 4; + + private static IFMRadioService mService = null; + private static FmSharedPreferences mPrefs; + + /* Button Resources */ + private ImageButton mOnOffButton; + private ImageButton mMuteButton; + /* Button to navigate Preset pages */ + private ImageButton mPresetPageButton; + /* 6 Preset Buttons */ + private Button[] mPresetButtons = { null, null, null, null, null}; + private Button mPresetListButton; + // private ImageButton mSearchButton; + private ImageView mForwardButton; + private ImageView mBackButton; + + /* Top row in the station info layout */ + private ImageView mRSSI; + private TextView mProgramServiceTV; + private TextView mStereoTV; + + /* Middle row in the station info layout */ + private TextView mTuneStationFrequencyTV; + private TextView mStationCallSignTV; + private TextView mProgramTypeTV; + + /* Bottom row in the station info layout */ + private TextView mRadioTextTV; + private TextView mERadioTextTV; + + /* Sleep and Recording Messages */ + private TextView mSleepMsgTV; + private TextView mRecordingMsgTV; + + private double mOutputFreq; + private int mPresetPageNumber = 0; + private int mStereo = -1; + + // default audio device - speaker + private static int mAudioRoute = FMRadioService.RADIO_AUDIO_DEVICE_WIRED_HEADSET; + private static boolean mFMStats = false; + + + /* Current Status Indicators */ + private static boolean mRecording = false; + private static boolean mIsScaning = false; + private static boolean mIsSeeking = false; + private static boolean mIsSearching = false; + private static int mScanPty = 0; + + private Animation mAnimation = null; + private ScrollerText mRadioTextScroller = null; + + private static PresetStation mTunedStation = new PresetStation("", 102100); + private PresetStation mPresetButtonStation = null; + + /* Radio Vars */ + final Handler mHandler = new Handler(); + /* Search Progress Dialog */ + private ProgressDialog mProgressDialog=null; + + /* Asynchronous command active */ + private static int mCommandActive = 0; + + /* Command that failed (Sycnhronous or Asynchronous) */ + private static int mCommandFailed = 0; + + private LoadedDataAndState SavedDataAndState = null; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setVolumeControlStream(AudioManager.STREAM_MUSIC); + mPrefs = new FmSharedPreferences(this); + mCommandActive = CMD_NONE; + mCommandFailed = CMD_NONE; + + Log.d(LOGTAG, "onCreate - Height : "+ getWindowManager().getDefaultDisplay().getHeight() + + " - Width : "+ getWindowManager().getDefaultDisplay().getWidth()); + + setContentView(R.layout.fmradio); + SavedDataAndState = (LoadedDataAndState)getLastNonConfigurationInstance(); + mAnimation = AnimationUtils.loadAnimation(this, + R.anim.preset_select); + + mMuteButton = (ImageButton) findViewById(R.id.btn_silent); + if (mMuteButton != null) + { + mMuteButton.setOnClickListener(mMuteModeClickListener); + } + + mOnOffButton = (ImageButton) findViewById(R.id.btn_onoff); + if (mOnOffButton != null) + { + mOnOffButton.setOnClickListener(mTurnOnOffClickListener); + } + + mForwardButton = (ImageView) findViewById(R.id.btn_forward); + if (mForwardButton != null) + { + mForwardButton.setOnClickListener(mForwardClickListener); + mForwardButton.setOnLongClickListener(mForwardLongClickListener); + } + + mBackButton = (ImageView) findViewById(R.id.btn_back); + if (mBackButton != null) + { + mBackButton.setOnClickListener(mBackClickListener); + mBackButton.setOnLongClickListener(mBackLongClickListener); + } + + mPresetPageButton = (ImageButton) findViewById(R.id.btn_preset_page); + if (mPresetPageButton != null) + { + mPresetPageButton.setOnClickListener(mPresetsPageClickListener); + } + + mPresetListButton = (Button) findViewById(R.id.btn_presets_list); + if (mPresetListButton != null) + { + mPresetListButton.setOnClickListener(mPresetListClickListener); + mPresetListButton + .setOnLongClickListener(mPresetListButtonOnLongClickListener); + } + + /* 6 Preset Buttons */ + mPresetButtons[0] = (Button) findViewById(R.id.presets_button_1); + mPresetButtons[1] = (Button) findViewById(R.id.presets_button_2); + mPresetButtons[2] = (Button) findViewById(R.id.presets_button_3); + mPresetButtons[3] = (Button) findViewById(R.id.presets_button_4); + mPresetButtons[4] = (Button) findViewById(R.id.presets_button_5); + for (int nButton = 0; nButton < MAX_PRESETS_PER_PAGE; nButton++) + { + if (mPresetButtons[nButton] != null) + { + mPresetButtons[nButton] + .setOnClickListener(mPresetButtonClickListener); + mPresetButtons[nButton] + .setOnLongClickListener(mPresetButtonOnLongClickListener); + } + } + + mTuneStationFrequencyTV = (TextView) findViewById(R.id.prog_frequency_tv); + if (mTuneStationFrequencyTV != null) + { + mTuneStationFrequencyTV.setOnLongClickListener(mFrequencyViewClickListener); + } + mProgramServiceTV = (TextView) findViewById(R.id.prog_service_tv); + mStereoTV = (TextView) findViewById(R.id.stereo_text_tv); + + mStationCallSignTV = (TextView) findViewById(R.id.call_sign_tv); + mProgramTypeTV = (TextView) findViewById(R.id.pty_tv); + + mRadioTextTV = (TextView) findViewById(R.id.radio_text_tv); + mERadioTextTV = (TextView) findViewById(R.id.eradio_text_tv); + mSleepMsgTV = (TextView) findViewById(R.id.sleep_msg_tv); + mRecordingMsgTV = (TextView) findViewById(R.id.record_msg_tv); + /* Disable displaying RSSI */ + mRSSI = (ImageView) findViewById(R.id.signal_level); + if (mRSSI != null) + { + mRSSI.setVisibility(View.INVISIBLE); + } + + if ((mRadioTextScroller == null) && (mRadioTextTV != null)) + { + mRadioTextScroller = new ScrollerText(mRadioTextTV); + } + + enableRadioOnOffUI(false); + //HDMI and FM concurrecny is not supported. + if (isHdmiOn()) + { + showDialog(DIALOG_CMD_FAILED_HDMI_ON); + } + else { + if (false == bindToService(this, osc)) + { + Log.d(LOGTAG, "onCreate: Failed to Start Service"); + } else + { + Log.d(LOGTAG, "onCreate: Start Service completed successfully"); + } + } + } + + @Override + public void onRestart() { + Log.d(LOGTAG, "FMRadio: onRestart"); + try { + if (null != mService) { + mService.requestFocus(); + } + } catch (Exception e) { + e.printStackTrace(); + } + super.onRestart(); + } + + @Override + public void onStop() { + Log.d(LOGTAG, "FMRadio: onStop"); + if(isSleepTimerActive() ) { + mSleepUpdateHandlerThread.interrupt(); + long timeNow = ((SystemClock.elapsedRealtime())); + if (timeNow < mSleepAtPhoneTime) + { + try { + if (null != mService) { + mService.delayedStop((mSleepAtPhoneTime - timeNow),FMRadioService.STOP_SERVICE); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if(isRecordTimerActive() ) { + try { + if ( null != mRecordUpdateHandlerThread) { + mRecordUpdateHandlerThread.interrupt(); + } + } catch (NullPointerException e) { + e.printStackTrace(); + } + long rtimeNow = ((SystemClock.elapsedRealtime())); + if (rtimeNow < mRecordDuration) + { + try { + if (null != mService) { + mService.delayedStop((mRecordDuration - rtimeNow), FMRadioService.STOP_RECORD); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + super.onStop(); + } + + @Override + public void onStart() { + super.onStart(); + Log.d(LOGTAG, "FMRadio: onStart"); + try + { + if(mService != null) + { + mService.registerCallbacks(mServiceCallbacks); + } + } + catch (RemoteException e) + { + e.printStackTrace(); + } + if(isSleepTimerActive()) { + Log.d(LOGTAG,"isSleepTimerActive is true"); + try { + if (null != mService) { + mService.cancelDelayedStop(FMRadioService.STOP_SERVICE); + } + } catch (Exception e) { + e.printStackTrace(); + } + initiateSleepThread(); + } + if(isRecordTimerActive()) { + Log.d(LOGTAG,"isRecordTimerActive is true"); + try { + if (null != mService) { + mService.cancelDelayedStop(FMRadioService.STOP_RECORD); + } + } catch (Exception e) { + e.printStackTrace(); + } + if(isRecording()) { + initiateRecordThread(); + } + } + + } + + @Override + protected void onPause() { + Log.d(LOGTAG, "FMRadio: onPause"); + super.onPause(); + mRadioTextScroller.stopScroll(); + FmSharedPreferences.setTunedFrequency(mTunedStation.getFrequency()); + mPrefs.Save(); + } + + @Override + public void onResume() { + super.onResume(); + Log.d(LOGTAG, "FMRadio: onResume"); + mPrefs.Load(); + PresetStation station = new PresetStation("", FmSharedPreferences.getTunedFrequency()); + if (station != null) { + mTunedStation.Copy(station); + } + mHandler.post(mUpdateProgramService); + mHandler.post(mUpdateRadioText); + mHandler.post(mOnStereo); + updateStationInfoToUI(); + enableRadioOnOffUI(); + } + private class LoadedDataAndState { + public LoadedDataAndState(){}; + public boolean onOrOff; + } + @Override + public Object onRetainNonConfigurationInstance() { + final LoadedDataAndState data = new LoadedDataAndState(); + if(mService != null) { + try { + data.onOrOff = mService.isFmOn(); + } catch(RemoteException e) { + data.onOrOff = false; + e.printStackTrace(); + } + } else { + data.onOrOff = false; + } + return data; + } + + @Override + public void onDestroy() { + Log.d(LOGTAG, "FMRadio: onDestroy"); + cleanupTimeoutHandler(); + if (mService != null) { + try { + if(!mService.isFmOn()) { + endSleepTimer(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + unbindFromService(this); + mService = null; + Log.d(LOGTAG, "onDestroy: unbindFromService completed"); + super.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuItem item; + boolean radioOn = isFmOn(); + boolean recording = isRecording(); + boolean mSpeakerPhoneOn = isSpeakerEnabled(); + boolean sleepActive = isSleepTimerActive(); + boolean searchActive = isScanActive() || isSeekActive(); + + item = menu.add(0, MENU_SCAN_START, 0, R.string.menu_scan_start).setIcon( + R.drawable.ic_btn_search); + if (item != null) + { + item.setVisible(!searchActive && radioOn); + } + item = menu.add(0, MENU_SCAN_STOP, 0, R.string.menu_scan_stop).setIcon( + R.drawable.ic_btn_search); + if (item != null) + { + item.setVisible(searchActive && radioOn); + } + + if (RECORDING_ENABLE) + { + item = menu.add(0, MENU_RECORD_START, 0, R.string.menu_record_start) + .setIcon(R.drawable.ic_menu_record); + if (item != null) + { + item.setVisible(true); + item.setEnabled(!recording && radioOn); + } + item = menu.add(0, MENU_RECORD_STOP, 0, R.string.menu_record_stop) + .setIcon(R.drawable.ic_menu_record); + if (item != null) + { + item.setVisible(true); + item.setEnabled(recording && radioOn); + } + } + /* Settings can be active */ + item = menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings).setIcon( + android.R.drawable.ic_menu_preferences); + + item = menu.add(0, MENU_SLEEP, 0, R.string.menu_sleep) + .setTitle(R.string.menu_sleep); + if (item != null) + { + item.setVisible(!sleepActive && radioOn); + } + item = menu.add(0, MENU_SLEEP_CANCEL, 0, R.string.menu_sleep_cancel) + .setTitle(R.string.menu_sleep_cancel); + if (item != null) + { + item.setVisible(sleepActive && radioOn); + } + mFMStats = SystemProperties.getBoolean("persist.fm.stats",false); + if(mFMStats) { + item = menu.add(0, MENU_STAT_TEST, 0,R.string.menu_stats).setIcon( + android.R.drawable.ic_menu_info_details); + } + if (!mSpeakerPhoneOn) { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_on); + } + else { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_off); + } + item = menu.add(0, MENU_TAGS, 0, R.string.menu_display_tags); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + + super.onPrepareOptionsMenu(menu); + + MenuItem item; + boolean radioOn = isFmOn(); + boolean recording = isRecording(); + boolean mSpeakerPhoneOn = isSpeakerEnabled(); + boolean searchActive = isScanActive() || isSeekActive(); + + item = menu.findItem(MENU_SCAN_START); + if (item != null) + { + item.setVisible(!searchActive && radioOn); + } + item = menu.findItem(MENU_SCAN_STOP); + if (item != null) + { + item.setVisible(searchActive && radioOn); + } + if (RECORDING_ENABLE) + { + item = menu.findItem(MENU_RECORD_START); + if (item != null) + { + item.setVisible(true); + item.setEnabled(!recording && radioOn && !isAnalogModeEnabled()); + } + item = menu.findItem(MENU_RECORD_STOP); + if (item != null) + { + item.setVisible(true); + item.setEnabled(recording && radioOn && !isAnalogModeEnabled()); + } + } + + boolean sleepActive = isSleepTimerActive(); + item = menu.findItem(MENU_SLEEP); + if (item != null) + { + item.setVisible(!sleepActive && radioOn); + } + item = menu.findItem(MENU_SLEEP_CANCEL); + if (item != null) + { + item.setVisible(sleepActive && radioOn); + } + + if (radioOn) + { + if (menu.findItem(MENU_SPEAKER) == null) + { + if (!mSpeakerPhoneOn) { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_on); + } + else { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_off); + } + } + else { + menu.removeItem(MENU_SPEAKER); + if (!mSpeakerPhoneOn) { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_on); + } + else { + item = menu.add(0, MENU_SPEAKER, 0, R.string.menu_speaker_off); + } + } + } else + { + menu.removeItem(MENU_SPEAKER); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) + { + case MENU_SETTINGS: + Intent launchPreferencesIntent = new Intent().setClass(this, + Settings.class); + launchPreferencesIntent.putExtra(Settings.RX_MODE,true); + startActivityForResult(launchPreferencesIntent, + ACTIVITY_RESULT_SETTINGS); + //startActivity(launchPreferencesIntent); + return true; + case MENU_STAT_TEST: + Intent launchFMStatIntent = new Intent().setClass(this, + FMStats.class); + startActivity(launchFMStatIntent); + return true; + + case MENU_SCAN_START: + showDialog(DIALOG_SEARCH); + return true; + + case MENU_SCAN_STOP: + cancelSearch(); + return true; + + case MENU_RECORD_START: + startRecording(); + return true; + + case MENU_RECORD_STOP: + stopRecording(); + return true; + + case MENU_SLEEP: + showDialog(DIALOG_SLEEP); + return true; + + case MENU_SLEEP_CANCEL: + DebugToasts("Sleep Cancelled", Toast.LENGTH_SHORT); + endSleepTimer(); + return true; + + case MENU_SPEAKER: + /* Call the mm interface to route the wired headset*/ + enableSpeaker(); + return true; + case MENU_TAGS: + Intent launchFMTagsIntent = new Intent().setClass(this, + FmTags.class); + startActivity(launchFMTagsIntent); + return true; + default: + break; + } + return super.onOptionsItemSelected(item); + } + + private boolean isHdmiOn() { + //HDMI and FM concurrecny is not supported. + try { + String hdmiUserOption = android.provider.Settings.System.getString( + getContentResolver(), "HDMI_USEROPTION"); + } + catch (Exception ex){ + } + return false; + } + + private void enableSpeaker() { + //This method with toggle Speaker phone based on existing state . + boolean bSpeakerPhoneOn = isSpeakerEnabled(); + if(mService != null) + { + try { + if(bSpeakerPhoneOn){ // as Speaker is already on turn it off. + mService.enableSpeaker(false); + Log.d(LOGTAG, "Speaker phone is turned off"); + }else{ // as Speaker is off turn it on. + mService.enableSpeaker(true); + Log.d(LOGTAG, "Speaker phone is turned on"); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + private static final int RECORDTIMER_EXPIRED = 0x1003; + private static final int RECORDTIMER_UPDATE = 0x1004; + + private boolean hasRecordTimerExpired() { + boolean expired = true; + + /* If record duration is 'until stopped' then return false and exit */ + if ( mRecordUntilStopped ) + return false; + + if (isRecordTimerActive()) + { + long timeNow = ((SystemClock.elapsedRealtime())); + //Log.d(LOGTAG, "hasSleepTimerExpired - " + mSleepAtPhoneTime + " now: "+ timeNow); + if (mRecording == false || timeNow < mRecordDuration) + { + expired = false; + } + } + return expired; + } + + private boolean isRecordTimerActive() { + boolean active = false; + if (mRecordDuration > 0) + { + active = true; + } + return active; + } + + private void updateExpiredRecordTime() { + int vis = View.VISIBLE; + if ( mRecordUntilStopped || isRecordTimerActive()) + { + long timeNow = ((SystemClock.elapsedRealtime())); + if (mRecordUntilStopped || mRecordDuration >= timeNow) + { + long seconds = (timeNow - mRecordStartTime) / 1000; + String Msg = makeTimeString(seconds); + mRecordingMsgTV.setText(Msg); + } else + { + /* Clean up timer */ + mRecordDuration = 0; + } + } + mRecordingMsgTV.setVisibility(vis); + } + + /* Recorder Thread processing */ + private Runnable doRecordProcessing = new Runnable() { + public void run() { + boolean recordTimerExpired; + if( mRecordUntilStopped ) + { + recordTimerExpired = false; + } + else + { + recordTimerExpired = hasRecordTimerExpired(); + } + while (recordTimerExpired == false && (!Thread.currentThread().isInterrupted()) ) + { + try + { + Thread.sleep(500); + Message statusUpdate = new Message(); + statusUpdate.what = RECORDTIMER_UPDATE; + mUIUpdateHandlerHandler.sendMessage(statusUpdate); + recordTimerExpired = hasRecordTimerExpired(); + } catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + } + if( true == recordTimerExpired) { + Message finished = new Message(); + finished.what = RECORDTIMER_EXPIRED; + mUIUpdateHandlerHandler.sendMessage(finished); + } + } + } + }; + + private static long mRecordDuration = 0; + private static long mRecordStartTime = 0; + private static boolean mRecordUntilStopped =false; + private Thread mRecordUpdateHandlerThread = null; + + private void initiateRecordDurationTimer(long mins ) { + + if( mins == FmSharedPreferences.RECORD_DUR_INDEX_3_VAL ) + { + mRecordUntilStopped = true; + mRecordStartTime = SystemClock.elapsedRealtime(); + } + else + { + mRecordUntilStopped = false; + mRecordDuration = ( mRecordStartTime = SystemClock.elapsedRealtime()) + (mins * 60 * 1000); + } + + Log.d(LOGTAG, "Stop Recording in mins : " + mins); + initiateRecordThread(); + } + private void initiateRecordThread() { + if (mRecordUpdateHandlerThread == null) + { + mRecordUpdateHandlerThread = new Thread(null, doRecordProcessing, + "RecordUpdateThread"); + } + /* Launch the dummy thread to simulate the transfer progress */ + Log.d(LOGTAG, "Thread State: " + mRecordUpdateHandlerThread.getState()); + if (mRecordUpdateHandlerThread.getState() == Thread.State.TERMINATED) + { + mRecordUpdateHandlerThread = new Thread(null, doRecordProcessing, + "RecordUpdateThread"); + } + /* If the thread state is "new" then the thread has not yet started */ + if (mRecordUpdateHandlerThread.getState() == Thread.State.NEW) + { + mRecordUpdateHandlerThread.start(); + } + } + + + + private void audioRoute (int audioDevice) + { + boolean bStatus; + if(mService != null) + { + try + { + bStatus = mService.routeAudio(audioDevice); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + + } + + @Override + protected Dialog onCreateDialog(int id) { + AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this); + dlgBuilder.setOnKeyListener(new OnKeyListener() { + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + Log.d(LOGTAG, "OnKeyListener event received"+keyCode); + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case 126: //KeyEvent.KEYCODE_MEDIA_PLAY: + case 127: //KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_STOP: + return true; + } + return false; + } + }); + switch (id) + { + case DIALOG_SELECT_PRESET_LIST: { + return createSelectPresetListDlg(id, dlgBuilder); + } + case DIALOG_PRESETS_LIST: { + return createPresetListEditDlg(id, dlgBuilder); + } + case DIALOG_PRESET_LIST_RENAME: { + return createPresetListRenameDlg(id, dlgBuilder); + } + case DIALOG_PRESET_LIST_DELETE: { + return createPresetListDeleteDlg(id, dlgBuilder); + } + case DIALOG_PRESET_LIST_AUTO_SET: { + return createPresetListAutoSelectWarnDlg(id, dlgBuilder); + } + case DIALOG_SEARCH: { + return createSearchDlg(id, dlgBuilder); + } + case DIALOG_SLEEP: { + return createSleepDlg(id, dlgBuilder); + } + case DIALOG_PICK_FREQUENCY: { + FmConfig fmConfig = FmSharedPreferences.getFMConfiguration(); + return new FrequencyPickerDialog(this, fmConfig, mTunedStation.getFrequency(), mFrequencyChangeListener); + } + case DIALOG_PROGRESS_PROGRESS: { + return createProgressDialog(id); + } + case DIALOG_PRESET_OPTIONS: { + return createPresetOptionsDlg(id, dlgBuilder); + } + case DIALOG_PRESET_RENAME: { + return createPresetRenameDlg(id, dlgBuilder); + } + case DIALOG_CMD_TIMEOUT:{ + return createCmdTimeoutDlg(id, dlgBuilder); + } + case DIALOG_CMD_FAILED:{ + return createCmdFailedDlg(id, dlgBuilder); + } + case DIALOG_CMD_FAILED_HDMI_ON:{ + return createCmdFailedDlgHdmiOn(id); + } + case DIALOG_CMD_FAILED_CALL_ON:{ + return createCmdFailedDlgCallOn(id); + } + default: + break; + } + return null; + } + + @Override + protected void onPrepareDialog(int id, Dialog dialog) { + super.onPrepareDialog(id, dialog); + int curListIndex = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = FmSharedPreferences.getStationList(curListIndex); + switch (id) + { + case DIALOG_PRESET_LIST_RENAME: { + EditText et = (EditText) dialog.findViewById(R.id.list_edit); + if (et != null && curList != null) + { + et.setText(curList.getName()); + } + break; + } + case DIALOG_PRESET_LIST_DELETE: { + AlertDialog alertDlg = ((AlertDialog) dialog); + if (alertDlg != null && curList != null) + { + alertDlg.setTitle(curList.getName()); + alertDlg.setMessage(getString(R.string.presetlist_delete_name, curList.getName())); + } + break; + } + + case DIALOG_PRESET_LIST_AUTO_SET: { + AlertDialog alertDlg = ((AlertDialog) dialog); + if (alertDlg != null && curList != null) + { + alertDlg.setTitle(curList.getName()); + ((AlertDialog) dialog) + .setMessage(getString(R.string.presetlist_autoselect_name, curList.getName())); + } + break; + } + case DIALOG_SELECT_PRESET_LIST: { + AlertDialog alertDlg = ((AlertDialog) dialog); + ListView lv = (ListView) alertDlg.findViewById(R.id.list); + if (lv != null) + { + updateSelectPresetListDlg(lv); + } + break; + } + case DIALOG_PRESETS_LIST: { + AlertDialog alertDlg = ((AlertDialog) dialog); + if(alertDlg != null && curList != null) + { + alertDlg.setTitle(curList.getName()); + } + break; + } + case DIALOG_PICK_FREQUENCY: + { + if (dialog != null && mTunedStation != null) + { + FmConfig fmConfig = FmSharedPreferences.getFMConfiguration(); + ((FrequencyPickerDialog) dialog).updateSteps(fmConfig.getChSpacing()); + ((FrequencyPickerDialog) dialog).updateMinFreq(fmConfig.getLowerLimit()); + ((FrequencyPickerDialog) dialog).updateMaxFreq(fmConfig.getUpperLimit()); + ((FrequencyPickerDialog) dialog).UpdateFrequency(mTunedStation.getFrequency()); + } + break; + } + case DIALOG_PRESET_RENAME: + { + EditText et = (EditText) dialog.findViewById(R.id.list_edit); + if ((et != null) && (mPresetButtonStation != null)) + { + et.setText(mPresetButtonStation.getName()); + } + break; + } + case DIALOG_PRESET_OPTIONS: { + AlertDialog alertDlg = ((AlertDialog) dialog); + if ((alertDlg != null) && (mPresetButtonStation != null)) + { + alertDlg.setTitle(mPresetButtonStation.getName()); + } + break; + } + + default: + break; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(LOGTAG, "onActivityResult : requestCode -> "+ requestCode); + Log.d(LOGTAG, "onActivityResult : resultCode -> "+ resultCode); + if (requestCode ==ACTIVITY_RESULT_SETTINGS) + { + if (resultCode == RESULT_OK) + { + /* */ + if (data != null) + { + String action = data.getAction(); + if (action != null) + { + if (action.equals(Settings.RESTORE_FACTORY_DEFAULT_ACTION)) + { + RestoreDefaults(); + enableRadioOnOffUI(); + } + } + } + } //if ACTIVITY_RESULT_SETTINGS + }//if (resultCode == RESULT_OK) + } + + + + /** + * @return true if a wired headset is connected. + */ + boolean isWiredHeadsetAvailable() { + boolean bAvailable = false; + if(mService != null) + { + try + { + bAvailable = mService.isWiredHeadsetAvailable(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + Log.e(LOGTAG, "isWiredHeadsetAvailable: " + bAvailable); + return bAvailable; + } + + /** + * @return true if a internal antenna is available. + * + */ + boolean isAntennaAvailable() { + boolean bAvailable = false; + if(mService != null) + { + try + { + bAvailable = mService.isAntennaAvailable(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return bAvailable; + + } + boolean isCallActive(){ + boolean bCallActive = false; + if( mService != null) + { + try + { + bCallActive = mService.isCallActive(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return bCallActive; + } + + private Dialog createSearchDlg(int id, AlertDialog.Builder dlgBuilder) { + String[] items; + dlgBuilder.setIcon(R.drawable.ic_btn_search); + dlgBuilder.setTitle(getString(R.string.search_dialog_title)); + /* Pick RBDS or RDS */ + if(FmSharedPreferences.isRBDSStd()) + { + items = getResources().getStringArray(R.array.search_category_rbds_entries); + } else // if(FmSharedPreferences.isRDSStd()) + { + items = getResources().getStringArray(R.array.search_category_rds_entries); + } + + dlgBuilder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + String[] items; + String[] values; + + /* Pick RBDS or RDS */ + if(FmSharedPreferences.isRBDSStd()) + { + items = getResources().getStringArray(R.array.search_category_rbds_entries); + values = getResources(). + getStringArray(R.array.search_category_rbds_values); + } else // if(FmSharedPreferences.isRDSStd()) + { + items = getResources().getStringArray(R.array.search_category_rds_entries); + values = getResources(). + getStringArray(R.array.search_category_rds_values); + } + if ((items != null) && + (values != null) && + (item >= 0)) + { + if ( (item>=0) && (item <= items.length) && (item <= items.length) ) + { + DebugToasts("Search Stations for : " + items[item] + +" ("+values[item]+")", + Toast.LENGTH_SHORT); + int pty = Integer.parseInt(values[item]); + initiateSearch(pty); + } + } + removeDialog (DIALOG_SEARCH); + } + }); + + return dlgBuilder.create(); + } + + private Dialog createPresetOptionsDlg(int id, AlertDialog.Builder dlgBuilder) { + if(mPresetButtonStation != null) + { + dlgBuilder.setTitle(mPresetButtonStation.getName()); + ArrayList<String> arrayList = new ArrayList<String>(); + //PRESETS_OPTIONS_TUNE=0 + arrayList.add(getResources().getString(R.string.preset_tune)); + //PRESETS_OPTIONS_REPLACE=1 + arrayList.add(getResources().getString(R.string.preset_replace)); + //PRESETS_OPTIONS_RENAME=2 + arrayList.add(getResources().getString(R.string.preset_rename)); + //PRESETS_OPTIONS_DELETE=3 + arrayList.add(getResources().getString(R.string.preset_delete)); + String piString = mPresetButtonStation.getPIString(); + if (!TextUtils.isEmpty(piString)) + { + //PRESETS_OPTIONS_SEARCHPI=4 + arrayList.add(getResources().getString(R.string.preset_search, piString)); + } + + dlgBuilder.setCancelable(true); + dlgBuilder.setOnCancelListener (new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + mPresetButtonStation = null; + removeDialog(DIALOG_PRESET_OPTIONS); + } + }); + String[] items = new String [arrayList.size ()]; + arrayList.toArray(items); + dlgBuilder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + if(mPresetButtonStation != null) + { + switch(item) + { + case PRESETS_OPTIONS_TUNE: + { + // Tune to the station + tuneRadio(mPresetButtonStation.getFrequency()); + mPresetButtonStation = null; + break; + } + case PRESETS_OPTIONS_REPLACE: + { + // Replace preset Station with currently tuned station + Log.d(LOGTAG, "station - " + mPresetButtonStation.getName() + " (" + + mPresetButtonStation.getFrequency() + ")"); + mPresetButtonStation.Copy(mTunedStation); + mPresetButtonStation = null; + setupPresetLayout(); + mPrefs.Save(); + break; + } + case PRESETS_OPTIONS_RENAME: + { + // Rename + showDialog(DIALOG_PRESET_RENAME); + break; + } + case PRESETS_OPTIONS_DELETE: + { + // Delete + int curListIndex = FmSharedPreferences.getCurrentListIndex(); + FmSharedPreferences.removeStation(curListIndex, mPresetButtonStation); + mPresetButtonStation = null; + setupPresetLayout(); + mPrefs.Save(); + break; + } + case PRESETS_OPTIONS_SEARCHPI: + { + // SearchPI + String piString = mPresetButtonStation.getPIString(); + int pi = mPresetButtonStation.getPI(); + if ((!TextUtils.isEmpty(piString)) && (pi>0)) + { + initiatePISearch(pi); + } + mPresetButtonStation = null; + break; + } + default: + { + // Should not happen + mPresetButtonStation = null; + break; + } + }//switch item + }//if(mPresetButtonStation != null) + removeDialog (DIALOG_PRESET_OPTIONS); + }//onClick + }); + return dlgBuilder.create(); + } + return null; + } + + private Dialog createSleepDlg(int id, AlertDialog.Builder dlgBuilder) { + dlgBuilder.setTitle(R.string.dialog_sleep_title); + String[] items = getResources().getStringArray(R.array.sleep_duration_values); + + dlgBuilder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + String[] items = getResources().getStringArray(R.array.sleep_duration_values); + Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show(); + if ((item>=0) && (item <= items.length)) + { + long seconds = (long) (900 * (item + 1)); + initiateSleepTimer(seconds); + } + removeDialog (DIALOG_SLEEP); + } + }); + + return dlgBuilder.create(); + } + + + private Dialog createProgressDialog(int id) { + String msgStr=""; + String titleStr=""; + double frequency = mTunedStation.getFrequency() / 1000.0; + boolean bSearchActive = false; + + if(isSeekActive()) + { + msgStr = getString(R.string.msg_seeking); + bSearchActive=true; + } + else if(isScanActive()) + { + String ptyStr = PresetStation.parsePTY(mScanPty); + if (!TextUtils.isEmpty(ptyStr)) + { + msgStr = getString(R.string.msg_scanning_pty, ptyStr); + } + else + { + msgStr = getString(R.string.msg_scanning); + } + titleStr = getString(R.string.msg_search_title, ("" + frequency)); + bSearchActive=true; + } + else if(isSearchActive()) + { + msgStr = getString(R.string.msg_searching); + titleStr = getString(R.string.msg_searching_title); + bSearchActive=true; + } + + if(bSearchActive) { + mProgressDialog = new ProgressDialog(FMRadio.this); + if (mProgressDialog != null) { + mProgressDialog.setTitle(titleStr); + mProgressDialog.setMessage(msgStr); + mProgressDialog.setIcon(R.drawable.ic_launcher_fmradio); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.setButton(DialogInterface.BUTTON_POSITIVE, + getText(R.string.button_text_stop), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + cancelSearch(); + } + } + ); + mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + cancelSearch(); + } + } + ); + mProgressDialog.setOnKeyListener(new OnKeyListener() { + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + Log.d(LOGTAG, "OnKeyListener event received in ProgressDialog"+keyCode); + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case 126: //KeyEvent.KEYCODE_MEDIA_PLAY: + case 127: //KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_STOP: + return true; + } + return false; + } + } + ); + } + + Message msg = new Message(); + msg.what = TIMEOUT_PROGRESS_DLG; + mSearchProgressHandler.sendMessageDelayed(msg, SHOWBUSY_TIMEOUT); + } + return mProgressDialog; + } + + private void updateSelectPresetListDlg(ListView lv) { + if (lv != null) + { + List<PresetList> presetLists = FmSharedPreferences.getPresetLists(); + ListIterator<PresetList> presetIter; + presetIter = presetLists.listIterator(); + int numLists = presetLists.size(); + int curIndex = FmSharedPreferences.getCurrentListIndex(); + ArrayAdapter<String> typeAdapter = new ArrayAdapter<String>(this, + android.R.layout.simple_list_item_single_choice); + for (int stationIter = 0; stationIter < numLists; stationIter++) + { + PresetList temp = presetIter.next(); + if (temp != null) + { + typeAdapter.add(getString(R.string.presetlist_select_name, temp.getName())); + } + } + typeAdapter.add(getString(R.string.presetlist_add_new)); + lv.setAdapter(typeAdapter); + lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); + lv.clearChoices(); + if (curIndex >= numLists) + { + curIndex = 0; + } + if (lv.getCount() >= curIndex) + { + lv.setItemChecked(curIndex, true); + lv.setSelection(curIndex); + } else + { + lv.setItemChecked(0, true); + lv.setSelection(0); + } + } + } + + + private Dialog createPresetListEditDlg(int id, AlertDialog.Builder dlgBuilder) { + String[] items = getResources().getStringArray(R.array.presetlist_edit_category); + + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = FmSharedPreferences.getStationList(currentList); + + dlgBuilder.setIcon(R.drawable.alert_dialog_icon); + dlgBuilder.setTitle("\"" + ((curList == null)?"":curList.getName()) + "\""); + dlgBuilder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + switch(item) + { + case 0: + { + // Rename + showDialog(DIALOG_PRESET_LIST_RENAME); + break; + } + case 1: + { + // Auto-Select - Build Preset List + int currentList = + FmSharedPreferences.getCurrentListIndex(); + PresetList curList = + FmSharedPreferences.getStationList(currentList); + if (curList != null && curList.getStationCount() > 0) + { + /* Show a autoselect- deletes presets warning */ + showDialog(DIALOG_PRESET_LIST_AUTO_SET); + } + else + { + if (curList != null) { + curList.clear(); + } + /* Since the presets will be changed, + * reset the page number + */ + mPresetPageNumber = 0; + initiateSearchList(); + //curList.addDummyStations(); + setupPresetLayout(); + mPrefs.Save(); + } + + break; + } + case 2: + { + // Delete + showDialog(DIALOG_PRESET_LIST_DELETE); + break; + } + }//switch item + removeDialog(DIALOG_PRESETS_LIST); + }//onClick + }); + return dlgBuilder.create(); + } + + private Dialog createSelectPresetListDlg(int id, AlertDialog.Builder dlgBuilder) { + LayoutInflater factory = LayoutInflater.from(this); + final View listView = factory.inflate(R.layout.alert_dialog_list, null); + ListView lv = (ListView) listView.findViewById(R.id.list); + + if (lv != null) + { + updateSelectPresetListDlg(lv); + } else + { + return null; + } + dlgBuilder.setView(listView); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon); + dlgBuilder.setTitle(R.string.dialog_presetlist_title); + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + ListView lv = (ListView) listView + .findViewById(R.id.list); + int selectedPos = -1; + if (lv != null) + { + selectedPos = lv.getCheckedItemPosition(); + } + if (selectedPos >= 0) + { + if (selectedPos < FmSharedPreferences.getNumList()) + { + /* If the preset List was changed, reset the page number */ + if(selectedPos != FmSharedPreferences.getCurrentListIndex()) + { + mPresetPageNumber = 0; + } + FmSharedPreferences.setListIndex(selectedPos); + mPrefs.Save(); + } + else + { + String presetListName = getString(R.string.presetlist_prefix_name) + + (FmSharedPreferences.getNumList() + 1); + int newIndex = FmSharedPreferences + .createPresetList(presetListName); + FmSharedPreferences.setListIndex(newIndex); + showDialog(DIALOG_PRESET_LIST_RENAME); + } + } + setupPresetLayout(); + removeDialog(DIALOG_SELECT_PRESET_LIST); + } + }); + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_SELECT_PRESET_LIST); + } + }); + return dlgBuilder.create(); + } + + private Dialog createPresetRenameDlg(int id, AlertDialog.Builder dlgBuilder) { + if(mPresetButtonStation == null) + { + return null; + } + LayoutInflater factory = LayoutInflater.from(this); + final View textEntryView = factory.inflate( + R.layout.alert_dialog_text_entry, null); + dlgBuilder.setTitle(R.string.dialog_presetlist_rename_title); + dlgBuilder.setView(textEntryView); + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + //int curList = FmSharedPreferences.getCurrentListIndex(); + + EditText mTV = (EditText) textEntryView + .findViewById(R.id.list_edit); + CharSequence newName = null; + if (mTV != null) { + newName = mTV.getEditableText(); + } + String nName = String.valueOf(newName); + mPresetButtonStation.setName(nName); + mPresetButtonStation=null; + setupPresetLayout(); + mPrefs.Save(); + removeDialog(DIALOG_PRESET_RENAME); + } + }); + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_PRESET_RENAME); + } + }); + return(dlgBuilder.create()); + } + + private Dialog createPresetListRenameDlg(int id, AlertDialog.Builder dlgBuilder) { + LayoutInflater factory = LayoutInflater.from(this); + final View textEntryView = factory.inflate( + R.layout.alert_dialog_text_entry, null); + dlgBuilder.setTitle(R.string.dialog_presetlist_rename_title); + dlgBuilder.setView(textEntryView); + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + int curList = FmSharedPreferences.getCurrentListIndex(); + + EditText mTV = (EditText) textEntryView + .findViewById(R.id.list_edit); + CharSequence newName = null; + if (mTV != null) { + newName = mTV.getEditableText(); + } + String nName = String.valueOf(newName); + FmSharedPreferences.renamePresetList(nName, curList); + setupPresetLayout(); + mPrefs.Save(); + removeDialog(DIALOG_PRESET_LIST_RENAME); + } + }); + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_PRESET_LIST_RENAME); + } + }); + return(dlgBuilder.create()); + } + + private Dialog createPresetListDeleteDlg(int id, AlertDialog.Builder dlgBuilder) { + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = FmSharedPreferences.getStationList(currentList); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon).setTitle( + (curList == null)?"":curList.getName()); + dlgBuilder.setMessage(getString(R.string.presetlist_delete_name, + (curList == null)?"":curList.getName())); + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + int currentList = FmSharedPreferences.getCurrentListIndex(); + FmSharedPreferences.removeStationList(currentList); + /* Since the presets will be changed, + * reset the page number + */ + mPresetPageNumber = 0; + setupPresetLayout(); + mPrefs.Save(); + removeDialog(DIALOG_PRESET_LIST_DELETE); + } + }); + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_PRESET_LIST_DELETE); + } + }); + return(dlgBuilder.create()); + } + + private Dialog createPresetListAutoSelectWarnDlg(int id, AlertDialog.Builder dlgBuilder) { + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = FmSharedPreferences.getStationList(currentList); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon) + .setTitle(R.string.presetlist_autoselect_title); + dlgBuilder.setMessage(getString(R.string.presetlist_autoselect_name, + (curList == null)?"":curList.getName())); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + int currentList = + FmSharedPreferences.getCurrentListIndex(); + PresetList curList = + FmSharedPreferences.getStationList(currentList); + initiateSearchList(); + //curList.addDummyStations(); + setupPresetLayout(); + mPrefs.Save(); + removeDialog(DIALOG_PRESET_LIST_AUTO_SET); + } + }); + + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_PRESET_LIST_AUTO_SET); + } + }); + return(dlgBuilder.create()); + } + + + private Dialog createCmdTimeoutDlg(int id, AlertDialog.Builder dlgBuilder) { + if(mCommandActive > 0) + { + dlgBuilder.setIcon(R.drawable.alert_dialog_icon) + .setTitle(R.string.fm_command_timeout_title); + dlgBuilder.setMessage(R.string.fm_tune_timeout_msg); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + cleanupTimeoutHandler(); + removeDialog(DIALOG_CMD_TIMEOUT); + } + }); + + return(dlgBuilder.create()); + } + else + { + return (null); + } + } + + private Dialog createCmdFailedDlg(int id, AlertDialog.Builder dlgBuilder) { + dlgBuilder.setIcon(R.drawable.alert_dialog_icon) + .setTitle(R.string.fm_command_failed_title); + dlgBuilder.setMessage(R.string.fm_cmd_failed_msg); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + removeDialog(DIALOG_CMD_TIMEOUT); + mCommandFailed = CMD_NONE; + } + }); + + return(dlgBuilder.create()); + } + + private Dialog createCmdFailedDlgHdmiOn(int id) { + AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon) + .setTitle(R.string.fm_command_failed_title); + dlgBuilder.setMessage(R.string.fm_cmd_failed_msg_hdmi); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + removeDialog(DIALOG_CMD_TIMEOUT); + mCommandFailed = CMD_NONE; + } + }); + + return(dlgBuilder.create()); + } + + private Dialog createCmdFailedDlgCallOn(int id) { + AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon) + .setTitle(R.string.fm_command_failed_title); + dlgBuilder.setMessage(R.string.fm_cmd_failed_call_on); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + removeDialog(DIALOG_CMD_TIMEOUT); + mCommandFailed = CMD_NONE; + } + }); + + return(dlgBuilder.create()); + } + private void RestoreDefaults() { + FmSharedPreferences.SetDefaults(); + mPrefs.Save(); + } + + private View.OnLongClickListener mFrequencyViewClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View v) { + showDialog(DIALOG_PICK_FREQUENCY); + return true; + } + }; + + private View.OnClickListener mForwardClickListener + = new View.OnClickListener() { + public void onClick(View v) { + int frequency = FmSharedPreferences.getNextTuneFrequency(); + Log.d(LOGTAG, "Tune Up: to " + frequency); + tuneRadio(frequency); + } + }; + + private View.OnClickListener mBackClickListener + = new View.OnClickListener() { + public void onClick(View v) { + int frequency = FmSharedPreferences.getPrevTuneFrequency(); + Log.d(LOGTAG, "Tune Down: to " + frequency); + tuneRadio(frequency); + } + }; + + private View.OnLongClickListener mForwardLongClickListener + = new View.OnLongClickListener() { + public boolean onLongClick(View view) { + //mForwardButton.startAnimation(mAnimation); + SeekNextStation(); + //enableScanningOnOffUI(); + return true; + } + }; + + private View.OnLongClickListener mBackLongClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View view) { + //mBackButton.startAnimation(mAnimation); + SeekPreviousStation(); + //enableScanningOnOffUI(); + return true; + } + }; + + private View.OnClickListener mPresetListClickListener = new View.OnClickListener() { + public void onClick(View v) { + showDialog(DIALOG_SELECT_PRESET_LIST); + } + }; + private View.OnLongClickListener mPresetListButtonOnLongClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View view) { + showDialog(DIALOG_PRESETS_LIST); + return true; + } + }; + + private View.OnClickListener mPresetsPageClickListener = new View.OnClickListener() { + public void onClick(View v) { + mPresetPageNumber++; + setupPresetLayout(); + } + }; + + private View.OnClickListener mPresetButtonClickListener = new View.OnClickListener() { + public void onClick(View view) { + PresetStation station = (PresetStation) view.getTag(); + if (station != null) + { + Log.d(LOGTAG, "station - " + station.getName() + " (" + + station.getFrequency() + ")"); + //mTunedStation.Copy(station); + tuneRadio(station.getFrequency()); + view.startAnimation(mAnimation); + } + } + }; + + private View.OnLongClickListener mPresetButtonOnLongClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View view) { + PresetStation station = (PresetStation) view.getTag(); + mPresetButtonStation = station; + if (station != null) + { + showDialog(DIALOG_PRESET_OPTIONS); + } else + { + addToPresets(); + view.startAnimation(mAnimation); + } + return true; + } + }; + + final FrequencyPickerDialog.OnFrequencySetListener mFrequencyChangeListener + = new FrequencyPickerDialog.OnFrequencySetListener() { + public void onFrequencySet(FrequencyPicker view, int frequency) { + Log.d(LOGTAG, "mFrequencyChangeListener: onFrequencyChanged to : "+frequency); + tuneRadio(frequency); + } + }; + + private View.OnClickListener mMuteModeClickListener + = new View.OnClickListener() { + public void onClick(View v) { + boolean bStatus = false; + if(mService != null) + { + try + { + if (true == isMuted()) + { + bStatus = mService.unMute(); + } else + { + bStatus = mService.mute(); + } + + if(bStatus) + { + setMuteModeButtonImage(true); + v.startAnimation(mAnimation); + } + else + { + mCommandFailed = CMD_MUTE; + if( isCallActive() ) { + showDialog(DIALOG_CMD_FAILED_CALL_ON); + } else { + showDialog(DIALOG_CMD_FAILED); + } + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + }; + + private Handler mEnableRadioHandler = new Handler(); + private Handler mDisableRadioHandler = new Handler(); + private Runnable mEnableRadioTask = new Runnable() { + public void run() { + enableRadio(); + mOnOffButton.setEnabled(true); + mOnOffButton.setClickable(true); + mOnOffButton.setOnClickListener(mTurnOnOffClickListener); + } + }; + private Runnable mDisableRadioTask = new Runnable() { + public void run() { + disableRadio(); + mOnOffButton.setEnabled(true); + mOnOffButton.setClickable(true); + mOnOffButton.setOnClickListener(mTurnOnOffClickListener); + } + }; + private View.OnClickListener mTurnOnOffClickListener + = new View.OnClickListener() { + public void onClick(View v) { + mOnOffButton.setEnabled(false); + mOnOffButton.setClickable(false); + mOnOffButton.setOnClickListener(null); + if (isFmOn()) + { + mEnableRadioHandler.removeCallbacks(mEnableRadioTask); + mDisableRadioHandler.removeCallbacks(mDisableRadioTask); + mDisableRadioHandler.postDelayed(mDisableRadioTask, 0); + + } else + { + mDisableRadioHandler.removeCallbacks(mDisableRadioTask); + mEnableRadioHandler.removeCallbacks(mEnableRadioTask); + mEnableRadioHandler.postDelayed(mEnableRadioTask, 0); + } + cleanupTimeoutHandler(); + } + }; + private void setTurnOnOffButtonImage() { + if (isFmOn() == true) + { + mOnOffButton.setImageResource(R.drawable.ic_btn_onoff); + } else + { + /* Find a icon to indicate off */ + mOnOffButton.setImageResource(R.drawable.ic_btn_onoff); + } + } + + + private void setMuteModeButtonImage(boolean notify) { + String fmMutedString; + if (isMuted() == true) + { + mMuteButton.setImageResource(R.drawable.ic_silent_mode); + fmMutedString = "FM Radio Muted"; + } else + { + /* Find a icon for Stations */ + mMuteButton.setImageResource(R.drawable.ic_silent_mode_off); + fmMutedString = "FM Radio Playing"; + } + if (notify) + { + //Toast.makeText(this, fmMutedString, Toast.LENGTH_SHORT).show(); + Log.d(LOGTAG, fmMutedString); + } + } + + private void enableRadio() { + mIsScaning = false; + mIsSeeking = false; + mIsSearching=false; + boolean bStatus = false; + if (isHdmiOn()) + { + showDialog(DIALOG_CMD_FAILED_HDMI_ON); + } else + { + if(mService != null) + { + try + { + if( false == mService.isFmOn() && isAntennaAvailable()) { + bStatus = mService.fmOn(); + if(bStatus) + { + tuneRadio(FmSharedPreferences.getTunedFrequency()); + enableRadioOnOffUI(); + } + else + { + Log.e(LOGTAG, " mService.fmOn failed"); + mCommandFailed = CMD_FMON; + if( isCallActive() ) { + enableRadioOnOffUI(); + showDialog(DIALOG_CMD_FAILED_CALL_ON); + } else { + showDialog(DIALOG_CMD_FAILED); + } + } + } else { + enableRadioOnOffUI(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + } + + private void disableRadio() { + boolean bStatus = false; + boolean bSpeakerPhoneOn = isSpeakerEnabled(); + cancelSearch(); + endSleepTimer(); + if( mRecording ) + { + //Stop if there is an ongoing Record + stopRecording(); + } + if(mService != null) + { + try + { + if( bSpeakerPhoneOn ) + { + mService.enableSpeaker(false); + } + bStatus = mService.fmOff(); + enableRadioOnOffUI(); + if(bStatus == false) + { + mCommandFailed = CMD_FMOFF; + Log.e(LOGTAG, " mService.fmOff failed"); + //showDialog(DIALOG_CMD_FAILED); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + private void resetRadio() { + boolean bSpeakerPhoneOn = isSpeakerEnabled(); + resetSearch(); + endSleepTimer(); + if(mRecording) + { + //Stop if there is an ongoing Record + stopRecording(); + } + if(mService != null) + { + try + { + if(bSpeakerPhoneOn) + { + mService.enableSpeaker(false); + } + mService.fmRadioReset(); + enableRadioOnOffUI(false); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + public static boolean fmConfigure() { + boolean bStatus = true; + if(mService != null) + { + try + { + bStatus = mService.fmReconfigure(); + if(bStatus == false) + { + mCommandFailed = CMD_FMCONFIGURE; + Log.e(LOGTAG, " mService.fmReconfigure failed"); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return bStatus; + } + public static void fmAutoAFSwitch() { + boolean bStatus = false; + if(mService != null) + { + try + { + bStatus = mService.enableAutoAF(FmSharedPreferences.getAutoAFSwitch()); + if(bStatus == false) + { + mCommandFailed = CMD_SET_AUTOAF; + Log.e(LOGTAG, " mService.enableAutoAF failed"); + //showDialog(DIALOG_CMD_FAILED); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + public static void fmAudioOutputMode() { + boolean bStatus = false; + if(mService != null) + { + try + { + bStatus = mService.enableStereo(FmSharedPreferences.getAudioOutputMode()); + if(bStatus == false) + { + mCommandFailed = CMD_SET_AUDIO_MODE; + Log.e(LOGTAG, " mService.enableStereo failed"); + //showDialog(DIALOG_CMD_FAILED); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + private void startRecording() { + if(mService != null) + { + try + { + mRecording = mService.startRecording(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + //Initiate record timer thread here + if( mRecording == true ) { + int durationInMins = FmSharedPreferences.getRecordDuration(); + Log.e(LOGTAG, " Fected duration:" + durationInMins ); + initiateRecordDurationTimer( durationInMins ); + } + } + } + + private void stopRecording() { + mRecording = false; + DebugToasts("Stopped Recording", Toast.LENGTH_SHORT); + if( null != mRecordUpdateHandlerThread) { + mRecordUpdateHandlerThread.interrupt(); + } + if( null != mRecordingMsgTV ) { + mRecordingMsgTV.setVisibility(View.INVISIBLE); + } + if(mService != null) { + try + { + mService.stopRecording(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + mRecordDuration = 0; + } + + private boolean isRecording() { + mRecording = false; + if(mService != null) + { + try + { + mRecording = mService.isFmRecordingOn(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return(mRecording); + } + + private boolean isSpeakerEnabled() { + boolean speakerEnabled = false; + if(mService != null) + { + try + { + speakerEnabled = mService.isSpeakerEnabled(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return(speakerEnabled); + } + + private void addToPresets() { + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetStation selectedStation = getCurrentTunedStation(); + FmSharedPreferences.addStation(selectedStation.getName(), selectedStation + .getFrequency(), currentList); + setupPresetLayout(); + } + + private void enableRadioOnOffUI() { + boolean bEnable = isFmOn(); + /* Disable if no antenna/headset is available */ + if(!isAntennaAvailable() ) + { + bEnable = false; + } + enableRadioOnOffUI(bEnable); + } + + private void enableRadioOnOffUI(boolean bEnable) { + + if (mMuteButton != null) { + mMuteButton.setEnabled(bEnable); + setMuteModeButtonImage(false); + } + if (bEnable) + { + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setOnLongClickListener(mFrequencyViewClickListener); + } + if (mRadioTextScroller != null) { + mRadioTextScroller.startScroll(); + } + if (mSleepMsgTV != null && !isSleepTimerActive()) + { + mSleepMsgTV.setText(""); + } + if (mRecordingMsgTV != null && !isRecording()) + { + mRecordingMsgTV.setText(""); + } + } else + { + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setOnLongClickListener(null); + } + if (mRadioTextScroller != null) { + mRadioTextScroller.stopScroll(); + } + } + + if (mForwardButton != null) { + mForwardButton.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mBackButton != null) { + mBackButton.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mStationCallSignTV != null) { + mStationCallSignTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mProgramTypeTV != null) { + mProgramTypeTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mSleepMsgTV != null) { + mSleepMsgTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mRecordingMsgTV != null) { + mRecordingMsgTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if(mERadioTextTV != null) { + mERadioTextTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mProgramServiceTV != null) { + mProgramServiceTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + + if (!isAntennaAvailable()) + { + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(View.VISIBLE); + mRadioTextTV.setText(getString(R.string.msg_noantenna)); + mRadioTextScroller.mOriginalString = getString(R.string.msg_noantenna); + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(false); + } + } + else if (isCallActive()) + { + if (mRadioTextTV != null) { + mRadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + } + if (mERadioTextTV != null) + mERadioTextTV.setText(""); + if (mOnOffButton != null) { + mOnOffButton.setEnabled(false); + } + } + else + { + if (mRadioTextTV != null) { + mRadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + } + if (mERadioTextTV != null) + mERadioTextTV.setText(""); + if (mOnOffButton != null) { + mOnOffButton.setEnabled(true); + } + } + + /* Disable displaying RSSI */ + //mRSSI.setVisibility(((bEnable == true) ? View.VISIBLE + // : View.INVISIBLE)); + if (mStereoTV != null) { + mStereoTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + for (int nButton = 0; nButton < MAX_PRESETS_PER_PAGE; nButton++) + { + if (mPresetButtons[nButton] != null) { + mPresetButtons[nButton].setEnabled(bEnable); + } + } + if (mPresetListButton != null) { + mPresetListButton.setEnabled(bEnable); + } + if (mPresetPageButton != null) { + mPresetPageButton.setEnabled(bEnable && + (FmSharedPreferences.getListStationCount() >= MAX_PRESETS_PER_PAGE)); + } + } + + private void resetSearchProgress() { + Message msg = new Message(); + msg.what = END_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + + private void updateSearchProgress() { + boolean searchActive = isScanActive() || isSeekActive() || isSearchActive(); + if(searchActive) + { + synchronized (this) + { + if(mProgressDialog == null) + { + showDialog(DIALOG_PROGRESS_PROGRESS); + } + else + { + Message msg = new Message(); + msg.what = UPDATE_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + } + } + else + { + Message msg = new Message(); + msg.what = END_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + } + + private void setupPresetLayout() { + int numStations = FmSharedPreferences.getListStationCount(); + int addedStations = 0; + + /* + * Validate mPresetPageNumber (Preset Page Number) + */ + if (mPresetPageNumber > ((numStations) / MAX_PRESETS_PER_PAGE)) + { + mPresetPageNumber = 0; + } + + /* + * For every station, save the station as a tag and update the display + * on the preset Button. + */ + for (int buttonIndex = 0; (buttonIndex < MAX_PRESETS_PER_PAGE); buttonIndex++) + { + if (mPresetButtons[buttonIndex] != null) + { + int stationIdex = (mPresetPageNumber * MAX_PRESETS_PER_PAGE) + + buttonIndex; + PresetStation station = FmSharedPreferences.getStationInList(stationIdex); + String display = ""; + if (station != null) + { + display = station.getName(); + mPresetButtons[buttonIndex].setText(display); + mPresetButtons[buttonIndex].setTag(station); + addedStations++; + } else + { + mPresetButtons[buttonIndex].setText(display); + mPresetButtons[buttonIndex].setTag(station); + } + } + } + /* + * Enable "Next" Page: Only if more than 4 presets are added, + * else there is nothing more + */ + mPresetPageButton.setEnabled(isFmOn() && (numStations >= MAX_PRESETS_PER_PAGE)); + mPresetListButton.setText(FmSharedPreferences.getListName(FmSharedPreferences + .getCurrentListIndex())); + } + + private void updateStationInfoToUI() { + double frequency = mTunedStation.getFrequency() / 1000.0; + mTuneStationFrequencyTV.setText("" + frequency); + mStationCallSignTV.setText(mTunedStation.getPIString()); + mProgramTypeTV.setText(mTunedStation.getPtyString()); + mRadioTextTV.setText(""); + mERadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + mRadioTextScroller.mStringlength = 0; + mRadioTextScroller.mIteration = 0; + mProgramServiceTV.setText(""); + mStereoTV.setText(""); + setupPresetLayout(); + } + + + private boolean isFmOn() { + boolean bOn = false; + if(mService != null) + { + try + { + bOn = mService.isFmOn(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return(bOn); + } + + private boolean isAnalogModeEnabled() { + boolean aEnabled = false; + if (mService != null) + { + try + { + aEnabled = mService.isAnalogModeEnabled(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return (aEnabled); + } + + private boolean isMuted() { + boolean bMuted = false; + if(mService != null) + { + try + { + bMuted = mService.isMuted(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return(bMuted); + } + + private boolean isScanActive() { + return(mIsScaning); + } + + private boolean isSeekActive() { + return(mIsSeeking); + } + private boolean isSearchActive() { + return(mIsSearching); + } + + public static PresetStation getCurrentTunedStation() { + return mTunedStation; + } + + private void SeekPreviousStation() { + Log.d(LOGTAG, "SeekPreviousStation"); + if(mService != null) + { + try + { + if(!isSeekActive()) + { + mIsSeeking = mService.seek(false); + if(mIsSeeking == false) + { + mCommandFailed = CMD_SEEK; + Log.e(LOGTAG, " mService.seek failed"); + showDialog(DIALOG_CMD_FAILED); + } + + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + updateSearchProgress(); + } + + private void SeekNextStation() { + Log.d(LOGTAG, "SeekNextStation"); + if(mService != null) + { + try + { + if(!isSeekActive()) + { + mIsSeeking = mService.seek(true); + if(mIsSeeking == false) + { + mCommandFailed = CMD_SEEK; + Log.e(LOGTAG, " mService.seek failed"); + showDialog(DIALOG_CMD_FAILED); + } + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + updateSearchProgress(); + } + + /** Scan related */ + private void initiateSearch(int pty) { + synchronized (this) + { + mIsScaning = true; + if(mService != null) + { + try + { + mIsScaning = mService.scan(pty); + if(mIsScaning == false) + { + mCommandFailed = CMD_SCAN; + Log.e(LOGTAG, " mService.scan failed"); + showDialog(DIALOG_CMD_FAILED); + } + else + { + mScanPty = pty; + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + updateSearchProgress(); + } + } + } + + /** SEEK Station with the matching PI */ + private void initiatePISearch(int pi) { + Log.d(LOGTAG, "initiatePISearch"); + if(mService != null) + { + try + { + if(!isSeekActive()) + { + mIsSeeking = mService.seekPI(pi); + if(mIsSeeking == false) + { + mCommandFailed = CMD_SEEKPI; + Log.e(LOGTAG, " mService.seekPI failed"); + showDialog(DIALOG_CMD_FAILED); + } + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + updateSearchProgress(); + } + + private void resetSearch() { + mIsScaning = false; + mIsSeeking = false; + mIsSearching = false; + resetSearchProgress(); + } + + private void cancelSearch() { + synchronized (this) + { + if(mService != null) + { + try + { + if( (mIsScaning == true) + || (mIsSeeking == true) + || (mIsSearching == true)) + { + mService.cancelSearch(); + mIsScaning = false; + mIsSeeking = false; + mIsSearching=false; + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + updateSearchProgress(); + } + + /** get Strongest Stations */ + private void initiateSearchList() { + synchronized (this) + { + mIsSearching = false; + if(mService != null) + { + try + { + mIsSearching = mService.searchStrongStationList(NUM_AUTO_PRESETS_SEARCH); + if(mIsSearching == false) + { + mCommandFailed = CMD_SEARCHLIST; + Log.e(LOGTAG, " mService.searchStrongStationList failed"); + showDialog(DIALOG_CMD_FAILED); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + updateSearchProgress(); + } + } + } + + private static final int UPDATE_PROGRESS_DLG = 1; + private static final int END_PROGRESS_DLG = 2; + private static final int TIMEOUT_PROGRESS_DLG = 3; + private static final int SHOWBUSY_TIMEOUT = 300000; + private Handler mSearchProgressHandler = new Handler() { + public void handleMessage(Message msg) { + if (msg.what == UPDATE_PROGRESS_DLG) { + //Log.d(LOGTAG, "mTimeoutHandler: UPDATE_PROGRESS_DLG"); + if(mProgressDialog != null) + { + double frequency = mTunedStation.getFrequency() / 1000.0; + String titleStr = getString(R.string.msg_search_title, ("" + frequency)); + + mProgressDialog.setTitle(titleStr); + } + } + else if (msg.what == END_PROGRESS_DLG) { + //Log.d(LOGTAG, "mTimeoutHandler: END_PROGRESS_DLG"); + mSearchProgressHandler.removeMessages(END_PROGRESS_DLG); + mSearchProgressHandler.removeMessages(UPDATE_PROGRESS_DLG); + mSearchProgressHandler.removeMessages(TIMEOUT_PROGRESS_DLG); + removeDialog(DIALOG_PROGRESS_PROGRESS); + mProgressDialog=null; + } + else if(msg.what == TIMEOUT_PROGRESS_DLG) { + cancelSearch(); + } + } + }; + + /** Sleep Handling: After the timer expires, the app needs to shut down */ + private static final int SLEEPTIMER_EXPIRED = 0x1001; + private static final int SLEEPTIMER_UPDATE = 0x1002; + private Thread mSleepUpdateHandlerThread = null; + /* + * Phone time when the App has to be shut down, calculated based on what the + * user configured + */ + private static long mSleepAtPhoneTime = 0; + + private void initiateSleepTimer(long seconds) { + mSleepAtPhoneTime = (SystemClock.elapsedRealtime()) + (seconds * 1000); + Log.d(LOGTAG, "Sleep in seconds : " + seconds); + initiateSleepThread(); + } + private void initiateSleepThread() { + if (mSleepUpdateHandlerThread == null) + { + mSleepUpdateHandlerThread = new Thread(null, doSleepProcessing, + "SleepUpdateThread"); + } + /* Launch he dummy thread to simulate the transfer progress */ + Log.d(LOGTAG, "Thread State: " + mSleepUpdateHandlerThread.getState()); + if (mSleepUpdateHandlerThread.getState() == Thread.State.TERMINATED) + { + mSleepUpdateHandlerThread = new Thread(null, doSleepProcessing, + "SleepUpdateThread"); + } + /* If the thread state is "new" then the thread has not yet started */ + if (mSleepUpdateHandlerThread.getState() == Thread.State.NEW) + { + mSleepUpdateHandlerThread.start(); + } + } + + private void endSleepTimer() { + mSleepAtPhoneTime = 0; + if( null != mSleepUpdateHandlerThread) { + mSleepUpdateHandlerThread.interrupt(); + } + if( null != mSleepMsgTV ) { + mSleepMsgTV.setVisibility(View.INVISIBLE); + } + // Log.d(LOGTAG, "endSleepTimer"); + } + + private boolean hasSleepTimerExpired() { + boolean expired = true; + if (isSleepTimerActive()) + { + long timeNow = ((SystemClock.elapsedRealtime())); + //Log.d(LOGTAG, "hasSleepTimerExpired - " + mSleepAtPhoneTime + " now: "+ timeNow); + if (timeNow < mSleepAtPhoneTime) + { + expired = false; + } + } + return expired; + } + + private boolean isSleepTimerActive() { + boolean active = false; + if (mSleepAtPhoneTime > 0) + { + active = true; + } + return active; + } + + private void updateExpiredSleepTime() { + int vis = View.INVISIBLE; + if (isSleepTimerActive()) + { + long timeNow = ((SystemClock.elapsedRealtime())); + if (mSleepAtPhoneTime >= timeNow) + { + long seconds = (mSleepAtPhoneTime - timeNow) / 1000; + String sleepMsg = makeTimeString(seconds); + //Log.d(LOGTAG, "updateExpiredSleepTime: " + sleepMsg); + mSleepMsgTV.setText(sleepMsg); + if (seconds < SLEEP_TOGGLE_SECONDS) + { + int nowVis = mSleepMsgTV.getVisibility(); + vis = (nowVis == View.INVISIBLE) ? View.VISIBLE + : View.INVISIBLE; + } else + { + vis = View.VISIBLE; + } + } else + { + /* Clean up timer */ + mSleepAtPhoneTime = 0; + } + } + mSleepMsgTV.setVisibility(vis); + } + + private Handler mUIUpdateHandlerHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) + { + case SLEEPTIMER_EXPIRED: { + mSleepAtPhoneTime = 0; + //Log.d(LOGTAG, "mUIUpdateHandlerHandler - SLEEPTIMER_EXPIRED"); + DebugToasts("Turning Off FM Radio", Toast.LENGTH_SHORT); + disableRadio(); + return; + } + case SLEEPTIMER_UPDATE: { + //Log.d(LOGTAG, "mUIUpdateHandlerHandler - SLEEPTIMER_UPDATE"); + updateExpiredSleepTime(); + break; + } + case RECORDTIMER_EXPIRED: { + Log.d(LOGTAG, "mUIUpdateHandlerHandler - RECORDTIMER_EXPIRED"); + mRecordDuration = 0; + //Clear the Recorder text + mRecordingMsgTV.setText(""); + if (mRecording != false) + { + DebugToasts("Stop Recording", Toast.LENGTH_SHORT); + stopRecording(); + } + return; + } + case RECORDTIMER_UPDATE: { + Log.d(LOGTAG, "mUIUpdateHandlerHandler - RECORDTIMER_UPDATE"); + updateExpiredRecordTime(); + break; + } + default: + break; + } + super.handleMessage(msg); + } + }; + + /* Thread processing */ + private Runnable doSleepProcessing = new Runnable() { + public void run() { + boolean sleepTimerExpired = hasSleepTimerExpired(); + while ((sleepTimerExpired == false)&& (!Thread.currentThread().isInterrupted()) ) + { + try + { + Thread.sleep(500); + Message statusUpdate = new Message(); + statusUpdate.what = SLEEPTIMER_UPDATE; + mUIUpdateHandlerHandler.sendMessage(statusUpdate); + sleepTimerExpired = hasSleepTimerExpired(); + }catch ( Exception ex ) { + Log.d( LOGTAG, "RunningThread InterruptedException"); + Thread.currentThread().interrupt(); + }//try + } + if( true == sleepTimerExpired) { + Message finished = new Message(); + finished.what = SLEEPTIMER_EXPIRED; + mUIUpdateHandlerHandler.sendMessage(finished); + } + } + }; + + private static StringBuilder sFormatBuilder = new StringBuilder(); + private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale + .getDefault()); + private static final Object[] sTimeArgs = new Object[5]; + + private String makeTimeString(long secs) { + String durationformat = getString(R.string.durationformat); + + /* + * Provide multiple arguments so the format can be changed easily by + * modifying the xml. + */ + sFormatBuilder.setLength(0); + + final Object[] timeArgs = sTimeArgs; + timeArgs[0] = secs / 3600; + timeArgs[1] = secs / 60; + timeArgs[2] = (secs / 60) % 60; + timeArgs[3] = secs; + timeArgs[4] = secs % 60; + + return sFormatter.format(durationformat, timeArgs).toString(); + } + + private void tuneRadio(int frequency){ + /* Issue the tune command only if tuneCommand is already not active */ + if( (mService != null) && (mCommandActive != CMD_TUNE) && isFmOn() ) + { + boolean bStatus = false; + try + { + + bStatus = mService.tune(frequency); + if(bStatus) + { + postTimeoutHandler(CMD_TUNE); + } + else + { + if(isFmOn()) + { + mCommandFailed = CMD_TUNE; + Log.e(LOGTAG, " mService.tune failed"); + showDialog(DIALOG_CMD_FAILED); + } + } + mTunedStation.setName(""); + mTunedStation.setPI(0); + mTunedStation.setPty(0); + updateStationInfoToUI(); + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + else + { + Log.e(LOGTAG, "Delayed Tune handler stopped"); + } + } + + /* Start a Command timeout + */ + private synchronized void postTimeoutHandler(int cmd){ + + mCommandActive = cmd; + mCommandTimeoutHandler.sendEmptyMessageDelayed (MSG_CMD_TIMEOUT, CMD_TIMEOUT_DELAY_MS); + } + + /* Stop the Command timeout + */ + private synchronized void cleanupTimeoutHandler(){ + mCommandActive = CMD_NONE; + mCommandTimeoutHandler.removeMessages(MSG_CMD_TIMEOUT); + } + /* Command timeout Handler + Routine to handle the Command timeouts for FM operations + that return asynchronous event callbacks + */ + private Handler mCommandTimeoutHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) + { + case MSG_CMD_TIMEOUT: + { + if (mCommandActive>0) + { + Log.d(LOGTAG, "mCommandTimeoutHandler: Cmd failed : "+mCommandActive); + mCommandTimeoutHandler.removeMessages(MSG_CMD_TIMEOUT); + showDialog(DIALOG_CMD_TIMEOUT); + return; + } + break; + }//case MSG_CMD_TIMEOUT + }//switch + }//handleMessage + }; + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + Log.d(LOGTAG, "KEY event received"+keyCode); + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case 126: //KeyEvent.KEYCODE_MEDIA_PLAY: + case 127: //KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_STOP: + return true; + } + return super.onKeyDown(keyCode, event); + } + private void resetFMStationInfoUI() + { + mTunedStation.setFrequency(FmSharedPreferences.getTunedFrequency()); + mTunedStation.setName(""); + mTunedStation.setPI(0); + mTunedStation.setRDSSupported(false); + //mTunedStation.setPI(20942); + mTunedStation.setPty(0); + mRadioTextTV.setText(""); + mERadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + mProgramServiceTV.setText(""); + mRadioTextScroller.stopScroll(); + updateStationInfoToUI(); + } + + final Runnable mRadioEnabled = new Runnable() { + public void run() { + /* Update UI to FM On State */ + enableRadioOnOffUI(true); + /* Tune to the last tuned frequency */ + tuneRadio(FmSharedPreferences.getTunedFrequency()); + } + }; + + final Runnable mRadioDisabled = new Runnable() { + public void run() { + /* Update UI to FM Off State */ + cleanupTimeoutHandler(); + endSleepTimer(); + stopRecording(); + cancelSearch(); + enableRadioOnOffUI(false); + } + }; + final Runnable mRadioReset = new Runnable() { + public void run() { + /* Update UI to FM Reset (Off) State */ + resetRadio(); + } + }; + + final Runnable mUpdateStationInfo = new Runnable() { + public void run() { + cleanupTimeoutHandler(); + PresetStation station = new PresetStation("", FmSharedPreferences.getTunedFrequency()); + if (station != null) { + mTunedStation.Copy(station); + } + updateSearchProgress(); + resetFMStationInfoUI(); + } + }; + + final Runnable mSearchComplete = new Runnable() { + public void run() { + Log.d(LOGTAG, "mSearchComplete: "); + mScanPty=0; + mIsScaning = false; + mIsSeeking = false; + mIsSearching = false; + updateSearchProgress(); + resetFMStationInfoUI(); + } + }; + + final Runnable mSearchListComplete = new Runnable() { + public void run() { + Log.d(LOGTAG, "mSearchListComplete: "); + mIsSearching = false; + + /* Now get the list*/ + if(mService != null) + { + try + { + int[] searchList = mService.getSearchList(); + if (searchList != null) + { + /* Add the stations into the preset list */ + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = + FmSharedPreferences.getStationList(currentList); + if (curList != null) { + curList.clear(); + } + mPresetPageNumber = 0; + + for( int station=0; + (station < searchList.length) && (station < NUM_AUTO_PRESETS_SEARCH); + station++) + { + int frequency = searchList[station]; + Log.d(LOGTAG, "mSearchListComplete: [" + station + "] = " + frequency); + if( (frequency <= FmSharedPreferences.getUpperLimit()) + && (frequency >= FmSharedPreferences.getLowerLimit())) + { + FmSharedPreferences.addStation("", searchList[station],currentList); + } + + if(frequency == 0) + { + break; + } + } + mPrefs.Save(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + updateSearchProgress(); + resetFMStationInfoUI(); + setupPresetLayout(); + } + }; + + final Runnable mOnMute = new Runnable() { + public void run() { + setMuteModeButtonImage(true); + } + }; + + final Runnable mOnStereo = new Runnable() { + public void run() { + if(FMRADIO_UI_STATION_AUDIO_STEREO == mStereo) + { + mStereoTV.setText(R.string.audio_type_stereo); + } + else if(FMRADIO_UI_STATION_AUDIO_MONO == mStereo) + { + mStereoTV.setText(R.string.audio_type_mono); + } + else + { + mStereoTV.setText(""); + } + } + }; + + final Runnable mUpdateRadioText = new Runnable() { + public void run() { + String str = ""; + if(mService != null && isFmOn()) + { + try + { + /* Get Radio Text and update the display */ + str = mService.getRadioText(); + + /* Update only if all the characters are printable */ + if (TextUtils.isPrintableAsciiOnly(str)) + { + Log.d(LOGTAG, "mUpdateRadioText: Updatable string: [" + str + "]"); + mRadioTextTV.setText(str); + mRadioTextScroller.mOriginalString = str; + } + /* Rest the string to empty*/ + else if (TextUtils.isEmpty(str)) + { + mRadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + } else + { + //Log.d(LOGTAG, "mUpdateRadioText: Leaving old string " + mRadioTextTV.getText()); + } + + /* Get PTY and PI and update the display */ + int tempInt =mService.getProgramType(); + /* Save PTY */ + mTunedStation.setPty(tempInt); + mProgramTypeTV.setText(PresetStation.parsePTY(tempInt)); + tempInt =mService.getProgramID(); + mStationCallSignTV.setText(PresetStation.parsePI(tempInt)); + if(tempInt != 0) + { + mTunedStation.setPI(tempInt); + } + /* For non-Empty, non-Printable string, just leave the + existing old string + */ + mRadioTextScroller.startScroll(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + }; + + final Runnable mUpdateExtenRadioText = new Runnable() { + public void run() { + String str = ""; + if(mService != null && isFmOn()) + { + try + { + /* Get Extended Radio Text and update the display */ + str = mService.getExtenRadioText(); + if (TextUtils.isEmpty(str)) + mERadioTextTV.setText(""); + else + mERadioTextTV.setText(str); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + }; + + /* Create runnable for posting */ + final Runnable mUpdateProgramService = new Runnable() { + public void run() { + String str = ""; + if(mService != null) + { + try + { + /* Get the Station PS and update the display */ + str = mService.getProgramService(); + /* Update only if all the characters are printable */ + //if(isStringPrintable(str)) + if (TextUtils.isPrintableAsciiOnly(str)) + { + Log.d(LOGTAG, "mUpdateProgramService: Updatable string: [" + str + "]"); + mProgramServiceTV.setText(str); + } + /* Rest the string to empty*/ + else if (TextUtils.isEmpty(str)) + { + mProgramServiceTV.setText(""); + } else + { + /* For non-Empty, non-Printable string, just leave the + existing old string + */ + //Log.d(LOGTAG, "mUpdateProgramService: Leaving old string " + mProgramServiceTV.getText()); + } + /* Get PTY and PI and update the display */ + int tempInt =mService.getProgramType(); + /* Save PTY */ + mTunedStation.setPty(tempInt); + + mProgramTypeTV.setText(PresetStation.parsePTY(tempInt)); + tempInt =mService.getProgramID(); + /* Save the program ID */ + if(tempInt != 0) + { + mTunedStation.setPI(tempInt); + } + mStationCallSignTV.setText(PresetStation.parsePI(tempInt)); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + }; + + private void DebugToasts(String str, int duration) { + //Toast.makeText(this, str, duration).show(); + Log.d(LOGTAG, "Debug:" + str); + } + + /** + * This Handler will scroll the text view. + * On startScroll, the scrolling starts after SCROLLER_START_DELAY_MS + * The Text View is scrolled left one character after every + * SCROLLER_UPDATE_DELAY_MS + * When the entire text is scrolled, the scrolling will restart + * after SCROLLER_RESTART_DELAY_MS + */ + private static final class ScrollerText extends Handler + { + + private static final byte SCROLLER_STOPPED = 0x51; + private static final byte SCROLLER_STARTING = 0x52; + private static final byte SCROLLER_RUNNING = 0x53; + + private static final int SCROLLER_MSG_START = 0xF1; + private static final int SCROLLER_MSG_TICK = 0xF2; + private static final int SCROLLER_MSG_RESTART = 0xF3; + + private static final int SCROLLER_START_DELAY_MS = 1000; + private static final int SCROLLER_RESTART_DELAY_MS = 3000; + private static final int SCROLLER_UPDATE_DELAY_MS = 200; + + private final WeakReference<TextView> mView; + + private byte mStatus = SCROLLER_STOPPED; + String mOriginalString; + int mStringlength=0; + int mIteration=0; + + ScrollerText(TextView v) { + mView = new WeakReference<TextView>(v); + } + + /** + * Scrolling Message Handler + */ + @Override + public void handleMessage(Message msg) { + switch (msg.what) + { + case SCROLLER_MSG_START: + mStatus = SCROLLER_RUNNING; + updateText(); + break; + case SCROLLER_MSG_TICK: + updateText(); + break; + case SCROLLER_MSG_RESTART: + if (mStatus == SCROLLER_RUNNING) + { + startScroll(); + } + break; + } + } + + /** + * Moves the text left by one character and posts a + * delayed message for next update after SCROLLER_UPDATE_DELAY_MS. + * If the entire string is scrolled, then it displays the entire string + * and waits for SCROLLER_RESTART_DELAY_MS for scrolling restart + */ + void updateText() { + if (mStatus != SCROLLER_RUNNING) + { + return; + } + + removeMessages(SCROLLER_MSG_TICK); + + final TextView textView = mView.get(); + if (textView != null) + { + String szStr2 = ""; + if (mStringlength > 0) + { + mIteration++; + if (mIteration >= mStringlength) + { + mIteration = 0; + sendEmptyMessageDelayed(SCROLLER_MSG_RESTART, SCROLLER_RESTART_DELAY_MS); + } else + { + sendEmptyMessageDelayed(SCROLLER_MSG_TICK, SCROLLER_UPDATE_DELAY_MS); + } + //String szStr1 = mOriginalString.substring(0, mTick); + if ((mOriginalString !=null) && (mOriginalString.length() >= mIteration)) + szStr2 = mOriginalString.substring(mIteration); + } + //textView.setText(szStr2+" "+szStr1); + textView.setText(szStr2); + } + } + + /** + * Stops the scrolling + * The textView will be set to the original string. + */ + void stopScroll() { + mStatus = SCROLLER_STOPPED; + removeMessages(SCROLLER_MSG_TICK); + removeMessages(SCROLLER_MSG_RESTART); + removeMessages(SCROLLER_MSG_START); + resetScroll(); + } + + /** + * Resets the scroll to display the original string. + */ + private void resetScroll() { + mIteration = 0; + final TextView textView = mView.get(); + if (textView != null) + { + textView.setText(mOriginalString); + } + } + + /** Starts the Scrolling of the TextView after a + * delay of SCROLLER_START_DELAY_MS + * Starts only if Length > 0 + */ + void startScroll() { + final TextView textView = mView.get(); + if (textView != null) + { + mOriginalString = (String) textView.getText(); + mStringlength = mOriginalString.length(); + if (mStringlength > 0) + { + mStatus = SCROLLER_STARTING; + sendEmptyMessageDelayed(SCROLLER_MSG_START, SCROLLER_START_DELAY_MS); + } + } + } + } + + + public static IFMRadioService sService = null; + private static HashMap<Context, ServiceBinder> sConnectionMap = new HashMap<Context, ServiceBinder>(); + + public static boolean bindToService(Context context) { + Log.e(LOGTAG, "bindToService: Context"); + return bindToService(context, null); + } + + public static boolean bindToService(Context context, ServiceConnection callback) { + Log.e(LOGTAG, "bindToService: Context with serviceconnection callback"); + context.startService(new Intent(context, FMRadioService.class)); + ServiceBinder sb = new ServiceBinder(callback); + sConnectionMap.put(context, sb); + return context.bindService((new Intent()).setClass(context, + FMRadioService.class), sb, 0); + } + + public static void unbindFromService(Context context) { + ServiceBinder sb = (ServiceBinder) sConnectionMap.remove(context); + Log.e(LOGTAG, "unbindFromService: Context"); + if (sb == null) + { + Log.e(LOGTAG, "Trying to unbind for unknown Context"); + return; + } + context.unbindService(sb); + if (sConnectionMap.isEmpty()) + { + // presumably there is nobody interested in the service at this point, + // so don't hang on to the ServiceConnection + sService = null; + } + } + + private static class ServiceBinder implements ServiceConnection + { + ServiceConnection mCallback; + ServiceBinder(ServiceConnection callback) { + mCallback = callback; + } + + public void onServiceConnected(ComponentName className, android.os.IBinder service) { + sService = IFMRadioService.Stub.asInterface(service); + if (mCallback != null) + { + Log.e(LOGTAG, "onServiceConnected: mCallback"); + mCallback.onServiceConnected(className, service); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (mCallback != null) + { + mCallback.onServiceDisconnected(className); + } + sService = null; + } + } + + private ServiceConnection osc = new ServiceConnection() { + public void onServiceConnected(ComponentName classname, IBinder obj) { + mService = IFMRadioService.Stub.asInterface(obj); + Log.e(LOGTAG, "ServiceConnection: onServiceConnected: "); + if (mService != null) + { + try + { + mService.registerCallbacks(mServiceCallbacks); + if(SavedDataAndState == null) { + enableRadio(); + } else if(SavedDataAndState.onOrOff){ + enableRadioOnOffUI(true); + } else { + enableRadioOnOffUI(false); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + if(isRecording()) { + initiateRecordThread(); + } + else if((mRecordDuration > 0) && + (mRecordUpdateHandlerThread != null)) { + mRecordUpdateHandlerThread.interrupt(); + if(mRecordingMsgTV != null) { + mRecordingMsgTV.setVisibility(View.INVISIBLE); + } + mRecordDuration = 0; + } + return; + } else + { + Log.e(LOGTAG, "IFMRadioService onServiceConnected failed"); + } + if (getIntent().getData() == null) + { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(FMRadio.this, FMRadio.class); + startActivity(intent); + } + finish(); + } + public void onServiceDisconnected(ComponentName classname) { + } + }; + + private IFMRadioServiceCallbacks.Stub mServiceCallbacks = new IFMRadioServiceCallbacks.Stub() + { + public void onEnabled() + { + Log.d(LOGTAG, "mServiceCallbacks.onEnabled :"); + mHandler.post(mRadioEnabled); + } + + public void onDisabled() + { + Log.d(LOGTAG, "mServiceCallbacks.onDisabled :"); + mHandler.post(mRadioDisabled); + } + public void onRadioReset() + { + Log.d(LOGTAG, "mServiceCallbacks.onRadioReset :"); + mHandler.post(mRadioReset); + } + + public void onTuneStatusChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onTuneStatusChanged :"); + cleanupTimeoutHandler(); + mHandler.post(mUpdateStationInfo); + } + + public void onProgramServiceChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onProgramServiceChanged :"); + mHandler.post(mUpdateProgramService); + } + + public void onRadioTextChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onRadioTextChanged :"); + mHandler.post(mUpdateRadioText); + } + public void onExtenRadioTextChanged() + { + mHandler.post(mUpdateExtenRadioText); + } + public void onAlternateFrequencyChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onAlternateFrequencyChanged :"); + } + + public void onSignalStrengthChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onSignalStrengthChanged :"); + } + + public void onSearchComplete() + { + Log.d(LOGTAG, "mServiceCallbacks.onSearchComplete :"); + mHandler.post(mSearchComplete); + } + public void onSearchListComplete() + { + Log.d(LOGTAG, "mServiceCallbacks.onSearchListComplete :"); + mHandler.post(mSearchListComplete); + } + + public void onMute(boolean bMuted) + { + Log.d(LOGTAG, "mServiceCallbacks.onMute :" + bMuted); + mHandler.post(mOnMute); + } + + public void onAudioUpdate(boolean bStereo) + { + if((bStereo) && (FmSharedPreferences.getAudioOutputMode())) + { + mStereo = FMRADIO_UI_STATION_AUDIO_STEREO; + } + else + { + mStereo = FMRADIO_UI_STATION_AUDIO_MONO; + + } + Log.d(LOGTAG, "mServiceCallbacks.onAudioUpdate :" + mStereo); + mHandler.post(mOnStereo); + } + + public void onStationRDSSupported(boolean bRDSSupported) + { + Log.d(LOGTAG, "mServiceCallbacks.onStationRDSSupported :" + bRDSSupported); + /* + * Depending on the signal strength etc, RDS Lock Sync/Supported may toggle, + * Since if a station Supports RDS, it will not change its support intermittently + * just save the status and ignore any "unsupported" state. + */ + if(bRDSSupported) + { + mTunedStation.setRDSSupported(true); + } + } + public void onRecordingStopped() + { + Log.d(LOGTAG, "mServiceCallbacks.onRecordingStopped:"); + stopRecording(); + } + }; +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMRadioService.java b/fmapp/src/com/codeaurora/fmradio/FMRadioService.java new file mode 100644 index 0000000..0bf8f1e --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMRadioService.java @@ -0,0 +1,3187 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import java.io.File; +import java.util.*; +import java.io.IOException; +import java.lang.ref.WeakReference; + +import android.app.AlarmManager; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.media.AudioManager; +import android.media.AudioManager.OnAudioFocusChangeListener; +import android.media.AudioSystem; +import android.media.MediaRecorder; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; +import android.os.RemoteException; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.widget.RemoteViews; +import android.widget.Toast; +import android.view.KeyEvent; +import android.os.SystemProperties; + +import qcom.fmradio.FmReceiver; +import qcom.fmradio.FmRxEvCallbacksAdaptor; +import qcom.fmradio.FmRxRdsData; +import qcom.fmradio.FmConfig; +import android.net.Uri; +import android.content.res.Resources; +import java.util.Date; +import java.text.SimpleDateFormat; +import android.provider.MediaStore; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import com.codeaurora.utils.A2dpDeviceStatus; +import android.media.AudioManager; +import android.content.ComponentName; +import android.os.StatFs; +import android.os.SystemClock; + +/** + * Provides "background" FM Radio (that uses the hardware) capabilities, + * allowing the user to switch between activities without stopping playback. + */ +public class FMRadioService extends Service +{ + + public static final int RADIO_AUDIO_DEVICE_WIRED_HEADSET = 0; + public static final int RADIO_AUDIO_DEVICE_SPEAKER = 1; + + private static final int FMRADIOSERVICE_STATUS = 101; + private static final String FMRADIO_DEVICE_FD_STRING = "/dev/radio0"; + private static final String LOGTAG = "FMService";//FMRadio.LOGTAG; + + private FmReceiver mReceiver; + private BroadcastReceiver mHeadsetReceiver = null; + private BroadcastReceiver mSdcardUnmountReceiver = null; + private BroadcastReceiver mMusicCommandListener = null; + private BroadcastReceiver mSleepExpiredListener = null; + private BroadcastReceiver mRecordTimeoutListener = null; + private BroadcastReceiver mDelayedServiceStopListener = null; + private boolean mOverA2DP = false; + private BroadcastReceiver mFmMediaButtonListener; + private IFMRadioServiceCallbacks mCallbacks; + private static FmSharedPreferences mPrefs; + private boolean mHeadsetPlugged = false; + private boolean mInternalAntennaAvailable = false; + private WakeLock mWakeLock; + private int mServiceStartId = -1; + private boolean mServiceInUse = false; + private static boolean mMuted = false; + private static boolean mResumeAfterCall = false; + private static String mAudioDevice="headset"; + MediaRecorder mRecorder = null; + MediaRecorder mA2dp = null; + private boolean mFMOn = false; + private boolean mFmRecordingOn = false; + private boolean mSpeakerPhoneOn = false; + private int mCallStatus = 0; + private BroadcastReceiver mScreenOnOffReceiver = null; + final Handler mHandler = new Handler(); + private boolean misAnalogModeSupported = false; + private boolean misAnalogPathEnabled = false; + private boolean mA2dpDisconnected = false; + //PhoneStateListener instances corresponding to each + + private FmRxRdsData mFMRxRDSData=null; + // interval after which we stop the service when idle + private static final int IDLE_DELAY = 60000; + private File mA2DPSampleFile = null; + //Track FM playback for reenter App usecases + private boolean mPlaybackInProgress = false; + private boolean mStoppedOnFocusLoss = false; + private File mSampleFile = null; + long mSampleStart = 0; + // Messages handled in FM Service + private static final int FM_STOP =1; + private static final int RESET_NOTCH_FILTER =2; + private static final int STOPSERVICE_ONSLEEP = 3; + private static final int STOPRECORD_ONTIMEOUT = 4; + private static final int FOCUSCHANGE = 5; + //Track notch filter settings + private boolean mNotchFilterSet = false; + public static final int STOP_SERVICE = 0; + public static final int STOP_RECORD = 1; + // A2dp Device Status will be queried through this class + A2dpDeviceStatus mA2dpDeviceState = null; + private boolean mA2dpDeviceSupportInHal = false; + //on shutdown not to send start Intent to AudioManager + private boolean mAppShutdown = false; + private boolean mSingleRecordingInstanceSupported = false; + private AudioManager mAudioManager; + public static final long UNAVAILABLE = -1L; + public static final long PREPARING = -2L; + public static final long UNKNOWN_SIZE = -3L; + public static final long LOW_STORAGE_THRESHOLD = 50000000; + private long mStorageSpace; + private static final String IOBUSY_UNVOTE = "com.android.server.CpuGovernorService.action.IOBUSY_UNVOTE"; + private static final String SLEEP_EXPIRED_ACTION = "com.codeaurora.fmradio.SLEEP_EXPIRED"; + private static final String RECORD_EXPIRED_ACTION = "com.codeaurora.fmradio.RECORD_TIMEOUT"; + private static final String SERVICE_DELAYED_STOP_ACTION = "com.codeaurora.fmradio.SERVICE_STOP"; + + public FMRadioService() { + } + + @Override + public void onCreate() { + super.onCreate(); + + mPrefs = new FmSharedPreferences(this); + mCallbacks = null; + TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE | + PhoneStateListener.LISTEN_DATA_ACTIVITY); + PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName()); + mWakeLock.setReferenceCounted(false); + misAnalogModeSupported = SystemProperties.getBoolean("ro.fm.analogpath.supported",false); + /* Register for Screen On/off broadcast notifications */ + mA2dpDeviceState = new A2dpDeviceStatus(getApplicationContext()); + registerScreenOnOffListener(); + registerHeadsetListener(); + registerSleepExpired(); + registerRecordTimeout(); + registerDelayedServiceStop(); + registerExternalStorageListener(); + // registering media button receiver seperately as we need to set + // different priority for receiving media events + registerFmMediaButtonReceiver(); + if ( false == SystemProperties.getBoolean("ro.fm.mulinst.recording.support",true)) { + mSingleRecordingInstanceSupported = true; + } + + // Register for pause commands from other apps to stop FM + registerMusicServiceCommandReceiver(); + + // If the service was idle, but got killed before it stopped itself, the + // system will relaunch it. Make sure it gets stopped again in that case. + setAlarmDelayedServiceStop(); + /* Query to check is a2dp supported in Hal */ + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + String valueStr = audioManager.getParameters("isA2dpDeviceSupported"); + mA2dpDeviceSupportInHal = valueStr.contains("=true"); + Log.d(LOGTAG, " is A2DP device Supported In HAL"+mA2dpDeviceSupportInHal); + } + + @Override + public void onDestroy() { + Log.d(LOGTAG, "onDestroy"); + if (isFmOn()) + { + Log.e(LOGTAG, "Service being destroyed while still playing."); + } + + // make sure there aren't any other messages coming + mDelayedStopHandler.removeCallbacksAndMessages(null); + cancelAlarms(); + //release the audio focus listener + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if (isMuted()) { + mMuted = false; + audioManager.setStreamMute(AudioManager.STREAM_MUSIC,false); + } + audioManager.abandonAudioFocus(mAudioFocusListener); + /* Remove the Screen On/off listener */ + if (mScreenOnOffReceiver != null) { + unregisterReceiver(mScreenOnOffReceiver); + mScreenOnOffReceiver = null; + } + /* Unregister the headset Broadcase receiver */ + if (mHeadsetReceiver != null) { + unregisterReceiver(mHeadsetReceiver); + mHeadsetReceiver = null; + } + if( mSdcardUnmountReceiver != null ) { + unregisterReceiver(mSdcardUnmountReceiver); + mSdcardUnmountReceiver = null; + } + if( mMusicCommandListener != null ) { + unregisterReceiver(mMusicCommandListener); + mMusicCommandListener = null; + } + if( mFmMediaButtonListener != null ) { + unregisterReceiver(mFmMediaButtonListener); + mFmMediaButtonListener = null; + } + if (mSleepExpiredListener != null ) { + unregisterReceiver(mSleepExpiredListener); + mSleepExpiredListener = null; + } + if (mRecordTimeoutListener != null) { + unregisterReceiver(mRecordTimeoutListener); + mRecordTimeoutListener = null; + } + if (mDelayedServiceStopListener != null) { + unregisterReceiver(mDelayedServiceStopListener); + mDelayedServiceStopListener = null; + } + /* Since the service is closing, disable the receiver */ + fmOff(); + + TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tmgr.listen(mPhoneStateListener, 0); + + Log.d(LOGTAG, "onDestroy: unbindFromService completed"); + + //unregisterReceiver(mIntentReceiver); + mWakeLock.release(); + super.onDestroy(); + } + +/** + * Registers an intent to listen for ACTION_MEDIA_UNMOUNTED notifications. + * The intent will call closeExternalStorageFiles() if the external media + * is going to be ejected, so applications can clean up. + */ + public void registerExternalStorageListener() { + if (mSdcardUnmountReceiver == null) { + mSdcardUnmountReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if ((action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) + || (action.equals(Intent.ACTION_MEDIA_EJECT))) { + Log.d(LOGTAG, "ACTION_MEDIA_UNMOUNTED Intent received"); + if (mFmRecordingOn == true) { + try { + stopRecording(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + }; + IntentFilter iFilter = new IntentFilter(); + iFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); + iFilter.addAction(Intent.ACTION_MEDIA_EJECT); + iFilter.addDataScheme("file"); + registerReceiver(mSdcardUnmountReceiver, iFilter); + } + } + + + /** + * Registers an intent to listen for ACTION_HEADSET_PLUG + * notifications. This intent is called to know if the headset + * was plugged in/out + */ + public void registerHeadsetListener() { + if (mHeadsetReceiver == null) { + mHeadsetReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_HEADSET_PLUG)) { + Log.d(LOGTAG, "ACTION_HEADSET_PLUG Intent received"); + // Listen for ACTION_HEADSET_PLUG broadcasts. + Log.d(LOGTAG, "mReceiver: ACTION_HEADSET_PLUG"); + Log.d(LOGTAG, "==> intent: " + intent); + Log.d(LOGTAG, " state: " + intent.getIntExtra("state", 0)); + Log.d(LOGTAG, " name: " + intent.getStringExtra("name")); + mHeadsetPlugged = (intent.getIntExtra("state", 0) == 1); + // if headset is plugged out it is required to disable + // in minimal duration to avoid race conditions with + // audio policy manager switch audio to speaker. + mHandler.removeCallbacks(mHeadsetPluginHandler); + mHandler.post(mHeadsetPluginHandler); + } else if(mA2dpDeviceState.isA2dpStateChange(action) ) { + boolean bA2dpConnected = + mA2dpDeviceState.isConnected(intent); + if (!bA2dpConnected) { + Log.d(LOGTAG, "A2DP device is dis-connected!"); + mA2dpDisconnected = true; + } else { + mA2dpDisconnected = false; + } + if (isAnalogModeEnabled()) { + Log.d(LOGTAG, "FM Audio Path is Analog Mode: FM Over BT not allowed"); + return ; + } + //when playback is overA2Dp and A2dp disconnected + //when playback is not overA2DP and A2DP Connected + // In above two cases we need to Stop and Start FM which + // will take care of audio routing + if( (isFmOn()) && + (true == ((bA2dpConnected)^(mOverA2DP))) && + (false == mStoppedOnFocusLoss) && + (!isSpeakerEnabled())) { + stopFM(); + startFM(); + } + } else if (action.equals("HDMI_CONNECTED")) { + //FM should be off when HDMI is connected. + fmOff(); + try + { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null) ) + { + mCallbacks.onDisabled(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } else if( action.equals(Intent.ACTION_SHUTDOWN)) { + mAppShutdown = true; + } + + } + }; + IntentFilter iFilter = new IntentFilter(); + iFilter.addAction(Intent.ACTION_HEADSET_PLUG); + iFilter.addAction(mA2dpDeviceState.getActionSinkStateChangedString()); + iFilter.addAction("HDMI_CONNECTED"); + iFilter.addAction(Intent.ACTION_SHUTDOWN); + iFilter.addCategory(Intent.CATEGORY_DEFAULT); + registerReceiver(mHeadsetReceiver, iFilter); + } + } + + public void registerFmMediaButtonReceiver() { + if (mFmMediaButtonListener == null) { + mFmMediaButtonListener = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + Log.d(LOGTAG, "FMMediaButtonIntentReceiver.FM_MEDIA_BUTTON"); + Log.d(LOGTAG, "KeyEvent = " +intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)); + String action = intent.getAction(); + if (action.equals(FMMediaButtonIntentReceiver.FM_MEDIA_BUTTON)) { + KeyEvent event = (KeyEvent) + intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + int keycode = event.getKeyCode(); + switch (keycode) { + case KeyEvent.KEYCODE_HEADSETHOOK : + if (isFmOn()){ + //FM should be off when Headset hook pressed. + fmOff(); + if (isOrderedBroadcast()) { + abortBroadcast(); + } + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if ((mServiceInUse) && (mCallbacks != null) ) { + mCallbacks.onDisabled(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } else if( mServiceInUse ) { + fmOn(); + if (isOrderedBroadcast()) { + abortBroadcast(); + } + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if (mCallbacks != null ) { + mCallbacks.onEnabled(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + break; + case KeyEvent.KEYCODE_MEDIA_PAUSE : + if (isFmOn()){ + //FM should be off when Headset hook pressed. + fmOff(); + if (isOrderedBroadcast()) { + abortBroadcast(); + } + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if ((mServiceInUse) && (mCallbacks != null) ) { + mCallbacks.onDisabled(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + break; + case KeyEvent.KEYCODE_MEDIA_PLAY: + if (mServiceInUse ) { + fmOn(); + if (isOrderedBroadcast()) { + abortBroadcast(); + } + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if (mCallbacks != null ) { + mCallbacks.onEnabled(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + break; + } // end of switch + } // end of FMMediaButtonIntentReceiver.FM_MEDIA_BUTTON + } // end of onReceive + }; + IntentFilter iFilter = new IntentFilter(); + iFilter.addAction(FMMediaButtonIntentReceiver.FM_MEDIA_BUTTON); + registerReceiver(mFmMediaButtonListener, iFilter); + } + } + + public void registerMusicServiceCommandReceiver() { + if (mMusicCommandListener == null) { + mMusicCommandListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals("com.android.music.musicservicecommand")) { + String cmd = intent.getStringExtra("command"); + Log.d(LOGTAG, "Music Service command : "+cmd+ " received"); + if (cmd != null && cmd.equals("pause")) { + if (mA2dpDisconnected) { + Log.d(LOGTAG, "not to pause,this is a2dp disconnected's pause"); + mA2dpDisconnected = false; + return; + } + if(isFmOn()){ + fmOff(); + if (isOrderedBroadcast()) { + abortBroadcast(); + } + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null) ){ + mCallbacks.onDisabled(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + } + } + }; + IntentFilter commandFilter = new IntentFilter(); + commandFilter.addAction("com.android.music.musicservicecommand"); + registerReceiver(mMusicCommandListener, commandFilter); + } + } + public void registerSleepExpired() { + if (mSleepExpiredListener == null) { + mSleepExpiredListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(LOGTAG, "registerSleepExpired"); + mWakeLock.acquire(10 * 1000); + fmOff(); + } + }; + IntentFilter intentFilter = new IntentFilter(SLEEP_EXPIRED_ACTION); + registerReceiver(mSleepExpiredListener, intentFilter); + } + } + public void registerRecordTimeout() { + if (mRecordTimeoutListener == null) { + mRecordTimeoutListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(LOGTAG, "registerRecordTimeout"); + mWakeLock.acquire(5 * 1000); + stopRecording(); + } + }; + IntentFilter intentFilter = new IntentFilter(RECORD_EXPIRED_ACTION); + registerReceiver(mRecordTimeoutListener, intentFilter); + } + } + public void registerDelayedServiceStop() { + if (mDelayedServiceStopListener == null) { + mDelayedServiceStopListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(LOGTAG, "registerDelayedServiceStop"); + mWakeLock.acquire(5 * 1000); + if (isFmOn() || mServiceInUse) { + return; + } + stopSelf(mServiceStartId); + } + }; + IntentFilter intentFilter = new IntentFilter(SERVICE_DELAYED_STOP_ACTION); + registerReceiver(mDelayedServiceStopListener, intentFilter); + } + } + + + + final Runnable mHeadsetPluginHandler = new Runnable() { + public void run() { + /* Update the UI based on the state change of the headset/antenna*/ + if(!isAntennaAvailable()) + { + /* Disable FM and let the UI know */ + fmOff(); + try + { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null) ) + { + mCallbacks.onDisabled(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + else + { + /* headset is plugged back in, + So turn on FM if: + - FM is not already ON. + - If the FM UI/Activity is in the foreground + (the service is "bound" by an activity + and if Callbacks are registered) + */ + if ((!isFmOn()) && (mServiceInUse) + && (mCallbacks != null)) + { + if( true != fmOn() ) { + return; + } + try + { + mCallbacks.onEnabled(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + } + }; + + + @Override + public IBinder onBind(Intent intent) { + mDelayedStopHandler.removeCallbacksAndMessages(null); + cancelAlarms(); + mServiceInUse = true; + /* Application/UI is attached, so get out of lower power mode */ + setLowPowerMode(false); + Log.d(LOGTAG, "onBind"); + return mBinder; + } + + @Override + public void onRebind(Intent intent) { + mDelayedStopHandler.removeCallbacksAndMessages(null); + cancelAlarms(); + mServiceInUse = true; + /* Application/UI is attached, so get out of lower power mode */ + setLowPowerMode(false); + if((mPlaybackInProgress == false) && isWiredHeadsetAvailable()) + startFM(); + Log.d(LOGTAG, "onRebind"); + } + + @Override + public void onStart(Intent intent, int startId) { + Log.d(LOGTAG, "onStart"); + mServiceStartId = startId; + // make sure the service will shut down on its own if it was + // just started but not bound to and nothing is playing + mDelayedStopHandler.removeCallbacksAndMessages(null); + cancelAlarms(); + setAlarmDelayedServiceStop(); + } + + @Override + public boolean onUnbind(Intent intent) { + mServiceInUse = false; + Log.d(LOGTAG, "onUnbind"); + + /* Application/UI is not attached, so go into lower power mode */ + unregisterCallbacks(); + setLowPowerMode(true); + if (isFmOn()) + { + // something is currently playing, or will be playing once + // an in-progress call ends, so don't stop the service now. + return true; + } + gotoIdleState(); + return true; + } + + private void startFM(){ + Log.d(LOGTAG, "In startFM"); + if(true == mAppShutdown) { // not to send intent to AudioManager in Shutdown + return; + } + if (isCallActive()) { // when Call is active never let audio playback + mResumeAfterCall = true; + return; + } + mResumeAfterCall = false; + if ( true == mPlaybackInProgress ) // no need to resend event + return; + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + audioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + Log.d(LOGTAG,"FM registering for registerMediaButtonEventReceiver"); + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + ComponentName fmRadio = new ComponentName(this.getPackageName(), + FMMediaButtonIntentReceiver.class.getName()); + mAudioManager.registerMediaButtonEventReceiver(fmRadio); + mStoppedOnFocusLoss = false; + + if (!mA2dpDeviceSupportInHal && (true == mA2dpDeviceState.isDeviceAvailable()) && + (!isSpeakerEnabled()) && !isAnalogModeEnabled() + && (true == startA2dpPlayback())) { + mOverA2DP=true; + } else { + Log.d(LOGTAG, "FMRadio: Requesting to start FM"); + //reason for resending the Speaker option is we are sending + //ACTION_FM=1 to AudioManager, the previous state of Speaker we set + //need not be retained by the Audio Manager. + if (isSpeakerEnabled()) { + mSpeakerPhoneOn = true; + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER); + } + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM, + AudioSystem.DEVICE_STATE_AVAILABLE, ""); + } + mPlaybackInProgress = true; + } + + private void stopFM(){ + Log.d(LOGTAG, "In stopFM"); + if (mOverA2DP==true){ + mOverA2DP=false; + stopA2dpPlayback(); + }else{ + Log.d(LOGTAG, "FMRadio: Requesting to stop FM"); + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM, + AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); + } + mPlaybackInProgress = false; + } + + private void resetFM(){ + Log.d(LOGTAG, "resetFM"); + if (mOverA2DP==true){ + mOverA2DP=false; + resetA2dpPlayback(); + }else{ + Log.d(LOGTAG, "FMRadio: Requesting to stop FM"); + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM, + AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); + } + mPlaybackInProgress = false; + } + + public boolean startRecording() { + Log.d(LOGTAG, "In startRecording of Recorder"); + if( (true == mSingleRecordingInstanceSupported) && + (true == mOverA2DP )) { + Toast.makeText( this, + "playback on BT in progress,can't record now", + Toast.LENGTH_SHORT).show(); + return false; + } + stopRecording(); + + if (!updateAndShowStorageHint()) + return false; + long maxFileSize = mStorageSpace - LOW_STORAGE_THRESHOLD; + mRecorder = new MediaRecorder(); + try { + mRecorder.setMaxFileSize(maxFileSize); + } catch (RuntimeException exception) { + + } + + mSampleFile = null; + File sampleDir = Environment.getExternalStorageDirectory(); + if (!sampleDir.canWrite()) // Workaround for broken sdcard support on + // the device. + sampleDir = new File("/sdcard/sdcard"); + try { + mSampleFile = File + .createTempFile("FMRecording", ".3gpp", sampleDir); + } catch (IOException e) { + Log.e(LOGTAG, "Not able to access SD Card"); + Toast.makeText(this, "Not able to access SD Card", Toast.LENGTH_SHORT).show(); + return false; + } + if (mRecorder == null) { + Toast.makeText(this,"MediaRecorder failed to create an instance", + Toast.LENGTH_SHORT).show(); + return false; + } + + try { + mRecorder.setAudioSource(MediaRecorder.AudioSource.FM_RX); + mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); + mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + } catch (RuntimeException exception) { + mRecorder.reset(); + mRecorder.release(); + mRecorder = null; + return false; + } + mRecorder.setOutputFile(mSampleFile.getAbsolutePath()); + try { + mRecorder.prepare(); + mRecorder.start(); + } catch (IOException e) { + mRecorder.reset(); + mRecorder.release(); + mRecorder = null; + return false; + } catch (RuntimeException e) { + mRecorder.reset(); + mRecorder.release(); + mRecorder = null; + return false; + } + mFmRecordingOn = true; + mRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { + public void onInfo(MediaRecorder mr, int what, int extra) { + if ((what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) || + (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)) { + if (mFmRecordingOn) { + Log.d(LOGTAG, "Maximum file size/duration reached, stopping the recording"); + stopRecording(); + } + // Show the toast. + Toast.makeText(FMRadioService.this, R.string.FMRecording_reach_size_limit, + Toast.LENGTH_LONG).show(); + } + } + // from MediaRecorder.OnErrorListener + public void onError(MediaRecorder mr, int what, int extra) { + Log.e(LOGTAG, "MediaRecorder error. what=" + what + ". extra=" + extra); + if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { + // We may have run out of space on the sdcard. + if (mFmRecordingOn) { + stopRecording(); + } + updateAndShowStorageHint(); + } + } + }); + + mSampleStart = System.currentTimeMillis(); + return true; + } + + public boolean startA2dpPlayback() { + Log.d(LOGTAG, "In startA2dpPlayback"); + if( (true == mSingleRecordingInstanceSupported) && + (true == mFmRecordingOn )) { + Toast.makeText(this, + "Recording already in progress,can't play on BT", + Toast.LENGTH_SHORT).show(); + return false; + } + if(mOverA2DP) + stopA2dpPlayback(); + mA2dp = new MediaRecorder(); + if (mA2dp == null) { + Toast.makeText(this,"A2dpPlayback failed to create an instance", + Toast.LENGTH_SHORT).show(); + return false; + } + try { + mA2dp.setAudioSource(MediaRecorder.AudioSource.FM_RX_A2DP); + mA2dp.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR); + mA2dp.setAudioEncoder(MediaRecorder.OutputFormat.DEFAULT); + File sampleDir = new File(getFilesDir().getAbsolutePath()); + try { + mA2DPSampleFile = File + .createTempFile("FMRecording", ".3gpp", sampleDir); + } catch (IOException e) { + Log.e(LOGTAG, "Not able to access Phone's internal memory"); + Toast.makeText(this, "Not able to access Phone's internal memory", + Toast.LENGTH_SHORT).show(); + return false; + } + mA2dp.setOutputFile(mA2DPSampleFile.getAbsolutePath()); + mA2dp.prepare(); + mA2dp.start(); + } catch (Exception exception) { + mA2dp.reset(); + mA2dp.release(); + mA2dp = null; + return false; + } + return true; + } + + public void stopA2dpPlayback() { + if (mA2dp == null) + return; + if(mA2DPSampleFile != null) + { + try { + mA2DPSampleFile.delete(); + } catch (Exception e) { + Log.e(LOGTAG, "Not able to delete file"); + } + } + try { + mA2dp.stop(); + mA2dp.reset(); + mA2dp.release(); + mA2dp = null; + } catch (Exception exception ) { + Log.e( LOGTAG, "Stop failed with exception"+ exception); + } + return; + } + + private void resetA2dpPlayback() { + if (mA2dp == null) + return; + if(mA2DPSampleFile != null) + { + try { + mA2DPSampleFile.delete(); + } catch (Exception e) { + Log.e(LOGTAG, "Not able to delete file"); + } + } + try { + // Send Intent for IOBUSY VOTE, because MediaRecorder.stop + // gets Activity context which might not be always available + // and would thus fail to send the intent. + Intent ioBusyUnVoteIntent = new Intent(IOBUSY_UNVOTE); + // Remove vote for io_is_busy to be turned off. + ioBusyUnVoteIntent.putExtra("com.android.server.CpuGovernorService.voteType", 0); + sendBroadcast(ioBusyUnVoteIntent); + + mA2dp.stop(); + + mA2dp.reset(); + mA2dp.release(); + mA2dp = null; + } catch (Exception exception ) { + Log.e( LOGTAG, "Stop failed with exception"+ exception); + } + return; + } + + private void resetRecording() { + + Log.v(LOGTAG, "resetRecording()"); + + mFmRecordingOn = false; + if (mRecorder == null) + return; + + // Send Intent for IOBUSY VOTE, because MediaRecorder.stop + // gets Activity context which might not be always available + // and would thus fail to send the intent. + Intent ioBusyUnVoteIntent = new Intent(IOBUSY_UNVOTE); + // Remove vote for io_is_busy to be turned off. + ioBusyUnVoteIntent.putExtra("com.android.server.CpuGovernorService.voteType", 0); + sendBroadcast(ioBusyUnVoteIntent); + + mRecorder.stop(); + + mRecorder.reset(); + mRecorder.release(); + mRecorder = null; + int sampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 ); + if (sampleLength == 0) + return; + String state = Environment.getExternalStorageState(); + Log.d(LOGTAG, "storage state is " + state); + + if (Environment.MEDIA_MOUNTED.equals(state)) { + this.addToMediaDB(mSampleFile); + } + else{ + Log.e(LOGTAG, "SD card must have removed during recording. "); + Toast.makeText(this, "Recording aborted", Toast.LENGTH_SHORT).show(); + } + return; + } + + public void stopRecording() { + mFmRecordingOn = false; + if (mRecorder == null) + return; + try { + mRecorder.stop(); + mRecorder.reset(); + mRecorder.release(); + mRecorder = null; + } catch(Exception e) { + e.printStackTrace(); + } + int sampleLength = (int)((System.currentTimeMillis() - mSampleStart)/1000 ); + if (sampleLength == 0) + return; + String state = Environment.getExternalStorageState(); + Log.d(LOGTAG, "storage state is " + state); + + if (Environment.MEDIA_MOUNTED.equals(state)) { + try { + this.addToMediaDB(mSampleFile); + } + catch(Exception e) { + e.printStackTrace(); + } + } + else{ + Log.e(LOGTAG, "SD card must have removed during recording. "); + Toast.makeText(this, "Recording aborted", Toast.LENGTH_SHORT).show(); + } + try + { + if((mServiceInUse) && (mCallbacks != null) ) { + mCallbacks.onRecordingStopped(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + return; + } + + /* + * Adds file and returns content uri. + */ + private Uri addToMediaDB(File file) { + Log.d(LOGTAG, "In addToMediaDB"); + Resources res = getResources(); + ContentValues cv = new ContentValues(); + long current = System.currentTimeMillis(); + long modDate = file.lastModified(); + Date date = new Date(current); + SimpleDateFormat formatter = new SimpleDateFormat( + res.getString(R.string.audio_db_title_format)); + String title = formatter.format(date); + + // Lets label the recorded audio file as NON-MUSIC so that the file + // won't be displayed automatically, except for in the playlist. + cv.put(MediaStore.Audio.Media.IS_MUSIC, "1"); + + cv.put(MediaStore.Audio.Media.TITLE, title); + cv.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath()); + cv.put(MediaStore.Audio.Media.DATE_ADDED, (int) (current / 1000)); + cv.put(MediaStore.Audio.Media.DATE_MODIFIED, (int) (modDate / 1000)); + cv.put(MediaStore.Audio.Media.MIME_TYPE, "AUDIO_AAC_MP4"); + cv.put(MediaStore.Audio.Media.ARTIST, + res.getString(R.string.audio_db_artist_name)); + cv.put(MediaStore.Audio.Media.ALBUM, + res.getString(R.string.audio_db_album_name)); + Log.d(LOGTAG, "Inserting audio record: " + cv.toString()); + ContentResolver resolver = getContentResolver(); + Uri base = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + Log.d(LOGTAG, "ContentURI: " + base); + Uri result = resolver.insert(base, cv); + if (result == null) { + Toast.makeText(this, "Unable to save recorded audio", Toast.LENGTH_SHORT).show(); + return null; + } + if (getPlaylistId(res) == -1) { + createPlaylist(res, resolver); + } + int audioId = Integer.valueOf(result.getLastPathSegment()); + addToPlaylist(resolver, audioId, getPlaylistId(res)); + + // Notify those applications such as Music listening to the + // scanner events that a recorded audio file just created. + sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result)); + return result; + } + + private int getPlaylistId(Resources res) { + Uri uri = MediaStore.Audio.Playlists.getContentUri("external"); + final String[] ids = new String[] { MediaStore.Audio.Playlists._ID }; + final String where = MediaStore.Audio.Playlists.NAME + "=?"; + final String[] args = new String[] { res.getString(R.string.audio_db_playlist_name) }; + Cursor cursor = query(uri, ids, where, args, null); + if (cursor == null) { + Log.v(LOGTAG, "query returns null"); + } + int id = -1; + if (cursor != null) { + cursor.moveToFirst(); + if (!cursor.isAfterLast()) { + id = cursor.getInt(0); + } + cursor.close(); + } + return id; + } + + private Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + try { + ContentResolver resolver = getContentResolver(); + if (resolver == null) { + return null; + } + return resolver.query(uri, projection, selection, selectionArgs, sortOrder); + } catch (UnsupportedOperationException ex) { + return null; + } + } + + private Uri createPlaylist(Resources res, ContentResolver resolver) { + ContentValues cv = new ContentValues(); + cv.put(MediaStore.Audio.Playlists.NAME, res.getString(R.string.audio_db_playlist_name)); + Uri uri = resolver.insert(MediaStore.Audio.Playlists.getContentUri("external"), cv); + if (uri == null) { + Toast.makeText(this, "Unable to save recorded audio", Toast.LENGTH_SHORT).show(); + } + return uri; + } + + private void addToPlaylist(ContentResolver resolver, int audioId, long playlistId) { + String[] cols = new String[] { + "count(*)" + }; + Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId); + Cursor cur = resolver.query(uri, cols, null, null, null); + final int base; + if (cur != null) { + cur.moveToFirst(); + base = cur.getInt(0); + cur.close(); + } + else { + base = 0; + } + ContentValues values = new ContentValues(); + values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(base + audioId)); + values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId); + resolver.insert(uri, values); + } + private void fmActionOnCallState( int state ) { + //if Call Status is non IDLE we need to Mute FM as well stop recording if + //any. Similarly once call is ended FM should be unmuted. + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + mCallStatus = state; + + if((TelephonyManager.CALL_STATE_OFFHOOK == state)|| + (TelephonyManager.CALL_STATE_RINGING == state)) { + if (state == TelephonyManager.CALL_STATE_RINGING) { + int ringvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); + if (ringvolume == 0) { + return; + } + } + boolean bTempSpeaker = mSpeakerPhoneOn; //need to restore SpeakerPhone + boolean bTempMute = mMuted;// need to restore Mute status + int bTempCall = mCallStatus;//need to restore call status + if (fmOff()) { + if((mServiceInUse) && (mCallbacks != null)) { + try { + mCallbacks.onDisabled(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + mResumeAfterCall = true; + mSpeakerPhoneOn = bTempSpeaker; + mCallStatus = bTempCall; + mMuted = bTempMute; + } else if (!mResumeAfterCall) { + mResumeAfterCall = false; + mSpeakerPhoneOn = bTempSpeaker; + mCallStatus = bTempCall; + mMuted = bTempMute; + } + } + else if (state == TelephonyManager.CALL_STATE_IDLE) { + // start playing again + if (mResumeAfterCall) + { + // resume playback only if FM Radio was playing + // when the call was answered + if (isAntennaAvailable() && (!isFmOn()) && mServiceInUse) + { + Log.d(LOGTAG, "Resuming after call:"); + if(true != fmOn()) { + return; + } + mResumeAfterCall = false; + if(mCallbacks != null) { + try { + mCallbacks.onEnabled(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + } + }//idle + } + + /* Handle Phone Call + FM Concurrency */ + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + Log.d(LOGTAG, "onCallStateChanged: State - " + state ); + Log.d(LOGTAG, "onCallStateChanged: incomingNumber - " + incomingNumber ); + fmActionOnCallState(state ); + } + + @Override + public void onDataActivity (int direction) { + Log.d(LOGTAG, "onDataActivity - " + direction ); + if (direction == TelephonyManager.DATA_ACTIVITY_NONE || + direction == TelephonyManager.DATA_ACTIVITY_DORMANT) { + if (mReceiver != null) { + Message msg = mDelayedStopHandler.obtainMessage(RESET_NOTCH_FILTER); + mDelayedStopHandler.sendMessageDelayed(msg, 10000); + } + } else { + if (mReceiver != null) { + if( true == mNotchFilterSet ) + { + mDelayedStopHandler.removeMessages(RESET_NOTCH_FILTER); + } + else + { + mReceiver.setNotchFilter(true); + mNotchFilterSet = true; + } + } + } + } + }; + +private Handler mSpeakerDisableHandler = new Handler(); + +private Runnable mSpeakerDisableTask = new Runnable() { + public void run() { + Log.v(LOGTAG, "Disabling Speaker"); + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + } +}; + + private Handler mDelayedStopHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case FM_STOP: + // Check again to make sure nothing is playing right now + if (isFmOn() || mServiceInUse) + { + return; + } + Log.d(LOGTAG, "mDelayedStopHandler: stopSelf"); + stopSelf(mServiceStartId); + break; + case RESET_NOTCH_FILTER: + if (mReceiver != null) { + mReceiver.setNotchFilter(false); + mNotchFilterSet = false; + } + break; + case STOPSERVICE_ONSLEEP: + fmOff(); + break; + case STOPRECORD_ONTIMEOUT: + stopRecording(); + break; + case FOCUSCHANGE: + if( false == isFmOn() ) { + Log.v(LOGTAG, "FM is not running, not handling change"); + return; + } + switch (msg.arg1) { + case AudioManager.AUDIOFOCUS_LOSS: + Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS"); + //intentional fall through. + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT"); + if (true == isFmRecordingOn()) + stopRecording(); + if (mSpeakerPhoneOn) { + mSpeakerDisableHandler.removeCallbacks(mSpeakerDisableTask); + mSpeakerDisableHandler.postDelayed(mSpeakerDisableTask, 0); + } + if (true == mPlaybackInProgress) + stopFM(); + if (mSpeakerPhoneOn) { + mSpeakerPhoneOn = false; + if (isAnalogModeSupported()) + setAudioPath(true); + } + mStoppedOnFocusLoss = true; + break; + case AudioManager.AUDIOFOCUS_GAIN: + Log.v(LOGTAG, "AudioFocus: received AUDIOFOCUS_GAIN"); + if(false == mPlaybackInProgress) + startFM(); + mStoppedOnFocusLoss = false; + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + default: + Log.e(LOGTAG, "Unknown audio focus change code"+msg.arg1); + } + break; + } + } + }; + + + /** + * Registers an intent to listen for + * ACTION_SCREEN_ON/ACTION_SCREEN_OFF notifications. This intent + * is called to know iwhen the screen is turned on/off. + */ + public void registerScreenOnOffListener() { + if (mScreenOnOffReceiver == null) { + mScreenOnOffReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_SCREEN_ON)) { + Log.d(LOGTAG, "ACTION_SCREEN_ON Intent received"); + //Screen turned on, set FM module into normal power mode + mHandler.post(mScreenOnHandler); + } + else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + Log.d(LOGTAG, "ACTION_SCREEN_OFF Intent received"); + //Screen turned on, set FM module into low power mode + mHandler.post(mScreenOffHandler); + } + } + }; + IntentFilter iFilter = new IntentFilter(); + iFilter.addAction(Intent.ACTION_SCREEN_ON); + iFilter.addAction(Intent.ACTION_SCREEN_OFF); + registerReceiver(mScreenOnOffReceiver, iFilter); + } + } + + /* Handle all the Screen On actions: + Set FM Power mode to Normal + */ + final Runnable mScreenOnHandler = new Runnable() { + public void run() { + setLowPowerMode(false); + } + }; + /* Handle all the Screen Off actions: + Set FM Power mode to Low Power + This will reduce all the interrupts coming up from the SoC, saving power + */ + final Runnable mScreenOffHandler = new Runnable() { + public void run() { + setLowPowerMode(true); + } + }; + + /* Show the FM Notification */ + public void startNotification() { + RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar); + views.setImageViewResource(R.id.icon, R.drawable.stat_notify_fm); + if (isFmOn()) + { + views.setTextViewText(R.id.frequency, getTunedFrequencyString()); + } else + { + views.setTextViewText(R.id.frequency, ""); + } + + Notification status = new Notification(); + status.contentView = views; + status.flags |= Notification.FLAG_ONGOING_EVENT; + status.icon = R.drawable.stat_notify_fm; + status.contentIntent = PendingIntent.getActivity(this, 0, + new Intent("com.codeaurora.fmradio.FMRADIO_ACTIVITY"), 0); + startForeground(FMRADIOSERVICE_STATUS, status); + //NotificationManager nm = (NotificationManager) + // getSystemService(Context.NOTIFICATION_SERVICE); + //nm.notify(FMRADIOSERVICE_STATUS, status); + //setForeground(true); + mFMOn = true; + } + + private void stop() { + Log.d(LOGTAG,"in stop"); + if (!mServiceInUse) { + Log.d(LOGTAG,"calling unregisterMediaButtonEventReceiver in stop"); + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + ComponentName fmRadio = new ComponentName(this.getPackageName(), + FMMediaButtonIntentReceiver.class.getName()); + mAudioManager.unregisterMediaButtonEventReceiver(fmRadio); + } + gotoIdleState(); + mFMOn = false; + } + + private void gotoIdleState() { + mDelayedStopHandler.removeCallbacksAndMessages(null); + cancelAlarms(); + setAlarmDelayedServiceStop(); + stopForeground(true); + } + + /** Read's the internal Antenna available state from the FM + * Device. + */ + public void readInternalAntennaAvailable() + { + mInternalAntennaAvailable = false; + if (mReceiver != null) + { + mInternalAntennaAvailable = mReceiver.getInternalAntenna(); + Log.d(LOGTAG, "getInternalAntenna: " + mInternalAntennaAvailable); + } + } + + /* + * By making this a static class with a WeakReference to the Service, we + * ensure that the Service can be GCd even when the system process still + * has a remote reference to the stub. + */ + static class ServiceStub extends IFMRadioService.Stub + { + WeakReference<FMRadioService> mService; + + ServiceStub(FMRadioService service) + { + mService = new WeakReference<FMRadioService>(service); + } + + public boolean fmOn() throws RemoteException + { + return(mService.get().fmOn()); + } + + public boolean fmOff() throws RemoteException + { + return(mService.get().fmOff()); + } + + public boolean fmRadioReset() throws RemoteException + { + return true; + } + + public boolean isFmOn() + { + return(mService.get().isFmOn()); + } + + public boolean isAnalogModeEnabled() + { + return(mService.get().isAnalogModeEnabled()); + } + + public boolean isFmRecordingOn() + { + return(mService.get().isFmRecordingOn()); + } + + public boolean isSpeakerEnabled() + { + return(mService.get().isSpeakerEnabled()); + } + + public boolean fmReconfigure() + { + return(mService.get().fmReconfigure()); + } + + public void registerCallbacks(IFMRadioServiceCallbacks cb) throws RemoteException + { + mService.get().registerCallbacks(cb); + } + + public void unregisterCallbacks() throws RemoteException + { + mService.get().unregisterCallbacks(); + } + + public boolean routeAudio(int device) + { + return(mService.get().routeAudio(device)); + } + + public boolean mute() + { + return(mService.get().mute()); + } + + public boolean unMute() + { + return(mService.get().unMute()); + } + + public boolean isMuted() + { + return(mService.get().isMuted()); + } + + public boolean startRecording() + { + return(mService.get().startRecording()); + } + + public void stopRecording() + { + mService.get().stopRecording(); + } + + public boolean tune(int frequency) + { + return(mService.get().tune(frequency)); + } + + public boolean seek(boolean up) + { + return(mService.get().seek(up)); + } + + public void enableSpeaker(boolean speakerOn) + { + mService.get().enableSpeaker(speakerOn); + } + + public boolean scan(int pty) + { + return(mService.get().scan(pty)); + } + + public boolean seekPI(int piCode) + { + return(mService.get().seekPI(piCode)); + } + public boolean searchStrongStationList(int numStations) + { + return(mService.get().searchStrongStationList(numStations)); + } + + public boolean cancelSearch() + { + return(mService.get().cancelSearch()); + } + + public String getProgramService() + { + return(mService.get().getProgramService()); + } + public String getRadioText() + { + return(mService.get().getRadioText()); + } + public String getExtenRadioText() + { + return(mService.get().getExtenRadioText()); + } + public int getProgramType() + { + return(mService.get().getProgramType()); + } + public int getProgramID() + { + return(mService.get().getProgramID()); + } + public int[] getSearchList() + { + return(mService.get().getSearchList()); + } + + public boolean setLowPowerMode(boolean enable) + { + return(mService.get().setLowPowerMode(enable)); + } + + public int getPowerMode() + { + return(mService.get().getPowerMode()); + } + public boolean enableAutoAF(boolean bEnable) + { + return(mService.get().enableAutoAF(bEnable)); + } + public boolean enableStereo(boolean bEnable) + { + return(mService.get().enableStereo(bEnable)); + } + public boolean isAntennaAvailable() + { + return(mService.get().isAntennaAvailable()); + } + public boolean isWiredHeadsetAvailable() + { + return(mService.get().isWiredHeadsetAvailable()); + } + public boolean isCallActive() + { + return(mService.get().isCallActive()); + } + public int getRssi() + { + return (mService.get().getRssi()); + } + public int getIoC() + { + return (mService.get().getIoC()); + } + public int getMpxDcc() + { + return (mService.get().getMpxDcc()); + } + public int getIntDet() + { + return (mService.get().getIntDet()); + } + public void setHiLoInj(int inj) + { + mService.get().setHiLoInj(inj); + } + public void delayedStop(long duration, int nType) + { + mService.get().delayedStop(duration, nType); + } + public void cancelDelayedStop(int nType) + { + mService.get().cancelDelayedStop(nType); + } + public void requestFocus() + { + mService.get().requestFocus(); + } + public int getSINR() + { + return (mService.get().getSINR()); + } + public boolean setSinrSamplesCnt(int samplesCnt) + { + return (mService.get().setSinrSamplesCnt(samplesCnt)); + } + public boolean setSinrTh(int sinr) + { + return (mService.get().setSinrTh(sinr)); + } + public boolean setIntfDetLowTh(int intfLowTh) + { + return (mService.get().setIntfDetLowTh(intfLowTh)); + } + public boolean setIntfDetHighTh(int intfHighTh) + { + return (mService.get().setIntfDetHighTh(intfHighTh)); + } + public int getSearchAlgoType() + { + return (mService.get().getSearchAlgoType()); + } + public boolean setSearchAlgoType(int searchType) + { + return (mService.get().setSearchAlgoType(searchType)); + } + public int getSinrFirstStage() + { + return (mService.get().getSinrFirstStage()); + } + public boolean setSinrFirstStage(int sinr) + { + return (mService.get().setSinrFirstStage(sinr)); + } + public int getRmssiFirstStage() + { + return (mService.get().getRmssiFirstStage()); + } + public boolean setRmssiFirstStage(int rmssi) + { + return (mService.get().setRmssiFirstStage(rmssi)); + } + public int getCFOMeanTh() + { + return (mService.get().getCFOMeanTh()); + } + public boolean setCFOMeanTh(int th) + { + return (mService.get().setCFOMeanTh(th)); + } + public int getSinrSamplesCnt() + { + return (mService.get().getSinrSamplesCnt()); + } + public int getSinrTh() + { + return (mService.get().getSinrTh()); + } + public int getAfJmpRmssiTh() + { + return (mService.get().getAfJmpRmssiTh()); + } + public boolean setAfJmpRmssiTh(int afJmpRmssiTh) + { + return (mService.get().setAfJmpRmssiTh(afJmpRmssiTh)); + } + public int getGoodChRmssiTh() + { + return (mService.get().getGoodChRmssiTh()); + } + public boolean setGoodChRmssiTh(int gdChRmssiTh) + { + return (mService.get().setGoodChRmssiTh(gdChRmssiTh)); + } + public int getAfJmpRmssiSamplesCnt() + { + return (mService.get().getAfJmpRmssiSamplesCnt()); + } + public boolean setAfJmpRmssiSamplesCnt(int afJmpRmssiSmplsCnt) + { + return (mService.get().setAfJmpRmssiSamplesCnt(afJmpRmssiSmplsCnt)); + } + public boolean setRxRepeatCount(int count) + { + return (mService.get().setRxRepeatCount(count)); + } + } + private final IBinder mBinder = new ServiceStub(this); + + private boolean setAudioPath(boolean analogMode) { + + if (mReceiver == null) { + return false; + } + if (isAnalogModeEnabled() == analogMode) { + Log.d(LOGTAG,"Analog Path already is set to "+analogMode); + return false; + } + if (!isAnalogModeSupported()) { + Log.d(LOGTAG,"Analog Path is not supported "); + return false; + } + if (SystemProperties.getBoolean("hw.fm.digitalpath",false)) { + return false; + } + + boolean state = mReceiver.setAnalogMode(analogMode); + if (false == state) { + Log.d(LOGTAG, "Error in toggling analog/digital path " + analogMode); + return false; + } + misAnalogPathEnabled = analogMode; + return true; + } + /* + * Turn ON FM: Powers up FM hardware, and initializes the FM module + * . + * @return true if fm Enable api was invoked successfully, false if the api failed. + */ + private boolean fmOn() { + boolean bStatus=false; + mWakeLock.acquire(10*1000); + if ( TelephonyManager.CALL_STATE_IDLE != getCallState() ) { + return bStatus; + } + + if(mReceiver == null) + { + try { + mReceiver = new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks); + } + catch (InstantiationException e) + { + throw new RuntimeException("FmReceiver service not available!"); + } + } + + if (mReceiver != null) + { + if (isFmOn()) + { + /* FM Is already on,*/ + bStatus = true; + Log.d(LOGTAG, "mReceiver.already enabled"); + } + else + { + // This sets up the FM radio device + FmConfig config = FmSharedPreferences.getFMConfiguration(); + Log.d(LOGTAG, "fmOn: RadioBand :"+ config.getRadioBand()); + Log.d(LOGTAG, "fmOn: Emphasis :"+ config.getEmphasis()); + Log.d(LOGTAG, "fmOn: ChSpacing :"+ config.getChSpacing()); + Log.d(LOGTAG, "fmOn: RdsStd :"+ config.getRdsStd()); + Log.d(LOGTAG, "fmOn: LowerLimit :"+ config.getLowerLimit()); + Log.d(LOGTAG, "fmOn: UpperLimit :"+ config.getUpperLimit()); + bStatus = mReceiver.enable(FmSharedPreferences.getFMConfiguration()); + if (isSpeakerEnabled()) { + setAudioPath(false); + } else { + setAudioPath(true); + } + Log.d(LOGTAG, "mReceiver.enable done, Status :" + bStatus); + } + + if (bStatus == true) + { + /* Put the hardware into normal mode */ + bStatus = setLowPowerMode(false); + Log.d(LOGTAG, "setLowPowerMode done, Status :" + bStatus); + + + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if( (audioManager != null) &&(false == mPlaybackInProgress) ) + { + Log.d(LOGTAG, "mAudioManager.setFmRadioOn = true \n" ); + //audioManager.setParameters("FMRadioOn="+mAudioDevice); + int state = getCallState(); + if ( TelephonyManager.CALL_STATE_IDLE != getCallState() ) + { + fmActionOnCallState(state); + } else { + startFM(); // enable FM Audio only when Call is IDLE + } + Log.d(LOGTAG, "mAudioManager.setFmRadioOn done \n" ); + } + if (mReceiver != null) { + bStatus = mReceiver.registerRdsGroupProcessing(FmReceiver.FM_RX_RDS_GRP_RT_EBL| + FmReceiver.FM_RX_RDS_GRP_PS_EBL| + FmReceiver.FM_RX_RDS_GRP_AF_EBL| + FmReceiver.FM_RX_RDS_GRP_PS_SIMPLE_EBL); + Log.d(LOGTAG, "registerRdsGroupProcessing done, Status :" + bStatus); + } + bStatus = enableAutoAF(FmSharedPreferences.getAutoAFSwitch()); + Log.d(LOGTAG, "enableAutoAF done, Status :" + bStatus); + + /* There is no internal Antenna*/ + bStatus = mReceiver.setInternalAntenna(false); + Log.d(LOGTAG, "setInternalAntenna done, Status :" + bStatus); + + /* Read back to verify the internal Antenna mode*/ + readInternalAntennaAvailable(); + + startNotification(); + bStatus = true; + } + else + { + mReceiver = null; // as enable failed no need to disable + // failure of enable can be because handle + // already open which gets effected if + // we disable + stop(); + } + } + return(bStatus); + } + + /* + * Turn OFF FM Operations: This disables all the current FM operations . + */ + private void fmOperationsOff() { + if ( mSpeakerPhoneOn) + { + mSpeakerPhoneOn = false; + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + } + if (isFmRecordingOn()) + { + stopRecording(); + } + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if(audioManager != null) + { + Log.d(LOGTAG, "audioManager.setFmRadioOn = false \n" ); + unMute(); + stopFM(); + //audioManager.setParameters("FMRadioOn=false"); + Log.d(LOGTAG, "audioManager.setFmRadioOn false done \n" ); + } + + if (isAnalogModeEnabled()) { + SystemProperties.set("hw.fm.isAnalog","false"); + misAnalogPathEnabled = false; + } + } + + /* + * Reset (OFF) FM Operations: This resets all the current FM operations . + */ + private void fmOperationsReset() { + if ( mSpeakerPhoneOn) + { + mSpeakerPhoneOn = false; + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + } + + if (isFmRecordingOn()) + { + resetRecording(); + } + + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if(audioManager != null) + { + Log.d(LOGTAG, "audioManager.setFmRadioOn = false \n" ); + unMute(); + resetFM(); + //audioManager.setParameters("FMRadioOn=false"); + Log.d(LOGTAG, "audioManager.setFmRadioOn false done \n" ); + } + + if (isAnalogModeEnabled()) { + SystemProperties.set("hw.fm.isAnalog","false"); + misAnalogPathEnabled = false; + } + } + + /* + * Turn OFF FM: Disable the FM Host and hardware . + * . + * @return true if fm Disable api was invoked successfully, false if the api failed. + */ + private boolean fmOff() { + boolean bStatus=false; + + fmOperationsOff(); + + // This will disable the FM radio device + if (mReceiver != null) + { + bStatus = mReceiver.disable(); + mReceiver = null; + } + stop(); + return(bStatus); + } + + /* + * Turn OFF FM: Disable the FM Host when hardware resets asynchronously . + * . + * @return true if fm Reset api was invoked successfully, false if the api failed . + */ + private boolean fmRadioReset() { + boolean bStatus=false; + + Log.v(LOGTAG, "fmRadioReset"); + + fmOperationsReset(); + + // This will reset the FM radio receiver + if (mReceiver != null) + { + bStatus = mReceiver.reset(); + mReceiver = null; + } + stop(); + return(bStatus); + } + + /* Returns whether FM hardware is ON. + * + * @return true if FM was tuned, searching. (at the end of + * the search FM goes back to tuned). + * + */ + public boolean isFmOn() { + return mFMOn; + } + + /* Returns true if Analog Path is enabled */ + public boolean isAnalogModeEnabled() { + return misAnalogPathEnabled; + } + + public boolean isAnalogModeSupported() { + return misAnalogModeSupported; + } + + public boolean isFmRecordingOn() { + return mFmRecordingOn; + } + + public boolean isSpeakerEnabled() { + return mSpeakerPhoneOn; + } + public boolean isExternalStorageAvailable() { + boolean mStorageAvail = false; + String state = Environment.getExternalStorageState(); + + if(Environment.MEDIA_MOUNTED.equals(state)){ + Log.d(LOGTAG, "device available"); + mStorageAvail = true; + } + return mStorageAvail; + } + public void enableSpeaker(boolean speakerOn) { + if(isCallActive()) + return ; + mSpeakerPhoneOn = speakerOn; + boolean analogmode = isAnalogModeSupported(); + if (false == speakerOn) { + if (analogmode) { + if (isFmRecordingOn()) + stopRecording(); + stopFM(); + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + if (mMuted) { + setAudioPath(true); + } else { + mute(); + setAudioPath(true); + unMute(); + } + } else { + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + } + if (analogmode) + startFM(); + } + + + //Need to turn off BT path when Speaker is set on vice versa. + if( !mA2dpDeviceSupportInHal && !analogmode && true == mA2dpDeviceState.isDeviceAvailable()) { + if( ((true == mOverA2DP) && (true == speakerOn)) || + ((false == mOverA2DP) && (false == speakerOn)) ) { + //disable A2DP playback for speaker option + stopFM(); + startFM(); + } + } + if (speakerOn) { + if (analogmode) { + stopFM(); + if (mMuted) { + setAudioPath(false); + } else { + mute(); + setAudioPath(false); + unMute(); + } + } + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_SPEAKER); + if (analogmode) + startFM(); + } + + } + /* + * ReConfigure the FM Setup parameters + * - Band + * - Channel Spacing (50/100/200 KHz) + * - Emphasis (50/75) + * - Frequency limits + * - RDS/RBDS standard + * + * @return true if configure api was invoked successfully, false if the api failed. + */ + public boolean fmReconfigure() { + boolean bStatus=false; + Log.d(LOGTAG, "fmReconfigure"); + if (mReceiver != null) + { + // This sets up the FM radio device + FmConfig config = FmSharedPreferences.getFMConfiguration(); + Log.d(LOGTAG, "RadioBand :"+ config.getRadioBand()); + Log.d(LOGTAG, "Emphasis :"+ config.getEmphasis()); + Log.d(LOGTAG, "ChSpacing :"+ config.getChSpacing()); + Log.d(LOGTAG, "RdsStd :"+ config.getRdsStd()); + Log.d(LOGTAG, "LowerLimit :"+ config.getLowerLimit()); + Log.d(LOGTAG, "UpperLimit :"+ config.getUpperLimit()); + bStatus = mReceiver.configure(config); + } + return(bStatus); + } + + /* + * Register UI/Activity Callbacks + */ + public void registerCallbacks(IFMRadioServiceCallbacks cb) + { + mCallbacks = cb; + } + + /* + * unRegister UI/Activity Callbacks + */ + public void unregisterCallbacks() + { + mCallbacks=null; + } + + /* + * Route Audio to headset or speaker phone + * @return true if routeAudio call succeeded, false if the route call failed. + */ + public boolean routeAudio(int audioDevice) { + boolean bStatus=false; + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + + //Log.d(LOGTAG, "routeAudio: " + audioDevice); + + switch (audioDevice) { + + case RADIO_AUDIO_DEVICE_WIRED_HEADSET: + mAudioDevice = "headset"; + break; + + case RADIO_AUDIO_DEVICE_SPEAKER: + mAudioDevice = "speaker"; + break; + + default: + mAudioDevice = "headset"; + break; + } + + if (mReceiver != null) + { + //audioManager.setParameters("FMRadioOn=false"); + //Log.d(LOGTAG, "mAudioManager.setFmRadioOn =" + mAudioDevice ); + //audioManager.setParameters("FMRadioOn="+mAudioDevice); + //Log.d(LOGTAG, "mAudioManager.setFmRadioOn done \n"); + } + + return bStatus; + } + + /* + * Mute FM Hardware (SoC) + * @return true if set mute mode api was invoked successfully, false if the api failed. + */ + public boolean mute() { + boolean bCommandSent=true; + if(isMuted()) + return bCommandSent; + if(isCallActive()) + return false; + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + Log.d(LOGTAG, "mute:"); + if (audioManager != null) + { + mMuted = true; + audioManager.setStreamMute(AudioManager.STREAM_MUSIC,true); + } + return bCommandSent; + } + + /* + * UnMute FM Hardware (SoC) + * @return true if set mute mode api was invoked successfully, false if the api failed. + */ + public boolean unMute() { + boolean bCommandSent=true; + if(!isMuted()) + return bCommandSent; + if(isCallActive()) + return false; + Log.d(LOGTAG, "unMute:"); + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if (audioManager != null) + { + mMuted = false; + audioManager.setStreamMute(AudioManager.STREAM_MUSIC,false); + if (mResumeAfterCall) + { + //We are unmuting FM in a voice call. Need to enable FM audio routing. + startFM(); + } + } + return bCommandSent; + } + + /* Returns whether FM Hardware(Soc) Audio is Muted. + * + * @return true if FM Audio is muted, false if not muted. + * + */ + public boolean isMuted() { + return mMuted; + } + + /* Tunes to the specified frequency + * + * @return true if Tune command was invoked successfully, false if not muted. + * Note: Callback FmRxEvRadioTuneStatus will be called when the tune + * is complete + */ + public boolean tune(int frequency) { + boolean bCommandSent=false; + double doubleFrequency = frequency/1000.00; + + Log.d(LOGTAG, "tuneRadio: " + doubleFrequency); + if (mReceiver != null) + { + mReceiver.setStation(frequency); + bCommandSent = true; + } + return bCommandSent; + } + + /* Seeks (Search for strong station) to the station in the direction specified + * relative to the tuned station. + * boolean up: true - Search in the forward direction. + * false - Search in the backward direction. + * @return true if Seek command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchComplete will be called when the Search + * is complete + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to a station + * at the end of the Search or if the seach was cancelled. + */ + public boolean seek(boolean up) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + if (up == true) + { + Log.d(LOGTAG, "seek: Up"); + mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SEEK, + FmReceiver.FM_RX_DWELL_PERIOD_1S, + FmReceiver.FM_RX_SEARCHDIR_UP); + } + else + { + Log.d(LOGTAG, "seek: Down"); + mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SEEK, + FmReceiver.FM_RX_DWELL_PERIOD_1S, + FmReceiver.FM_RX_SEARCHDIR_DOWN); + } + bCommandSent = true; + } + return bCommandSent; + } + + /* Scan (Search for station with a "preview" of "n" seconds) + * FM Stations. It always scans in the forward direction relative to the + * current tuned station. + * int pty: 0 or a reserved PTY value- Perform a "strong" station search of all stations. + * Valid/Known PTY - perform RDS Scan for that pty. + * + * @return true if Scan command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvRadioTuneStatus will be called when tuned to various stations + * during the Scan. + * 2. Callback FmRxEvSearchComplete will be called when the Search + * is complete + * 3. Callback FmRxEvRadioTuneStatus will also be called when tuned to a station + * at the end of the Search or if the seach was cancelled. + * + */ + public boolean scan(int pty) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "scan: PTY: " + pty); + if(FmSharedPreferences.isRBDSStd()) + { + /* RBDS : Validate PTY value?? */ + if( ((pty > 0) && (pty <= 23)) || ((pty >= 29) && (pty <= 31)) ) + { + bCommandSent = mReceiver.searchStations(FmReceiver.FM_RX_SRCHRDS_MODE_SCAN_PTY, + FmReceiver.FM_RX_DWELL_PERIOD_2S, + FmReceiver.FM_RX_SEARCHDIR_UP, + pty, + 0); + } + else + { + bCommandSent = mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SCAN, + FmReceiver.FM_RX_DWELL_PERIOD_2S, + FmReceiver.FM_RX_SEARCHDIR_UP); + } + } + else + { + /* RDS : Validate PTY value?? */ + if( (pty > 0) && (pty <= 31) ) + { + bCommandSent = mReceiver.searchStations(FmReceiver.FM_RX_SRCHRDS_MODE_SCAN_PTY, + FmReceiver.FM_RX_DWELL_PERIOD_2S, + FmReceiver.FM_RX_SEARCHDIR_UP, + pty, + 0); + } + else + { + bCommandSent = mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SCAN, + FmReceiver.FM_RX_DWELL_PERIOD_2S, + FmReceiver.FM_RX_SEARCHDIR_UP); + } + } + } + return bCommandSent; + } + + /* Search for the 'numStations' number of strong FM Stations. + * + * It searches in the forward direction relative to the current tuned station. + * int numStations: maximum number of stations to search. + * + * @return true if Search command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchListComplete will be called when the Search + * is complete + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to + * the previously tuned station. + */ + public boolean searchStrongStationList(int numStations) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "searchStrongStationList: numStations: " + numStations); + bCommandSent = mReceiver.searchStationList(FmReceiver.FM_RX_SRCHLIST_MODE_STRONG, + FmReceiver.FM_RX_SEARCHDIR_UP, + numStations, + 0); + } + return bCommandSent; + } + + /* Search for the FM Station that matches the RDS PI (Program Identifier) code. + * It always scans in the forward direction relative to the current tuned station. + * int piCode: PI Code of the station to search. + * + * @return true if Search command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchComplete will be called when the Search + * is complete + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to a station + * at the end of the Search or if the seach was cancelled. + */ + public boolean seekPI(int piCode) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "seekPI: piCode: " + piCode); + bCommandSent = mReceiver.searchStations(FmReceiver.FM_RX_SRCHRDS_MODE_SEEK_PI, + FmReceiver.FM_RX_DWELL_PERIOD_1S, + FmReceiver.FM_RX_SEARCHDIR_UP, + 0, + piCode + ); + } + return bCommandSent; + } + + + /* Cancel any ongoing Search (Seek/Scan/SearchStationList). + * + * @return true if Search command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchComplete will be called when the Search + * is complete/cancelled. + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to a station + * at the end of the Search or if the seach was cancelled. + */ + public boolean cancelSearch() + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "cancelSearch"); + bCommandSent = mReceiver.cancelSearch(); + } + return bCommandSent; + } + + /* Retrieves the RDS Program Service (PS) String. + * + * @return String - RDS PS String. + * Note: 1. This is a synchronous call that should typically called when + * Callback FmRxEvRdsPsInfo is invoked. + * 2. Since PS contains multiple fields, this Service reads all the fields and "caches" + * the values and provides this helper routine for the Activity to get only the information it needs. + * 3. The "cached" data fields are always "cleared" when the tune status changes. + */ + public String getProgramService() { + String str = ""; + if (mFMRxRDSData != null) + { + str = mFMRxRDSData.getPrgmServices(); + if(str == null) + { + str= ""; + } + } + Log.d(LOGTAG, "Program Service: [" + str + "]"); + return str; + } + + /* Retrieves the RDS Radio Text (RT) String. + * + * @return String - RDS RT String. + * Note: 1. This is a synchronous call that should typically called when + * Callback FmRxEvRdsRtInfo is invoked. + * 2. Since RT contains multiple fields, this Service reads all the fields and "caches" + * the values and provides this helper routine for the Activity to get only the information it needs. + * 3. The "cached" data fields are always "cleared" when the tune status changes. + */ + public String getRadioText() { + String str = ""; + if (mFMRxRDSData != null) + { + str = mFMRxRDSData.getRadioText(); + if(str == null) + { + str= ""; + } + } + Log.d(LOGTAG, "Radio Text: [" + str + "]"); + return str; + } + + public String getExtenRadioText() { + String str = ""; + if (mFMRxRDSData != null) + { + str = mFMRxRDSData.getERadioText(); + if(str == null) + { + str= ""; + } + } + Log.d(LOGTAG, "eRadio Text:[" + str +"]"); + return str; + } + /* Retrieves the RDS Program Type (PTY) code. + * + * @return int - RDS PTY code. + * Note: 1. This is a synchronous call that should typically called when + * Callback FmRxEvRdsRtInfo and or FmRxEvRdsPsInfo is invoked. + * 2. Since RT/PS contains multiple fields, this Service reads all the fields and "caches" + * the values and provides this helper routine for the Activity to get only the information it needs. + * 3. The "cached" data fields are always "cleared" when the tune status changes. + */ + public int getProgramType() { + int pty = -1; + if (mFMRxRDSData != null) + { + pty = mFMRxRDSData.getPrgmType(); + } + Log.d(LOGTAG, "PTY: [" + pty + "]"); + return pty; + } + + /* Retrieves the RDS Program Identifier (PI). + * + * @return int - RDS PI code. + * Note: 1. This is a synchronous call that should typically called when + * Callback FmRxEvRdsRtInfo and or FmRxEvRdsPsInfo is invoked. + * 2. Since RT/PS contains multiple fields, this Service reads all the fields and "caches" + * the values and provides this helper routine for the Activity to get only the information it needs. + * 3. The "cached" data fields are always "cleared" when the tune status changes. + */ + public int getProgramID() { + int pi = -1; + if (mFMRxRDSData != null) + { + pi = mFMRxRDSData.getPrgmId(); + } + Log.d(LOGTAG, "PI: [" + pi + "]"); + return pi; + } + + + /* Retrieves the station list from the SearchStationlist. + * + * @return Array of integers that represents the station frequencies. + * Note: 1. This is a synchronous call that should typically called when + * Callback onSearchListComplete. + */ + public int[] getSearchList() + { + int[] frequencyList = null; + if (mReceiver != null) + { + Log.d(LOGTAG, "getSearchList: "); + frequencyList = mReceiver.getStationList(); + } + return frequencyList; + } + + /* Set the FM Power Mode on the FM hardware SoC. + * Typically used when UI/Activity is in the background, so the Host is interrupted less often. + * + * boolean bLowPower: true: Enable Low Power mode on FM hardware. + * false: Disable Low Power mode on FM hardware. (Put into normal power mode) + * @return true if set power mode api was invoked successfully, false if the api failed. + */ + public boolean setLowPowerMode(boolean bLowPower) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "setLowPowerMode: " + bLowPower); + if(bLowPower) + { + bCommandSent = mReceiver.setPowerMode(FmReceiver.FM_RX_LOW_POWER_MODE); + } + else + { + bCommandSent = mReceiver.setPowerMode(FmReceiver.FM_RX_NORMAL_POWER_MODE); + } + } + return bCommandSent; + } + + /* Get the FM Power Mode on the FM hardware SoC. + * + * @return the device power mode. + */ + public int getPowerMode() + { + int powerMode=FmReceiver.FM_RX_NORMAL_POWER_MODE; + if (mReceiver != null) + { + powerMode = mReceiver.getPowerMode(); + Log.d(LOGTAG, "getLowPowerMode: " + powerMode); + } + return powerMode; + } + + /* Set the FM module to auto switch to an Alternate Frequency for the + * station if one the signal strength of that frequency is stronger than the + * current tuned frequency. + * + * boolean bEnable: true: Auto switch to stronger alternate frequency. + * false: Do not switch to alternate frequency. + * + * @return true if set Auto AF mode api was invoked successfully, false if the api failed. + * Note: Callback FmRxEvRadioTuneStatus will be called when tune + * is complete to a different frequency. + */ + public boolean enableAutoAF(boolean bEnable) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "enableAutoAF: " + bEnable); + bCommandSent = mReceiver.enableAFjump(bEnable); + } + return bCommandSent; + } + + /* Set the FM module to Stereo Mode or always force it to Mono Mode. + * Note: The stereo mode will be available only when the station is broadcasting + * in Stereo mode. + * + * boolean bEnable: true: Enable Stereo Mode. + * false: Always stay in Mono Mode. + * + * @return true if set Stereo mode api was invoked successfully, false if the api failed. + */ + public boolean enableStereo(boolean bEnable) + { + boolean bCommandSent=false; + if (mReceiver != null) + { + Log.d(LOGTAG, "enableStereo: " + bEnable); + bCommandSent = mReceiver.setStereoMode(bEnable); + } + return bCommandSent; + } + + /** Determines if an internal Antenna is available. + * Returns the cached value initialized on FMOn. + * + * @return true if internal antenna is available or wired + * headset is plugged in, false if internal antenna is + * not available and wired headset is not plugged in. + */ + public boolean isAntennaAvailable() + { + boolean bAvailable = false; + if ((mInternalAntennaAvailable) || (mHeadsetPlugged) ) + { + bAvailable = true; + } + return bAvailable; + } + + public static long getAvailableSpace() { + String state = Environment.getExternalStorageState(); + Log.d(LOGTAG, "External storage state=" + state); + if (Environment.MEDIA_CHECKING.equals(state)) { + return PREPARING; + } + if (!Environment.MEDIA_MOUNTED.equals(state)) { + return UNAVAILABLE; + } + + try { + File sampleDir = Environment.getExternalStorageDirectory(); + StatFs stat = new StatFs(sampleDir.getAbsolutePath()); + return stat.getAvailableBlocks() * (long) stat.getBlockSize(); + } catch (Exception e) { + Log.i(LOGTAG, "Fail to access external storage", e); + } + return UNKNOWN_SIZE; + } + + private boolean updateAndShowStorageHint() { + mStorageSpace = getAvailableSpace(); + return showStorageHint(); + } + + private boolean showStorageHint() { + String errorMessage = null; + if (mStorageSpace == UNAVAILABLE) { + errorMessage = getString(R.string.no_storage); + } else if (mStorageSpace == PREPARING) { + errorMessage = getString(R.string.preparing_sd); + } else if (mStorageSpace == UNKNOWN_SIZE) { + errorMessage = getString(R.string.access_sd_fail); + } else if (mStorageSpace < LOW_STORAGE_THRESHOLD) { + errorMessage = getString(R.string.spaceIsLow_content); + } + + if (errorMessage != null) { + Toast.makeText(this, errorMessage, + Toast.LENGTH_LONG).show(); + return false; + } + return true; + } + + /** Determines if a Wired headset is plugged in. Returns the + * cached value initialized on broadcast receiver + * initialization. + * + * @return true if wired headset is plugged in, false if wired + * headset is not plugged in. + */ + public boolean isWiredHeadsetAvailable() + { + return (mHeadsetPlugged); + } + public boolean isCallActive() + { + //Non-zero: Call state is RINGING or OFFHOOK on the available subscriptions + //zero: Call state is IDLE on all the available subscriptions + if(0 != getCallState()) return true; + return false; + } + public int getCallState() + { + return mCallStatus; + } + + public void clearStationInfo() { + if(mFMRxRDSData != null) { + mFMRxRDSData.setRadioText(""); + mFMRxRDSData.setPrgmId(0); + mFMRxRDSData.setPrgmType(0); + mFMRxRDSData.setPrgmServices(""); + mFMRxRDSData.setERadioText(""); + mFMRxRDSData.setTagValue("", 1); + mFMRxRDSData.setTagValue("", 2); + mFMRxRDSData.setTagCode((byte)0, 1); + mFMRxRDSData.setTagCode((byte)0, 2); + Log.d(LOGTAG, "clear tags data"); + FmSharedPreferences.clearTags(); + } + } + + /* Receiver callbacks back from the FM Stack */ + FmRxEvCallbacksAdaptor fmCallbacks = new FmRxEvCallbacksAdaptor() + { + public void FmRxEvEnableReceiver() { + Log.d(LOGTAG, "FmRxEvEnableReceiver"); + mReceiver.setRawRdsGrpMask(); + } + public void FmRxEvDisableReceiver() + { + Log.d(LOGTAG, "FmRxEvDisableReceiver"); + mFMOn = false; + FmSharedPreferences.clearTags(); + } + public void FmRxEvRadioReset() + { + Log.d(LOGTAG, "FmRxEvRadioReset"); + if(isFmOn()) { + // Received radio reset event while FM is ON + Log.d(LOGTAG, "FM Radio reset"); + fmRadioReset(); + try + { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null) ) + { + mCallbacks.onRadioReset(); + } + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + public void FmRxEvConfigReceiver() + { + Log.d(LOGTAG, "FmRxEvConfigReceiver"); + } + public void FmRxEvMuteModeSet() + { + Log.d(LOGTAG, "FmRxEvMuteModeSet"); + } + public void FmRxEvStereoModeSet() + { + Log.d(LOGTAG, "FmRxEvStereoModeSet"); + } + public void FmRxEvRadioStationSet() + { + Log.d(LOGTAG, "FmRxEvRadioStationSet"); + } + public void FmRxEvPowerModeSet() + { + Log.d(LOGTAG, "FmRxEvPowerModeSet"); + } + public void FmRxEvSetSignalThreshold() + { + Log.d(LOGTAG, "FmRxEvSetSignalThreshold"); + } + + public void FmRxEvRadioTuneStatus(int frequency) + { + Log.d(LOGTAG, "FmRxEvRadioTuneStatus: Tuned Frequency: " +frequency); + try + { + FmSharedPreferences.setTunedFrequency(frequency); + mPrefs.Save(); + //Log.d(LOGTAG, "Call mCallbacks.onTuneStatusChanged"); + /* Since the Tuned Status changed, clear out the RDSData cached */ + if(mReceiver != null) { + clearStationInfo(); + } + if(mCallbacks != null) + { + mCallbacks.onTuneStatusChanged(); + } + /* Update the frequency in the StatusBar's Notification */ + startNotification(); + + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + + public void FmRxEvStationParameters() + { + Log.d(LOGTAG, "FmRxEvStationParameters"); + } + + public void FmRxEvRdsLockStatus(boolean bRDSSupported) + { + Log.d(LOGTAG, "FmRxEvRdsLockStatus: " + bRDSSupported); + try + { + if(mCallbacks != null) + { + mCallbacks.onStationRDSSupported(bRDSSupported); + } + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + + public void FmRxEvStereoStatus(boolean stereo) + { + Log.d(LOGTAG, "FmRxEvStereoStatus: " + stereo); + try + { + if(mCallbacks != null) + { + mCallbacks.onAudioUpdate(stereo); + } + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + public void FmRxEvServiceAvailable(boolean signal) + { + Log.d(LOGTAG, "FmRxEvServiceAvailable"); + if(signal) { + Log.d(LOGTAG, "FmRxEvServiceAvailable: Tuned frequency is above signal threshold level"); + } + else { + Log.d(LOGTAG, "FmRxEvServiceAvailable: Tuned frequency is below signal threshold level"); + } + } + public void FmRxEvGetSignalThreshold() + { + Log.d(LOGTAG, "FmRxEvGetSignalThreshold"); + } + public void FmRxEvSearchInProgress() + { + Log.d(LOGTAG, "FmRxEvSearchInProgress"); + } + public void FmRxEvSearchRdsInProgress() + { + Log.d(LOGTAG, "FmRxEvSearchRdsInProgress"); + } + public void FmRxEvSearchListInProgress() + { + Log.d(LOGTAG, "FmRxEvSearchListInProgress"); + } + public void FmRxEvSearchComplete(int frequency) + { + Log.d(LOGTAG, "FmRxEvSearchComplete: Tuned Frequency: " +frequency); + try + { + FmSharedPreferences.setTunedFrequency(frequency); + //Log.d(LOGTAG, "Call mCallbacks.onSearchComplete"); + /* Since the Tuned Status changed, clear out the RDSData cached */ + if(mReceiver != null) { + clearStationInfo(); + } + if(mCallbacks != null) + { + mCallbacks.onSearchComplete(); + } + /* Update the frequency in the StatusBar's Notification */ + startNotification(); + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + + public void FmRxEvSearchRdsComplete() + { + Log.d(LOGTAG, "FmRxEvSearchRdsComplete"); + } + + public void FmRxEvSearchListComplete() + { + Log.d(LOGTAG, "FmRxEvSearchListComplete"); + try + { + if(mCallbacks != null) + { + mCallbacks.onSearchListComplete(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + + public void FmRxEvSearchCancelled() + { + Log.d(LOGTAG, "FmRxEvSearchCancelled: Cancelled the on-going search operation."); + } + public void FmRxEvRdsGroupData() + { + Log.d(LOGTAG, "FmRxEvRdsGroupData"); + } + + public void FmRxEvRdsPsInfo() { + Log.d(LOGTAG, "FmRxEvRdsPsInfo: "); + try + { + if(mReceiver != null) + { + mFMRxRDSData = mReceiver.getPSInfo(); + if(mFMRxRDSData != null) + { + Log.d(LOGTAG, "PI: [" + mFMRxRDSData.getPrgmId() + "]"); + Log.d(LOGTAG, "PTY: [" + mFMRxRDSData.getPrgmType() + "]"); + Log.d(LOGTAG, "PS: [" + mFMRxRDSData.getPrgmServices() + "]"); + } + if(mCallbacks != null) + { + mCallbacks.onProgramServiceChanged(); + } + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + + public void FmRxEvRdsRtInfo() { + Log.d(LOGTAG, "FmRxEvRdsRtInfo"); + try + { + //Log.d(LOGTAG, "Call mCallbacks.onRadioTextChanged"); + if(mReceiver != null) + { + mFMRxRDSData = mReceiver.getRTInfo(); + if(mFMRxRDSData != null) + { + Log.d(LOGTAG, "PI: [" + mFMRxRDSData.getPrgmId() + "]"); + Log.d(LOGTAG, "PTY: [" + mFMRxRDSData.getPrgmType() + "]"); + Log.d(LOGTAG, "RT: [" + mFMRxRDSData.getRadioText() + "]"); + } + if(mCallbacks != null) + { + mCallbacks.onRadioTextChanged(); + } + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + + } + + public void FmRxEvRdsAfInfo() + { + Log.d(LOGTAG, "FmRxEvRdsAfInfo"); + } + public void FmRxEvRTPlus() + { + int tag_nums; + Log.d(LOGTAG, "FmRxEvRTPlusInfo"); + if (mReceiver != null) { + mFMRxRDSData = mReceiver.getRTPlusInfo(); + tag_nums = mFMRxRDSData.getTagNums(); + if (tag_nums >= 1) { + Log.d(LOGTAG, "tag1 is: " + mFMRxRDSData.getTagCode(1) + "value: " + + mFMRxRDSData.getTagValue(1)); + FmSharedPreferences.addTags(mFMRxRDSData.getTagCode(1), mFMRxRDSData.getTagValue(1)); + } + if(tag_nums == 2) { + Log.d(LOGTAG, "tag2 is: " + mFMRxRDSData.getTagCode(2) + "value: " + + mFMRxRDSData.getTagValue(2)); + FmSharedPreferences.addTags(mFMRxRDSData.getTagCode(2), mFMRxRDSData.getTagValue(2)); + } + } + } + public void FmRxEvERTInfo() + { + Log.d(LOGTAG, "FmRxEvERTInfo"); + try { + if (mReceiver != null) { + mFMRxRDSData = mReceiver.getERTInfo(); + if(mCallbacks != null) + mCallbacks.onExtenRadioTextChanged(); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + public void FmRxEvRdsPiMatchAvailable() + { + Log.d(LOGTAG, "FmRxEvRdsPiMatchAvailable"); + } + public void FmRxEvRdsGroupOptionsSet() + { + Log.d(LOGTAG, "FmRxEvRdsGroupOptionsSet"); + } + public void FmRxEvRdsProcRegDone() + { + Log.d(LOGTAG, "FmRxEvRdsProcRegDone"); + } + public void FmRxEvRdsPiMatchRegDone() + { + Log.d(LOGTAG, "FmRxEvRdsPiMatchRegDone"); + } + }; + + + /* + * Read the Tuned Frequency from the FM module. + */ + private String getTunedFrequencyString() { + + double frequency = FmSharedPreferences.getTunedFrequency() / 1000.0; + String frequencyString = getString(R.string.stat_notif_frequency, (""+frequency)); + return frequencyString; + } + public int getRssi() { + if (mReceiver != null) + return mReceiver.getRssi(); + else + return Integer.MAX_VALUE; + } + public int getIoC() { + if (mReceiver != null) + return mReceiver.getIoverc(); + else + return Integer.MAX_VALUE; + } + public int getIntDet() { + if (mReceiver != null) + return mReceiver.getIntDet(); + else + return Integer.MAX_VALUE; + } + public int getMpxDcc() { + if (mReceiver != null) + return mReceiver.getMpxDcc(); + else + return Integer.MAX_VALUE; + } + public void setHiLoInj(int inj) { + if (mReceiver != null) + mReceiver.setHiLoInj(inj); + } + public int getSINR() { + if (mReceiver != null) + return mReceiver.getSINR(); + else + return Integer.MAX_VALUE; + } + public boolean setSinrSamplesCnt(int samplesCnt) { + if(mReceiver != null) + return mReceiver.setSINRsamples(samplesCnt); + else + return false; + } + public boolean setSinrTh(int sinr) { + if(mReceiver != null) + return mReceiver.setSINRThreshold(sinr); + else + return false; + } + public boolean setIntfDetLowTh(int intfLowTh) { + if(mReceiver != null) + return mReceiver.setOnChannelThreshold(intfLowTh); + else + return false; + } + public boolean setIntfDetHighTh(int intfHighTh) { + if(mReceiver != null) + return mReceiver.setOffChannelThreshold(intfHighTh); + else + return false; + } + public int getSearchAlgoType() { + if(mReceiver != null) + return mReceiver.getSearchAlgoType(); + else + return -1; + } + public boolean setSearchAlgoType(int searchType) { + if(mReceiver != null) + return mReceiver.setSearchAlgoType(searchType); + else + return false; + } + public int getSinrFirstStage() { + if(mReceiver != null) + return mReceiver.getSinrFirstStage(); + else + return Integer.MAX_VALUE; + } + public boolean setSinrFirstStage(int sinr) { + if(mReceiver != null) + return mReceiver.setSinrFirstStage(sinr); + else + return false; + } + public int getRmssiFirstStage() { + if(mReceiver != null) + return mReceiver.getRmssiFirstStage(); + else + return Integer.MAX_VALUE; + } + public boolean setRmssiFirstStage(int rmssi) { + if(mReceiver != null) + return mReceiver.setRmssiFirstStage(rmssi); + else + return false; + } + public int getCFOMeanTh() { + if(mReceiver != null) + return mReceiver.getCFOMeanTh(); + else + return Integer.MAX_VALUE; + } + public boolean setCFOMeanTh(int th) { + if(mReceiver != null) + return mReceiver.setCFOMeanTh(th); + else + return false; + } + public int getSinrSamplesCnt() { + if(mReceiver != null) + return mReceiver.getSINRsamples(); + else + return Integer.MAX_VALUE; + } + public int getSinrTh() { + if(mReceiver != null) + return mReceiver.getSINRThreshold(); + else + return Integer.MAX_VALUE; + } + + boolean setAfJmpRmssiTh(int afJmpRmssiTh) { + if(mReceiver != null) + return mReceiver.setAFJumpRmssiTh(afJmpRmssiTh); + else + return false; + } + boolean setGoodChRmssiTh(int gdChRmssiTh) { + if(mReceiver != null) + return mReceiver.setGdChRmssiTh(gdChRmssiTh); + else + return false; + } + boolean setAfJmpRmssiSamplesCnt(int afJmpRmssiSmplsCnt) { + if(mReceiver != null) + return mReceiver.setAFJumpRmssiSamples(afJmpRmssiSmplsCnt); + else + return false; + } + int getAfJmpRmssiTh() { + if(mReceiver != null) + return mReceiver.getAFJumpRmssiTh(); + else + return Integer.MIN_VALUE; + } + int getGoodChRmssiTh() { + if(mReceiver != null) + return mReceiver.getGdChRmssiTh(); + else + return Integer.MAX_VALUE; + } + int getAfJmpRmssiSamplesCnt() { + if(mReceiver != null) + return mReceiver.getAFJumpRmssiSamples(); + else + return Integer.MIN_VALUE; + } + private void setAlarmSleepExpired (long duration) { + Intent i = new Intent(SLEEP_EXPIRED_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + Log.d(LOGTAG, "delayedStop called" + SystemClock.elapsedRealtime() + duration); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + duration, pi); + } + private void cancelAlarmSleepExpired() { + Intent i = new Intent(SLEEP_EXPIRED_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + am.cancel(pi); + } + private void setAlarmRecordTimeout(long duration) { + Intent i = new Intent(RECORD_EXPIRED_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + Log.d(LOGTAG, "delayedStop called" + SystemClock.elapsedRealtime() + duration); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + duration, pi); + } + private void cancelAlarmRecordTimeout() { + Intent i = new Intent(RECORD_EXPIRED_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + am.cancel(pi); + } + private void setAlarmDelayedServiceStop() { + Intent i = new Intent(SERVICE_DELAYED_STOP_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + IDLE_DELAY, pi); + } + private void cancelAlarmDealyedServiceStop() { + Intent i = new Intent(SERVICE_DELAYED_STOP_ACTION); + AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE); + PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); + am.cancel(pi); + } + private void cancelAlarms() { + cancelAlarmSleepExpired(); + cancelAlarmRecordTimeout(); + cancelAlarmDealyedServiceStop(); + } + public boolean setRxRepeatCount(int count) { + if(mReceiver != null) + return mReceiver.setPSRxRepeatCount(count); + else + return false; + } + + //handling the sleep and record stop when FM App not in focus + private void delayedStop(long duration, int nType) { + int whatId = (nType == STOP_SERVICE) ? STOPSERVICE_ONSLEEP: STOPRECORD_ONTIMEOUT; + if (nType == STOP_SERVICE) + setAlarmSleepExpired(duration); + else + setAlarmRecordTimeout(duration); + } + private void cancelDelayedStop(int nType) { + int whatId = (nType == STOP_SERVICE) ? STOPSERVICE_ONSLEEP: STOPRECORD_ONTIMEOUT; + if (nType == STOP_SERVICE) + cancelAlarmSleepExpired(); + else + cancelAlarmRecordTimeout(); + } + private void requestFocus() { + if( (false == mPlaybackInProgress) && + (true == mStoppedOnFocusLoss) ) { + // adding code for audio focus gain. + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + audioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + startFM(); + mStoppedOnFocusLoss = false; + } + } + private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() { + public void onAudioFocusChange(int focusChange) { + mDelayedStopHandler.obtainMessage(FOCUSCHANGE, focusChange, 0).sendToTarget(); + } + }; +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMStats.java b/fmapp/src/com/codeaurora/fmradio/FMStats.java new file mode 100644 index 0000000..00cd56f --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMStats.java @@ -0,0 +1,2013 @@ +/* + * Copyright (c) 2011-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.RadioButton; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.Spinner; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; +import android.widget.AdapterView.OnItemSelectedListener; +import qcom.fmradio.FmReceiver; +import android.os.SystemProperties; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.HashMap; +import android.os.SystemProperties; + + +public class FMStats extends Activity { + + EditText txtbox1; + Button button1; + Button button2; + TextView tv1; + Button SetButton; + Button RunButton; + ProgressBar pbar; + TableLayout tLayout; + + private FmReceiver mReceiver; + + /*Data structure for band*/ + private class Band { + + public int lFreq; + public int hFreq; + public int Spacing; + } + /* Data structure for Result*/ + private class Result { + + private String mFreq; + private String mRSSI; + private String mIoC; + private String mIntDet; + private String mMpxDcc; + private String mSINR; + + + public void setFreq(String aFreq) { + this.mFreq = aFreq; + } + + public String getFreq() { + return mFreq; + } + + public void setRSSI(String aRSSI) { + this.mRSSI = aRSSI; + } + + public String getRSSI() { + return mRSSI; + } + + public void setIoC(String aIoC) { + this.mIoC = aIoC; + } + + public String getIoC() { + return mIoC; + } + + public void setIntDet(String aIntDet) { + this.mIntDet = aIntDet; + } + + public String getIntDet() { + return mIntDet; + } + + public void setMpxDcc(String aMpxDcc) { + this.mMpxDcc = aMpxDcc; + } + + public String getMpxDcc() { + return mMpxDcc; + } + public void setSINR(String aSINR) { + this.mSINR = aSINR; + } + + public String getSINR() { + return mSINR; + } + + }; + + /*constant column header*/ + Result mColumnHeader = new Result(); + + boolean mTestRunning = false; + FmRfItemSelectedListener mSpinFmRfListener = new FmRfItemSelectedListener(); + RfCfgItemSelectedListener mSpinRfCfgListener = new RfCfgItemSelectedListener(); + CfgRfItemSelectedListener1 mSpinCfgRfListener1 = null; + CfgRfItemSelectedListener2 mSpinCfgRfListener2 = null; + + + int mTestSelected = 0; + boolean mIsSearching = false; + private static String LOGTAG = "FMStats"; + private static IFMRadioService mService = null; + private Thread mMultiUpdateThread =null; + private static final int STATUS_UPDATE =1; + private static final int STATUS_DONE =2; + private static final int STOP_ROW_ID =200; + private static final int NEW_ROW_ID = 300; + private int mStopIds = STOP_ROW_ID; + private int mNewRowIds = NEW_ROW_ID; + private static final int SCAN_DWELL_PERIOD = 1; + + private static final int CUR_FREQ_TEST =0; + private static final int CUR_MULTI_TEST = 1; + private static final int SEARCH_TEST =2; + private static final int SWEEP_TEST =3; + private Band mBand =null; + private Band mSync = null; + int Lo = 1, Auto = 0; + + private FileOutputStream mFileCursor =null; + private String mCurrentFileName = null; + + Spinner spinOptionFmRf; + ArrayAdapter<CharSequence> adaptCfgRf; + ArrayAdapter<CharSequence> adaptRfCfg; + ArrayAdapter<CharSequence> adaptFmRf; + + private static boolean mIsTransportSMD = false; + + private static final int MPX_DCC = 0; + private static final int SINR_INTF = 1; + private static final int MIN_SINR_FIRST_STAGE = -128; + private static final int MAX_SINR_FIRST_STAGE = 127; + private static final int MIN_RMSSI_FIRST_STAGE = -128; + private static final int MAX_RMSSI_FIRST_STAGE = 127; + private static final int MIN_CF0TH12 = -2147483648; + private static final int MAX_CF0TH12 = 2147483647; + private static final int MIN_SINR_TH = -128; + private static final int MAX_SINR_TH = 127; + private static final int MIN_SINR_SAMPLES = 0; + private static final int MAX_SINR_SAMPLES = 255; + private static final int MIN_AF_JMP_RMSSI_TH = 0; + private static final int MAX_AF_JMP_RMSSI_TH = 65535; + private static final int MIN_GD_CH_RMSSI_TH = -128; + private static final int MAX_GD_CH_RMSSI_TH = 127; + private static final int MIN_AF_JMP_RMSSI_SAMPLES = 0; + private static final int MAX_AF_JMP_RMSSI_SAMPLES = 255; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + this.setContentView(R.layout.fmstats); + + spinOptionFmRf = (Spinner) findViewById(R.id.spinner); + adaptFmRf = ArrayAdapter.createFromResource( + this, R.array.stats_options, android.R.layout.simple_spinner_item); + adaptFmRf.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + if (spinOptionFmRf != null) { + spinOptionFmRf.setAdapter(adaptFmRf); + spinOptionFmRf.setOnItemSelectedListener(mSpinFmRfListener); + } + + checkTransportLayer(); + if (!isTransportLayerSMD()) { + mSpinCfgRfListener1 = new CfgRfItemSelectedListener1(); + adaptCfgRf = ArrayAdapter.createFromResource( + this, R.array.cfg_rf1, + android.R.layout.simple_spinner_item); + } else { + mSpinCfgRfListener2 = new CfgRfItemSelectedListener2(); + adaptCfgRf = ArrayAdapter.createFromResource( + this, R.array.cfg_rf2, + android.R.layout.simple_spinner_item); + } + adaptRfCfg = ArrayAdapter.createFromResource( + this, R.array.rf_cfg, android.R.layout.simple_spinner_item); + + tLayout = (TableLayout) findViewById(R.id.maintable); + + if(mReceiver == null) + mReceiver = new FmReceiver(); + + long curTime = System.currentTimeMillis(); + mCurrentFileName = "FMStats_".concat(Long.toString(curTime).concat(".txt")); + Log.e(LOGTAG,"Filename is "+mCurrentFileName); + try { + mFileCursor = openFileOutput(mCurrentFileName, Context.MODE_PRIVATE); + if(null != mFileCursor) { + Log.e(LOGTAG, "location of the file is"+getFilesDir()); + } + } catch (IOException e) { + e.printStackTrace(); + Log.e(LOGTAG,"Couldn't create the file to writeLog"); + mCurrentFileName = null; + } + + if (false == bindToService(this, osc)) + { + Log.d(LOGTAG, "onCreate: Failed to Start Service"); + } + else + { + Log.d(LOGTAG, "onCreate: Start Service completed successfully"); + } + + /*Initialize the column header with + constant values*/ + mColumnHeader.setFreq("Freq"); + mColumnHeader.setRSSI("RMSSI"); + mColumnHeader.setIoC("IoC"); + mColumnHeader.setSINR("SINR"); + mColumnHeader.setMpxDcc("Offset"); + mColumnHeader.setIntDet("IntDet"); + } + + public void onDestroy() { + if (null != mFileCursor ) { + try { + mFileCursor.close(); + } catch (IOException e) { + + e.printStackTrace(); + } + } + /*Stop the thread by interrupting it*/ + if (mMultiUpdateThread != null) { + mMultiUpdateThread.interrupt(); + mMultiUpdateThread = null; + } + /*Stop the search/scan if there is an ongoing*/ + if (SEARCH_TEST == mTestSelected) + { + Log.d(LOGTAG, "Stop Search\n"); + try { + mService.cancelSearch(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + unbindFromService(this); + Log.d(LOGTAG, "onDestroy: unbindFromService completed"); + mReceiver = null; + mService = null; + super.onDestroy(); + } + + private View.OnClickListener mOnRunListener = new View.OnClickListener() { + public void onClick(View v) { + if(false == mTestRunning) + { + clearPreviousTestResults(); + mTestRunning = true; + runCurrentTest(); + } + else + { + mTestRunning = false; + /*Set it back to ready to Run*/ + SetButtonState(true); + /*Stop the thread by interrupting it*/ + if (mMultiUpdateThread != null) { + mMultiUpdateThread.interrupt(); + mMultiUpdateThread = null; + } + + if(SEARCH_TEST == mTestSelected ) + { + try { + mService.cancelSearch(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + } + }; + + private void clearPreviousTestResults() + { + TableLayout tl = (TableLayout) findViewById(R.id.maintable); + if (tl != null) { + tl.removeAllViewsInLayout(); + } + mNewRowIds = NEW_ROW_ID; + } + + + private void SetButtonState(boolean state) + { + // Get the TableRow + Button RunButton = (Button)findViewById(R.id.Runbutton); + ProgressBar pbar = (ProgressBar) findViewById(R.id.progressbar); + /*Update the state of the button based on + state*/ + if( state ) + { + if (RunButton != null) { + RunButton.setText(R.string.test_run); + } + if (pbar != null) { + pbar.setVisibility(View.INVISIBLE); + } + } + else + { + if (RunButton != null) { + RunButton.setText("Stop Test"); + } + if (pbar != null) { + pbar.setVisibility(View.VISIBLE); + } + } + } + + private void chooseFMRFoption(){ + String[] szTestInformation = getResources().getStringArray( + R.array.stats_options); + final StringBuilder szbTestHeader = new StringBuilder(); + szbTestHeader.append("running test:").append(szTestInformation[mTestSelected]); + String szTestHeader = new String(szbTestHeader); + switch(mTestSelected){ + case 1: + RunButton = (Button)findViewById(R.id.Runbutton); + if (RunButton != null) { + RunButton.setVisibility(View.INVISIBLE); + } + pbar = (ProgressBar) findViewById(R.id.progressbar); + if (pbar != null) { + pbar.setVisibility(View.INVISIBLE); + } + adaptCfgRf.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinOptionFmRf.setAdapter(adaptCfgRf); + if(isTransportLayerSMD()) + spinOptionFmRf.setOnItemSelectedListener(mSpinCfgRfListener2); + else + spinOptionFmRf.setOnItemSelectedListener(mSpinCfgRfListener1); + break; + case 2: + txtbox1 = (EditText) findViewById(R.id.txtbox1); + tv1 = (TextView) findViewById(R.id.label); + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setVisibility(View.INVISIBLE); + } + Button SetButton = (Button)findViewById(R.id.Setbutton); + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + adaptRfCfg.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinOptionFmRf.setAdapter(adaptRfCfg); + spinOptionFmRf.setOnItemSelectedListener(mSpinRfCfgListener); + break; + } + } + + private View.OnClickListener mOnSetRmssitListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of RMSSI DELTA is : " + rdel); + mReceiver.setRmssiDel(rdel); + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format: " + a); + txtbox1.setText(""); + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + }; + + private View.OnClickListener mOnSetRxRePeatCount = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int count = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for mOnSetRxRePeatCount: " + count); + if ((count < 0) || (count > 255)) + return; + if (mService != null) { + try { + mService.setRxRepeatCount(count); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetSigThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of Signal Th. is : " + rdel); + mReceiver.setSignalThreshold(rdel); + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format: " + a); + txtbox1.setText(""); + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + }; + + private View.OnClickListener mOnSetSinrSmplCntListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of Sinr Samples count is : " + rdel); + if(mService != null) { + try { + mService.setSinrSamplesCnt(rdel); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format: " + a); + txtbox1.setText(""); + } + } + }; + private View.OnClickListener mOnSetSinrThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of Sinr Th is : " + rdel); + if(mService != null) { + try { + mService.setSinrTh(rdel); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format: " + a); + txtbox1.setText(""); + } + } + }; + private View.OnClickListener mOnSetIntfLowThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of Intf Det Low Th is : " + rdel); + if(mService != null) { + try { + mService.setIntfDetLowTh(rdel); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format: " + a); + txtbox1.setText(""); + } + } + }; + private View.OnClickListener mOnSetIntfHighThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rdel = Integer.parseInt(a); + Log.d(LOGTAG, "Value of Intf Det Low Th is : " + rdel); + if(mService != null) { + try { + mService.setIntfDetHighTh(rdel); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetSinrFirstStageListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int sinr = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for SINR FIRST STAGE is : " + sinr); + if((sinr < MIN_SINR_FIRST_STAGE) || + (sinr > MAX_SINR_FIRST_STAGE)) + return; + if(mService != null) { + try { + mService.setSinrFirstStage(sinr); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + private View.OnClickListener mOnSetRmssiFirstStageListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int rmssi = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for RMSSI FIRST STAGE is: " + rmssi); + if((rmssi < MIN_RMSSI_FIRST_STAGE) || + (rmssi > MAX_RMSSI_FIRST_STAGE)) + return; + if(mService != null) { + try { + mService.setRmssiFirstStage(rmssi); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetCFOMeanThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int cf0 = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for CF0TH12 is: " + cf0); + if((cf0 < MIN_CF0TH12) || + (cf0 > MAX_CF0TH12)) + return; + if(mService != null) { + try { + mService.setCFOMeanTh(cf0); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetSearchMPXDCCListener = new View.OnClickListener() { + public void onClick(View v) { + Log.d(LOGTAG, "Value entered for search is: MPX DCC"); + if(mService != null) { + try { + mService.setSearchAlgoType(MPX_DCC); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + }; + private View.OnClickListener mOnSetSearchSinrIntfListener = new View.OnClickListener() { + public void onClick(View v) { + Log.d(LOGTAG, "Value entered for search is: SINR INTF"); + if(mService != null) { + try { + mService.setSearchAlgoType(SINR_INTF); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + }; + + private View.OnClickListener mOnSetAfJmpRmssiThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int th = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for AfJmpRmssiTh is: " + th); + if((th < MIN_AF_JMP_RMSSI_TH) || + (th > MAX_AF_JMP_RMSSI_TH)) + return; + if(mService != null) { + try { + mService.setAfJmpRmssiTh(th); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetGdChRmssiThListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int th = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for Good channel Rmssi Th is: " + th); + if((th < MIN_GD_CH_RMSSI_TH) || + (th > MAX_GD_CH_RMSSI_TH)) + return; + if(mService != null) { + try { + mService.setGoodChRmssiTh(th); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + private View.OnClickListener mOnSetAfJmpRmssiSmplsCntListener = new View.OnClickListener() { + public void onClick(View v) { + String a; + a = txtbox1.getText().toString(); + try { + int cnt = Integer.parseInt(a); + Log.d(LOGTAG, "Value entered for AfJmpRmssiSamples is: " + cnt); + if((cnt < MIN_AF_JMP_RMSSI_SAMPLES) || + (cnt > MAX_AF_JMP_RMSSI_SAMPLES)) + return; + if(mService != null) { + try { + mService.setAfJmpRmssiSamplesCnt(cnt); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } catch (NumberFormatException e) { + Log.e(LOGTAG, "Value entered is not in correct format : " + a); + txtbox1.setText(""); + } + } + }; + + public class CfgRfItemSelectedListener1 implements OnItemSelectedListener { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + Log.d("Table","onItemSelected is hit with " + pos); + txtbox1 = (EditText) findViewById(R.id.txtbox1); + tv1 = (TextView) findViewById(R.id.label); + Button SetButton = (Button)findViewById(R.id.Setbutton); + tLayout.setVisibility(View.INVISIBLE); + switch(pos) + { + case 0: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_rssi); + tv1.setVisibility(View.VISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_rmmsi_delta); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetRmssitListener); + } + break; + case 1: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_sigth); + tv1.setVisibility(View.VISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_sigth); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetSigThListener); + } + break; + case 2: + tLayout.removeAllViewsInLayout(); + mNewRowIds = NEW_ROW_ID; + tLayout.setVisibility(View.VISIBLE); + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + adaptRfCfg.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinOptionFmRf.setAdapter(adaptRfCfg); + spinOptionFmRf.setOnItemSelectedListener(mSpinRfCfgListener); + break; + } + } + + public void onNothingSelected(AdapterView<?> parent) { + // Do Nothing + } + } + + public class CfgRfItemSelectedListener2 implements OnItemSelectedListener { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + Log.d("Table","onItemSelected is hit with " + pos); + int ret = Integer.MAX_VALUE; + txtbox1 = (EditText) findViewById(R.id.txtbox1); + tv1 = (TextView) findViewById(R.id.label); + button1 = (Button)findViewById(R.id.SearchMpxDcc); + button2 = (Button)findViewById(R.id.SearchSinrInt); + Button SetButton = (Button)findViewById(R.id.Setbutton); + tLayout.setVisibility(View.INVISIBLE); + switch(pos) + { + case 0: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_SinrSmplsCnt); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_SinrSmplsCnt); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetSinrSmplCntListener); + } + break; + case 1: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_SinrTh); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_SinrTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetSinrThListener); + } + break; + case 2: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_IntfLowTh); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_IntfLowTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetIntfLowThListener); + } + break; + case 3: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_IntfHighTh); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_IntfHighTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetIntfHighThListener); + } + break; + case 4: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_SinrFirstStage); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_SinrFirstStage); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetSinrFirstStageListener); + } + break; + case 5: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_RmssiFirstStage); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_RmssiFirstStage); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetRmssiFirstStageListener); + } + break; + case 6: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_CF0Th12); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_CF0Th12); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetCFOMeanThListener); + } + break; + case 7: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + if(button1 != null) { + button1.setText(R.string.search_algo_mpx); + button1.setVisibility(View.VISIBLE); + button1.setOnClickListener(mOnSetSearchMPXDCCListener); + } + if(button2 != null) { + button2.setText(R.string.search_algo_sinrint); + button2.setVisibility(View.VISIBLE); + button2.setOnClickListener(mOnSetSearchSinrIntfListener); + } + break; + case 8: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) + ret = mService.getSinrSamplesCnt(); + Log.d(LOGTAG, "Get Sinr Samples Count: " + ret); + if((ret >= MIN_SINR_SAMPLES) && + (ret <= MAX_SINR_SAMPLES)) + tv1.setText(" " + String.valueOf(ret)); + }catch (RemoteException e) { + } + break; + case 9: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) + ret = mService.getSinrTh(); + Log.d(LOGTAG, "Get Sinr Threshold: " + ret); + if((ret >= MIN_SINR_TH) && + (ret <= MAX_SINR_TH)) + tv1.setText(" " + String.valueOf(ret)); + }catch (RemoteException e) { + + } + break; + case 10: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getSinrFirstStage(); + Log.d(LOGTAG, "Get Sinr First Stage: " + ret); + if (ret >= MIN_SINR_FIRST_STAGE && + ret <= MAX_SINR_FIRST_STAGE) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 11: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getRmssiFirstStage(); + Log.d(LOGTAG, "Get Rmssi First Stage: " + ret); + if (ret >= MIN_RMSSI_FIRST_STAGE && + ret <= MAX_RMSSI_FIRST_STAGE) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 12: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getCFOMeanTh(); + Log.d(LOGTAG, "Get CF0 Threshold: " + ret); + if (ret >= MIN_CF0TH12 && + ret <= MAX_CF0TH12) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 13: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getSearchAlgoType(); + Log.d(LOGTAG, "Search Type: " + ret); + if (ret == MPX_DCC) + tv1.setText(R.string.search_algo_mpx); + else if(ret == SINR_INTF) + tv1.setText(R.string.search_algo_sinrint); + } + }catch (RemoteException e) { + + } + break; + case 14: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_AfJmpRmssiTh); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_AfJmpRmssiTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetAfJmpRmssiThListener); + } + break; + case 15: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_GdChRmssiTh); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_GdChRmssiTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetGdChRmssiThListener); + } + break; + case 16: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_AfJmpRmssiSmplsCnt); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_AfJmpRmssiSmplsCnt); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetAfJmpRmssiSmplsCntListener); + } + break; + case 17: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getAfJmpRmssiTh(); + Log.d(LOGTAG, "Get Af Jmp Rmssi Th: " + ret); + if ((ret >= MIN_AF_JMP_RMSSI_TH) && + (ret <= MAX_AF_JMP_RMSSI_TH)) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 18: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getGoodChRmssiTh(); + Log.d(LOGTAG, "Get GoodChRmssi Threshold: " + ret); + if ((ret >= MIN_GD_CH_RMSSI_TH) && + (ret <= MAX_GD_CH_RMSSI_TH)) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 19: + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setText(""); + tv1.setVisibility(View.VISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + try { + if(mService != null) { + ret = mService.getAfJmpRmssiSamplesCnt(); + Log.d(LOGTAG, "Get AfJmpRmssiSamples count: " + ret); + if ((ret >= MIN_AF_JMP_RMSSI_SAMPLES) && + (ret <= MAX_AF_JMP_RMSSI_SAMPLES)) + tv1.setText(" " + String.valueOf(ret)); + } + }catch (RemoteException e) { + + } + break; + case 20: + tLayout.removeAllViewsInLayout(); + mNewRowIds = NEW_ROW_ID; + tLayout.setVisibility(View.VISIBLE); + if (txtbox1 != null) { + txtbox1.setVisibility(View.INVISIBLE); + } + if (tv1 != null) { + tv1.setVisibility(View.INVISIBLE); + } + if (SetButton != null) { + SetButton.setVisibility(View.INVISIBLE); + } + if(button1 != null) { + button1.setVisibility(View.INVISIBLE); + } + if(button2 != null) { + button2.setVisibility(View.INVISIBLE); + } + adaptRfCfg.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinOptionFmRf.setAdapter(adaptRfCfg); + spinOptionFmRf.setOnItemSelectedListener(mSpinRfCfgListener); + break; + case 21: + if (txtbox1 != null) { + txtbox1.setText(R.string.type_rd); + txtbox1.setVisibility(View.VISIBLE); + } + if (tv1 != null) { + tv1.setText(R.string.enter_RxRePeatCount); + tv1.setVisibility(View.VISIBLE); + } + if (SetButton != null) { + SetButton.setText(R.string.set_RxRePeatCount); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetRxRePeatCount); + } + break; + } + } + public void onNothingSelected(AdapterView<?> parent) { + // Do Nothing + } + } + + public class RfCfgItemSelectedListener implements OnItemSelectedListener { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + Log.d("Table","onItemSelected is hit with "+pos); + tLayout.setVisibility(View.INVISIBLE); + if (mTestRunning) + stopCurTest(); + switch(pos) + { + case 0: + case 1: + case 2: + case 3: + mTestSelected = pos; + tLayout.removeAllViewsInLayout(); + mNewRowIds = NEW_ROW_ID; + tLayout.setVisibility(View.VISIBLE); + RunButton = (Button)findViewById(R.id.Runbutton); + if (RunButton != null) { + RunButton.setText(R.string.test_run); + RunButton.setVisibility(View.VISIBLE); + RunButton.setOnClickListener(mOnRunListener); + } + break; + case 4: + RunButton = (Button)findViewById(R.id.Runbutton); + if (RunButton != null) { + RunButton.setVisibility(View.INVISIBLE); + } + pbar = (ProgressBar) findViewById(R.id.progressbar); + if (pbar != null) { + pbar.setVisibility(View.INVISIBLE); + } + adaptCfgRf.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinOptionFmRf.setAdapter(adaptCfgRf); + if(isTransportLayerSMD()) + spinOptionFmRf.setOnItemSelectedListener(mSpinCfgRfListener2); + else + spinOptionFmRf.setOnItemSelectedListener(mSpinCfgRfListener1); + break; + } + } + + public void onNothingSelected(AdapterView<?> parent) { + // Do Nothing + } + } + public class FmRfItemSelectedListener implements OnItemSelectedListener { + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + Log.d("Table","onItemSelected is hit with "+pos); + mTestSelected = pos; + tLayout.setVisibility(View.INVISIBLE); + chooseFMRFoption(); + + } + + public void onNothingSelected(AdapterView<?> parent) { + // Do Nothing + } + } + private void checkTransportLayer() { + String transportLayer = ""; + + transportLayer = SystemProperties.get("ro.qualcomm.bt.hci_transport"); + if(transportLayer.equals("smd")) + mIsTransportSMD = true; + } + private boolean isTransportLayerSMD() { + return mIsTransportSMD; + } + private void createResult(Result aRes) { + // Get the TableLayout + TableLayout tl = (TableLayout) findViewById(R.id.maintable); + if (tl == null) { + return; + } + + /* Create a new row to be added. */ + mNewRowIds++; + TableRow tr2 = new TableRow(getApplicationContext()); + int width = getWindowManager().getDefaultDisplay().getWidth(); + tr2.setLayoutParams(new LayoutParams( + LayoutParams.FILL_PARENT, + LayoutParams.WRAP_CONTENT)); + tr2.setId(mNewRowIds); + /* Create a Button to be the row-content. */ + TextView colFreq = new TextView(getApplicationContext()); + colFreq.setText(aRes.getFreq()); + colFreq.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); + colFreq.setWidth(width/4); + /* Add Button to row. */ + tr2.addView(colFreq); + + TextView colRMSSI = new TextView(getApplicationContext()); + colRMSSI.setText(aRes.getRSSI()); + colRMSSI.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); + colRMSSI.setWidth(width/4); + tr2.addView(colRMSSI); + + TextView colIoC = new TextView(getApplicationContext()); + colIoC.setText(aRes.getIoC()); + colIoC.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); + colIoC.setWidth(width/4); + tr2.addView(colIoC); + if(isTransportLayerSMD()) + { + TextView colSINR = new TextView(getApplicationContext()); + colSINR.setText(aRes.getSINR()); + colSINR.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); + colSINR.setWidth(width/4); + tr2.addView(colSINR); + } else + { + TextView colMpxDcc = new TextView(getApplicationContext()); + colMpxDcc.setText(aRes.getMpxDcc()); + colMpxDcc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); + colMpxDcc.setWidth(width/4); + tr2.addView(colMpxDcc); + } + /* Add row to TableLayout. */ + /* Add row to TableLayout. */ + tl.addView(tr2,new TableLayout.LayoutParams( + LayoutParams.FILL_PARENT, + LayoutParams.WRAP_CONTENT)); + if(null != mFileCursor) + { + try { + StringBuilder tempStr = new StringBuilder(); + tempStr.append(String.format("%10s", aRes.getFreq())); + tempStr.append(String.format("%10s", aRes.getRSSI())); + tempStr.append(String.format("%10s", aRes.getIoC())); + tempStr.append(String.format("%10s", aRes.getIntDet())); + if(isTransportLayerSMD()) + { + tempStr.append(String.format("%10s", aRes.getSINR())); + } else + { + tempStr.append(String.format("%10s", aRes.getMpxDcc())); + } + tempStr.append("\r\n"); + String testStr = new String(tempStr); + mFileCursor.write(testStr.getBytes()); + } catch(IOException ioe) { + ioe.printStackTrace(); + } + } + } + + + private void runCurrentTest(){ + Log.d(LOGTAG, "The test being run is" +mTestSelected); + + //get test summary + String[] szTestInformation = getResources().getStringArray( + R.array.rf_cfg); + final StringBuilder szbTestHeader = new StringBuilder(); + szbTestHeader.append("running test:").append(szTestInformation[mTestSelected]); + szbTestHeader.append("\r\n"); + String szTestHeader = new String(szbTestHeader); + if(null != mFileCursor ) + { + try { + mFileCursor.write(szTestHeader.getBytes()); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + switch(mTestSelected){ + case CUR_FREQ_TEST: + Log.d(LOGTAG,"Current Freq test is going to run"); + int freq = FmSharedPreferences.getTunedFrequency(); + Result res = GetFMStatsForFreq(freq); + createResult(mColumnHeader); + if(res != null) + createResult(res); + mTestRunning = false; + break; + case CUR_MULTI_TEST: + /*Set it to ready to Stop*/ + SetButtonState(false); + createResult(mColumnHeader); + + if (mMultiUpdateThread == null) + { + mMultiUpdateThread = new Thread(null, getMultipleResults, + "MultiResultsThread"); + } + /* Launch dummy thread to simulate the transfer progress */ + Log.d(LOGTAG, "Thread State: " + mMultiUpdateThread.getState()); + if (mMultiUpdateThread.getState() == Thread.State.TERMINATED) + { + mMultiUpdateThread = new Thread(null, getMultipleResults, + "MultiResultsThread"); + } + /* If the thread state is "new" then the thread has not yet started */ + if (mMultiUpdateThread.getState() == Thread.State.NEW) + { + mMultiUpdateThread.start(); + } + // returns and UI in different thread. + break; + case SEARCH_TEST: + try { + Log.d(LOGTAG, "start scanning\n"); + if(isTransportLayerSMD()) { + Log.d(LOGTAG,"Scanning with 0 scan time"); + if (mReceiver != null) + mIsSearching = mReceiver.searchStations(FmReceiver.FM_RX_SRCH_MODE_SCAN, + SCAN_DWELL_PERIOD, FmReceiver.FM_RX_SEARCHDIR_UP); + } else { + mIsSearching = mService.scan(0); + } + } catch (RemoteException e) { + + e.printStackTrace(); + } + + if(mIsSearching) + { + /*Set it to Ready to Stop*/ + SetButtonState(false); + createResult(mColumnHeader); + Log.d(LOGTAG, "Created the results and cancel UI\n"); + } + else + { + mTestRunning = false; + } + break; + case SWEEP_TEST: + int Spacing = FmSharedPreferences.getChSpacing(); + int lowerFreq = FmSharedPreferences.getLowerLimit(); + int higherFreq = FmSharedPreferences.getUpperLimit(); + try { + Log.d(LOGTAG, "Going to set low side injection\n"); + mService.setHiLoInj(Lo); + } catch (RemoteException e) { + + e.printStackTrace(); + } + /* Set it to Ready to stop*/ + SetButtonState(false); + createResult(mColumnHeader); + getFMStatsInBand(lowerFreq,higherFreq,Spacing); + break; + } + } + + /* Thread processing */ + private Runnable getMultipleResults = new Runnable() { + public void run() { + /*Collect the data for the current frequency + 20 times*/ + int freq = FmSharedPreferences.getTunedFrequency(); + + for (int i = 0; i < 20; i++) + { + try + { + Thread.sleep(500); + Message updateUI = new Message(); + updateUI.what = STATUS_UPDATE; + updateUI.obj = (Object)GetFMStatsForFreq(freq); + if (updateUI.obj == null) + break; + mUIUpdateHandlerHandler.sendMessage(updateUI); + } catch (InterruptedException e) + { + /*break the loop*/ + break; + } + } + mTestRunning = false; + Message updateStop = new Message(); + updateStop.what = STATUS_DONE; + mUIUpdateHandlerHandler.sendMessage(updateStop); + } + }; + + private void getFMStatsInBand(int lFreq, int hFreq, int Spacing) + { + if( null == mBand) { + mBand = new Band(); + } + mBand.lFreq = lFreq; + mBand.hFreq = hFreq; + if(Spacing == 0) + { + mBand.Spacing = 200; // 200KHz + } + else if( Spacing == 1) + { + mBand.Spacing = 100; // 100KHz + } + else + { + mBand.Spacing = 50; + } + + if (mMultiUpdateThread == null) + { + mMultiUpdateThread = new Thread(null, getSweepResults, + "MultiResultsThread"); + } + /* Launch he dummy thread to simulate the transfer progress */ + Log.d(LOGTAG, "Thread State: " + mMultiUpdateThread.getState()); + if (mMultiUpdateThread.getState() == Thread.State.TERMINATED) + { + mMultiUpdateThread = new Thread(null, getSweepResults, + "MultiResultsThread"); + } + /* If the thread state is "new" then the thread has not yet started */ + if (mMultiUpdateThread.getState() == Thread.State.NEW) + { + mMultiUpdateThread.start(); + } + } + + /* Thread processing */ + private Runnable getSweepResults = new Runnable() { + public void run() { + for (int i = mBand.lFreq; (i <= mBand.hFreq) && (mService != null); + i += mBand.Spacing) + { + try { + if (!mService.tune(i)) { + Log.e(LOGTAG, "tune failed"); + break; + } + mSync = new Band(); + synchronized(mSync) { + mSync.wait(); //wait till notified + } + mSync = null; + Message updateUI = new Message(); + updateUI.what = STATUS_UPDATE; + updateUI.obj = (Object)GetFMStatsForFreq(i); + if (updateUI.obj == null) { + break; + } else { + mUIUpdateHandlerHandler.sendMessage(updateUI); + Log.d(LOGTAG,"highFerq is "+mBand.hFreq); + } + } + catch (RemoteException e) { + Log.e(LOGTAG, "SweepResults:Tune failed\n"); + } + + catch (InterruptedException e) { + /*Stop the thrad*/ + break; + } + } + mTestRunning = false; + Message updateStop = new Message(); + updateStop.what = STATUS_DONE; + try { + Log.d(LOGTAG, "Going to set auto hi-lo injection\n"); + mService.setHiLoInj(Auto); + } catch (RemoteException e) { + e.printStackTrace(); + } + mUIUpdateHandlerHandler.sendMessage(updateStop); + } + }; + + private Result GetFMStatsForFreq(int freq) + { + Result result = new Result(); + Log.d(LOGTAG,"freq is "+freq); + result.setFreq(Integer.toString(freq)); + byte nRssi = 0; + int nIoC = 0; + int dummy = 0; + int nIntDet = 0; + int nMpxDcc = 0; + byte nSINR = 0; + if((null != mService)) { + try { + dummy = mService.getRssi(); + if (dummy != Integer.MAX_VALUE) { + nRssi = (byte)dummy; + result.setRSSI(Byte.toString(nRssi)); + } else { + return null; + } + } catch (RemoteException e) { + e.printStackTrace(); + } + + try { + nIoC = mService.getIoC(); + if (nIoC != Integer.MAX_VALUE) + result.setIoC(Integer.toString(nIoC)); + else + return null; + } catch (RemoteException e) { + e.printStackTrace(); + } + + if(isTransportLayerSMD()) { + try { + dummy = mService.getSINR(); + if (dummy != Integer.MAX_VALUE) { + nSINR = (byte)dummy; + result.setSINR(Integer.toString(nSINR)); + } else { + return null; + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + try { + nMpxDcc = mService.getMpxDcc(); + if (nMpxDcc != Integer.MAX_VALUE) + result.setMpxDcc(Integer.toString(nMpxDcc)); + else + return null; + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + try { + nIntDet = mService.getIntDet(); + if (nIntDet != Integer.MAX_VALUE) + result.setIntDet(Integer.toString(nIntDet)); + else + return null; + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + return null; + } + return result; + } + + + private Handler mUIUpdateHandlerHandler = new Handler() { + public void handleMessage(Message msg) { + switch (msg.what) + { + case STATUS_UPDATE: + Result myRes = (Result) msg.obj; + Log.d(LOGTAG,"Status update is" +myRes.mFreq); + createResult(myRes); + break; + case STATUS_DONE: + SetButtonState(true); + break; + } + } + }; + + public static IFMRadioService sService = null; + private static HashMap<Context, ServiceBinder> sConnectionMap = new HashMap<Context, ServiceBinder>(); + + public static boolean bindToService(Context context) { + Log.e(LOGTAG, "bindToService: Context"); + return bindToService(context, null); + } + + public static boolean bindToService(Context context, ServiceConnection callback) { + Log.e(LOGTAG, "bindToService: Context with serviceconnection callback"); + context.startService(new Intent(context, FMRadioService.class)); + ServiceBinder sb = new ServiceBinder(callback); + sConnectionMap.put(context, sb); + return context.bindService((new Intent()).setClass(context, + FMRadioService.class), sb, 0); + } + + public static void unbindFromService(Context context) { + ServiceBinder sb = (ServiceBinder) sConnectionMap.remove(context); + Log.e(LOGTAG, "unbindFromService: Context"); + if (sb == null) + { + Log.e(LOGTAG, "Trying to unbind for unknown Context"); + return; + } + context.unbindService(sb); + if (sConnectionMap.isEmpty()) + { + // presumably there is nobody interested in the service at this point, + // so don't hang on to the ServiceConnection + sService = null; + } + } + + private static class ServiceBinder implements ServiceConnection + { + ServiceConnection mCallback; + ServiceBinder(ServiceConnection callback) { + mCallback = callback; + } + + public void onServiceConnected(ComponentName className, android.os.IBinder service) { + sService = IFMRadioService.Stub.asInterface(service); + if (mCallback != null) + { + Log.e(LOGTAG, "onServiceConnected: mCallback"); + mCallback.onServiceConnected(className, service); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (mCallback != null) + { + mCallback.onServiceDisconnected(className); + } + sService = null; + } + } + + + private ServiceConnection osc = new ServiceConnection() { + public void onServiceConnected(ComponentName classname, IBinder obj) { + mService = IFMRadioService.Stub.asInterface(obj); + Log.e(LOGTAG, "ServiceConnection: onServiceConnected: "); + if (mService != null) + { + try + { + mService.registerCallbacks(mServiceCallbacks); + + } catch (RemoteException e) + { + e.printStackTrace(); + } + return; + } else + { + Log.e(LOGTAG, "IFMRadioService onServiceConnected failed"); + } + finish(); + } + public void onServiceDisconnected(ComponentName classname) { + } + }; + + + private IFMRadioServiceCallbacks.Stub mServiceCallbacks = new IFMRadioServiceCallbacks.Stub() + { + public void onEnabled() + { + Log.d(LOGTAG, "mServiceCallbacks.onEnabled :"); + } + + public void onDisabled() + { + Log.d(LOGTAG, "mServiceCallbacks.onDisabled :"); + stopCurTest(); + } + + public void onRadioReset() + { + Log.d(LOGTAG, "mServiceCallbacks.onRadioReset :"); + } + + public void onTuneStatusChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onTuneStatusChanged :"); + if (mTestRunning) + mHandler.post(mTuneComplete); + } + + public void onProgramServiceChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onProgramServiceChanged :"); + } + + public void onRadioTextChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onRadioTextChanged :"); + } + public void onExtenRadioTextChanged() + { + Log.d(LOGTAG, "Extended Radio Text changed:"); + } + public void onAlternateFrequencyChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onAlternateFrequencyChanged :"); + } + + public void onSignalStrengthChanged() + { + Log.d(LOGTAG, "mServiceCallbacks.onSignalStrengthChanged :"); + } + + public void onSearchComplete() + { + Log.d(LOGTAG, "mServiceCallbacks.onSearchComplete :"); + /*Stop the update*/ + mTestRunning = false; + Message updateStop = new Message(); + updateStop.what = STATUS_DONE; + mUIUpdateHandlerHandler.sendMessage(updateStop); + } + public void onSearchListComplete() + { + Log.d(LOGTAG, "mServiceCallbacks.onSearchListComplete :"); + + } + + public void onMute(boolean bMuted) + { + Log.d(LOGTAG, "mServiceCallbacks.onMute :" + bMuted); + } + + public void onAudioUpdate(boolean bStereo) + { + Log.d(LOGTAG, "mServiceCallbacks.onAudioUpdate :" + bStereo); + } + + public void onStationRDSSupported(boolean bRDSSupported) + { + Log.d(LOGTAG, "mServiceCallbacks.onStationRDSSupported :" + bRDSSupported); + } + public void onRecordingStopped() + { + Log.d(LOGTAG, "mServiceCallbacks.onDisabled :"); + } + public void onFinishActivity() + { + Log.d(LOGTAG, "mServiceCallbacks.onFinishActivity:"); + } + }; + /* Radio Vars */ + final Handler mHandler = new Handler(); + + final Runnable mTuneComplete = new Runnable(){ + public void run(){ + if((null != mMultiUpdateThread) &&(null != mSync)) + { + synchronized(mSync){ + mSync.notify(); + } + } + if((mTestSelected == SEARCH_TEST) && (mService != null)) { + /* On every Tune Complete generate the result for the current + Frequency*/ + Message updateUI = new Message(); + updateUI.what = STATUS_UPDATE; + int freq = FmSharedPreferences.getTunedFrequency(); + updateUI.obj = (Object)GetFMStatsForFreq(freq); + if (updateUI.obj == null) + updateUI.what = STATUS_DONE; + mUIUpdateHandlerHandler.sendMessage(updateUI); + } + } + }; + + private void stopCurTest() { + if (mTestRunning) { + switch(mTestSelected) { + case CUR_FREQ_TEST: + break; + case SWEEP_TEST: + case CUR_MULTI_TEST: + if (mMultiUpdateThread != null) + mMultiUpdateThread.interrupt(); + break; + case SEARCH_TEST: + mHandler.removeCallbacks(mTuneComplete); + if (mService != null) { + try { + Message updateStop = new Message(); + updateStop.what = STATUS_DONE; + mUIUpdateHandlerHandler.sendMessage(updateStop); + mService.cancelSearch(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + break; + } + mTestRunning = false; + } + } + } diff --git a/fmapp/src/com/codeaurora/fmradio/FMTransmitterActivity.java b/fmapp/src/com/codeaurora/fmradio/FMTransmitterActivity.java new file mode 100644 index 0000000..f21ed00 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMTransmitterActivity.java @@ -0,0 +1,1477 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; +import android.media.AudioManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; //import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import android.text.TextUtils; //import android.app.ProgressDialog; + +import java.lang.ref.WeakReference; //import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.ArrayList; + +import com.codeaurora.utils.FrequencyPicker; +import com.codeaurora.utils.FrequencyPickerDialog; +import android.content.ServiceConnection; + +import qcom.fmradio.FmConfig; +import android.os.ServiceManager; + +public class FMTransmitterActivity extends Activity { + public static final String LOGTAG = "FMTransmitterActivity"; + + /* menu Identifiers */ + private static final int MENU_SCAN_START = Menu.FIRST + 1; + private static final int MENU_SCAN_STOP = Menu.FIRST + 2; + private static final int MENU_SETTINGS = Menu.FIRST + 3; + + /* Dialog Identifiers */ + private static final int DIALOG_PRESET_LIST_AUTO_SET = 1; + private static final int DIALOG_PICK_FREQUENCY = 2; + private static final int DIALOG_PRESET_OPTIONS = 3; + private static final int DIALOG_PROGRESS_PROGRESS = 5; + private static final int DIALOG_CMD_FAILED_HDMI_ON = 14; + /* Activity Return ResultIdentifiers */ + private static final int ACTIVITY_RESULT_SETTINGS = 1; + + private static final int MAX_PRESETS = 6; + private static IFMTransmitterService mService = null; + private static FmSharedPreferences mPrefs; + + /* Button Resources */ + private ImageButton mOnOffButton; + /* 6 Preset Buttons */ + private Button[] mPresetButtons = { null, null, null, null, null, null }; + private int[] mPresetFrequencies = { 0, 0, 0, 0, 0, 0 }; + // private ImageButton mSearchButton; + private ImageView mForwardButton; + private ImageView mBackButton; + + /* row in the station info layout */ + private TextView mTransmitStaticMsgTV; + private TextView mTuneStationFrequencyTV; + + /* Bottom row in the station info layout */ + private TextView mRadioTextTV; + + /* Current Status Indicators */ + private static boolean mIsSearching = false; + private static boolean mSearchingResultStatus = false; + + + private boolean mInternalAntennaAvailable = false; + + private Animation mAnimation = null; + private ScrollerText mRadioTextScroller = null; + + private static int mTunedFrequency = 0;; + private int mPresetButtonIndex = -1; + private String mMetaData = null; + private String mPSData = null; + + /* Radio Vars */ + final Handler mHandler = new Handler(); + private final Handler enableRadioHandler = new Handler(); + private final Handler disableRadioHandler = new Handler(); + + /* Search Progress Dialog */ + private ProgressDialog mProgressDialog = null; + + private LoadedDataAndState SavedDataAndState = null; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + mPrefs = new FmSharedPreferences(this); + Log.d(LOGTAG, "onCreate - Height : " + + getWindowManager().getDefaultDisplay().getHeight() + + " - Width : " + + getWindowManager().getDefaultDisplay().getWidth()); + setContentView(R.layout.fmtransmitter); + SavedDataAndState = (LoadedDataAndState)getLastNonConfigurationInstance(); + mAnimation = AnimationUtils.loadAnimation(this, R.anim.preset_select); + + mOnOffButton = (ImageButton) findViewById(R.id.btn_onoff); + if (mOnOffButton != null) { + mOnOffButton.setOnClickListener(mTurnOnOffClickListener); + } + + mForwardButton = (ImageView) findViewById(R.id.btn_forward); + if (mForwardButton != null) { + mForwardButton.setOnClickListener(mForwardClickListener); + } + + mBackButton = (ImageView) findViewById(R.id.btn_back); + if (mBackButton != null) { + mBackButton.setOnClickListener(mBackClickListener); + } + + /* Preset Buttons */ + mPresetButtons[0] = (Button) findViewById(R.id.presets_button_1); + mPresetButtons[1] = (Button) findViewById(R.id.presets_button_2); + mPresetButtons[2] = (Button) findViewById(R.id.presets_button_3); + mPresetButtons[3] = (Button) findViewById(R.id.presets_button_4); + mPresetButtons[4] = (Button) findViewById(R.id.presets_button_5); + mPresetButtons[5] = (Button) findViewById(R.id.presets_button_6); + for (int nButton = 0; nButton < MAX_PRESETS; nButton++) { + if (mPresetButtons[nButton] != null) { + mPresetButtons[nButton] + .setOnClickListener(mPresetButtonClickListener); + mPresetButtons[nButton] + .setOnLongClickListener(mPresetButtonOnLongClickListener); + } + } + + mTransmitStaticMsgTV = (TextView) findViewById(R.id.transmit_msg_tv); + String str = getString(R.string.transmit_msg_string); + if(null != mPSData ) { + str = mPSData.concat("\n").concat(str); + } + if (mTransmitStaticMsgTV != null) { + mTransmitStaticMsgTV.setText(str); + } + + mTuneStationFrequencyTV = (TextView) findViewById(R.id.prog_frequency_tv); + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV + .setOnLongClickListener(mFrequencyViewClickListener); + } + + mRadioTextTV = (TextView) findViewById(R.id.radio_text_tv); + + if ((mRadioTextScroller == null) && (mRadioTextTV != null)) { + mRadioTextScroller = new ScrollerText(mRadioTextTV); + } + + enableRadioOnOffUI(false); + //HDMI and FM concurrecny is not supported. + if (isHdmiOn()) + { + showDialog(DIALOG_CMD_FAILED_HDMI_ON); + } + else { + if (false == bindToService(this, osc)) + { + Log.d(LOGTAG, "onCreate: Failed to Start Service"); + } else + { + Log.d(LOGTAG, "onCreate: Start Service completed successfully"); + } + } + } + + @Override + public void onRestart() { + Log.d(LOGTAG, "onRestart"); + super.onRestart(); + } + + @Override + public void onStop() { + Log.d(LOGTAG, "onStop"); + super.onStop(); + } + + @Override + public void onStart() { + super.onStart(); + Log.d(LOGTAG, "onStart"); + try { + if (mService != null) { + mService.registerCallbacks(mServiceCallbacks); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + + } + + @Override + protected void onPause() { + super.onPause(); + mRadioTextScroller.stopScroll(); + SavePreferences(); + } + + private static final String TX_PREF_LAST_TUNED_FREQUENCY = "last_tx_frequency"; + private static final String TX_PRESET_FREQUENCY = "tx_preset_freq_"; + private static final int DEFAULT_NO_FREQUENCY = 98100; + + public void SavePreferences() { + Log.d(LOGTAG, "Save preferences "); + SharedPreferences sp = getPreferences(Context.MODE_PRIVATE); + SharedPreferences.Editor ed = sp.edit(); + + ed.putInt(TX_PREF_LAST_TUNED_FREQUENCY, mTunedFrequency); + + for (int presetIndex = 0; presetIndex < MAX_PRESETS; presetIndex++) { + ed.putInt(TX_PRESET_FREQUENCY + presetIndex, + mPresetFrequencies[presetIndex]); + } + ed.commit(); + } + + public void LoadPreferences() { + SharedPreferences sp = getPreferences(Context.MODE_PRIVATE); + mTunedFrequency = sp.getInt(TX_PREF_LAST_TUNED_FREQUENCY, DEFAULT_NO_FREQUENCY); + for (int presetIndex = 0; presetIndex < MAX_PRESETS; presetIndex++) { + mPresetFrequencies[presetIndex] = + sp.getInt(TX_PRESET_FREQUENCY + presetIndex, DEFAULT_NO_FREQUENCY); + } + } + + @Override + public void onResume() { + super.onResume(); + LoadPreferences(); + try { + if((null != mService )&&mService.isFmOn()) { + mHandler.post(mUpdateRadioText); + } + } catch(RemoteException ex){ + Log.d(LOGTAG,"expection for service"); + } + mHandler.post(mUpdateRadioText); + updateStationInfoToUI(); + enableRadioOnOffUI(); + } + + @Override + public void onDestroy() { + unbindFromService(this); + mService = null; + + Log.d(LOGTAG, "onDestroy: unbindFromService completed"); + super.onDestroy(); + } + + private class LoadedDataAndState { + public LoadedDataAndState(){}; + public boolean onOrOff; + } + + @Override + public Object onRetainNonConfigurationInstance() { + final LoadedDataAndState data = new LoadedDataAndState(); + if(mService != null) { + try { + data.onOrOff = mService.isFmOn(); + } catch(RemoteException e) { + data.onOrOff = false; + e.printStackTrace(); + } + } else { + data.onOrOff = false; + } + return data; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuItem item; + boolean radioOn = isFmOn(); + boolean searchActive = isSearchActive(); + + item = menu.add(0, MENU_SCAN_START, 0, R.string.menu_scan_for_preset) + .setIcon(R.drawable.ic_btn_search); + if (item != null) { + item.setVisible(!searchActive && radioOn); + } + item = menu.add(0, MENU_SCAN_STOP, 0, R.string.menu_scan_stop).setIcon( + R.drawable.ic_btn_search); + if (item != null) { + item.setVisible(searchActive && radioOn); + } + + /* Settings can be active */ + item = menu.add(0, MENU_SETTINGS, 0, R.string.menu_settings).setIcon( + android.R.drawable.ic_menu_preferences); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + MenuItem item; + boolean radioOn = isFmOn(); + boolean searchActive = isSearchActive(); + + item = menu.findItem(MENU_SCAN_START); + if (item != null) { + item.setVisible(!searchActive && radioOn); + } + item = menu.findItem(MENU_SCAN_STOP); + if (item != null) { + item.setVisible(searchActive && radioOn); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_SETTINGS: + Intent launchPreferencesIntent = new Intent().setClass(this, + Settings.class); + launchPreferencesIntent.putExtra(Settings.RX_MODE,false); + startActivityForResult(launchPreferencesIntent, + ACTIVITY_RESULT_SETTINGS); + + // startActivity(launchPreferencesIntent); + return true; + + case MENU_SCAN_START: + showDialog(DIALOG_PRESET_LIST_AUTO_SET); + return true; + + case MENU_SCAN_STOP: + cancelSearch(); + return true; + + default: + break; + } + return super.onOptionsItemSelected(item); + } + + private boolean isHdmiOn() { + //HDMI and FM concurrecny is not supported. + try { + String hdmiUserOption = android.provider.Settings.System.getString( + getContentResolver(), "HDMI_USEROPTION"); + } + catch (Exception ex){ + Log.d(LOGTAG,"Get HDMI open failed"); + } + return false; + } + @Override + protected Dialog onCreateDialog(int id) { + switch (id) { + case DIALOG_PRESET_LIST_AUTO_SET: { + return createPresetListAutoSelectWarnDlg(id); + } + case DIALOG_PICK_FREQUENCY: { + FmConfig fmConfig = FmSharedPreferences.getFMConfiguration(); + return new FrequencyPickerDialog(this, fmConfig, mTunedFrequency, + mFrequencyChangeListener); + } + case DIALOG_PROGRESS_PROGRESS: { + return createProgressDialog(id); + } + case DIALOG_PRESET_OPTIONS: { + return createPresetOptionsDlg(id); + } + default: + break; + } + return null; + } + + @Override + protected void onPrepareDialog(int id, Dialog dialog) { + super.onPrepareDialog(id, dialog); + switch (id) { + case DIALOG_PICK_FREQUENCY: { + ((FrequencyPickerDialog) dialog).UpdateFrequency(mTunedFrequency); + break; + } + case DIALOG_PRESET_OPTIONS: { + AlertDialog alertDlg = ((AlertDialog) dialog); + if ((alertDlg != null) && (mPresetButtonIndex >= 0) + && (mPresetButtonIndex <= MAX_PRESETS)) { + int frequency = mPresetFrequencies[mPresetButtonIndex]; + alertDlg.setTitle(PresetStation.getFrequencyString(frequency)); + } + break; + } + + default: + break; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(LOGTAG, "onActivityResult : requestCode -> " + requestCode); + Log.d(LOGTAG, "onActivityResult : resultCode -> " + resultCode); + if (requestCode ==ACTIVITY_RESULT_SETTINGS) + { + if (resultCode == RESULT_OK) + { + /* */ + if (data != null) + { + String action = data.getAction(); + if (action != null) + { + if (action.equals(Settings.RESTORE_FACTORY_DEFAULT_ACTION)) + { + RestoreDefaults(); + enableRadioOnOffUI(); + tuneRadio(mTunedFrequency); + } + } + } + } //if ACTIVITY_RESULT_SETTINGS + }//if (resultCode == RESULT_OK) + } + + + + + /** + * @return true if a internal antenna is available. + * + */ + boolean isInternalAntennaAvailable() { + return mInternalAntennaAvailable; + } + + private Dialog createPresetOptionsDlg(int id) { + if ((mPresetButtonIndex >= 0) && (mPresetButtonIndex <= MAX_PRESETS)) { + int frequency = mPresetFrequencies[mPresetButtonIndex]; + AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this); + dlgBuilder.setTitle(PresetStation.getFrequencyString(frequency)); + ArrayList<String> arrayList = new ArrayList<String>(); + arrayList.add(getResources().getString(R.string.preset_tune)); + arrayList.add(getResources().getString(R.string.preset_replace)); + arrayList.add(getResources().getString(R.string.preset_delete)); + dlgBuilder.setCancelable(true); + dlgBuilder + .setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + mPresetButtonIndex = -1; + removeDialog(DIALOG_PRESET_OPTIONS); + } + }); + String[] items = new String[arrayList.size()]; + arrayList.toArray(items); + dlgBuilder.setItems(items, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + if ((mPresetButtonIndex >= 0) + && (mPresetButtonIndex <= MAX_PRESETS)) { + int frequency = mPresetFrequencies[mPresetButtonIndex]; + switch (item) { + case 0: { + // Tune to this station + mPresetButtonIndex = -1; + tuneRadio(frequency); + break; + } + case 1: { + // Replace preset Station with currently tuned + // station + Log.d(LOGTAG, "Replace station - " + frequency + + " with " + mTunedFrequency); + mPresetFrequencies[mPresetButtonIndex] = mTunedFrequency; + mPresetButtonIndex = -1; + setupPresetLayout(); + SavePreferences(); + break; + } + case 2: { + // Delete + mPresetFrequencies[mPresetButtonIndex] = 0; + mPresetButtonIndex = -1; + setupPresetLayout(); + SavePreferences(); + break; + } + default: { + // Should not happen + mPresetButtonIndex = -1; + break; + } + }// switch item + }// if(mPresetButtonStation != null) + removeDialog(DIALOG_PRESET_OPTIONS); + }// onClick + }); + return dlgBuilder.create(); + } + return null; + } + + private Dialog createProgressDialog(int id) { + String msgStr = ""; + String titleStr = ""; + boolean bSearchActive = false; + + if (isSearchActive()) { + msgStr = getString(R.string.msg_weak_searching); + titleStr = getString(R.string.msg_searching_title); + bSearchActive = true; + } + + if (bSearchActive) { + mProgressDialog = new ProgressDialog(FMTransmitterActivity.this); + if (mProgressDialog != null) { + mProgressDialog.setTitle(titleStr); + mProgressDialog.setMessage(msgStr); + mProgressDialog.setIcon(R.drawable.ic_launcher_fm_tx); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.setButton(DialogInterface.BUTTON_POSITIVE, + getText(R.string.button_text_stop), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + cancelSearch(); + } + }); + mProgressDialog + .setOnCancelListener(new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + cancelSearch(); + } + }); + } + + Message msg = new Message(); + msg.what = TIMEOUT_PROGRESS_DLG; + mSearchProgressHandler.sendMessageDelayed(msg, SHOWBUSY_TIMEOUT); + } + return mProgressDialog; + } + + private Dialog createPresetListAutoSelectWarnDlg(int id) { + AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this); + dlgBuilder.setIcon(R.drawable.alert_dialog_icon).setTitle( + R.string.presetlist_autoselect_title); + dlgBuilder.setMessage(getString(R.string.fmtx_autoselect_name)); + + dlgBuilder.setPositiveButton(R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + /* + * Since the presets will be changed, reset the page + * number + */ + initiateSearchList(); + setupPresetLayout(); + SavePreferences(); + removeDialog(DIALOG_PRESET_LIST_AUTO_SET); + } + }); + + dlgBuilder.setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + removeDialog(DIALOG_PRESET_LIST_AUTO_SET); + } + }); + return (dlgBuilder.create()); + } + + private void RestoreDefaults() { + for (int index = 0; index < MAX_PRESETS; index++) { + mPresetFrequencies[index] = FmSharedPreferences.getLowerLimit(); + } + mTunedFrequency = FmSharedPreferences.getLowerLimit(); + SavePreferences(); + } + + private View.OnLongClickListener mFrequencyViewClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View v) { + showDialog(DIALOG_PICK_FREQUENCY); + return true; + } + }; + + private View.OnClickListener mForwardClickListener = new View.OnClickListener() { + public void onClick(View v) { + int frequency = FmSharedPreferences + .getNextTuneFrequency(mTunedFrequency); + Log.d(LOGTAG, "Tune Up: to " + frequency); + tuneRadio(frequency); + } + }; + + private View.OnClickListener mBackClickListener = new View.OnClickListener() { + public void onClick(View v) { + int frequency = FmSharedPreferences + .getPrevTuneFrequency(mTunedFrequency); + Log.d(LOGTAG, "Tune Down: to " + frequency); + tuneRadio(frequency); + } + }; + + private View.OnClickListener mPresetButtonClickListener = new View.OnClickListener() { + public void onClick(View view) { + Integer indexInteger = (Integer) view.getTag(); + int index = indexInteger.intValue(); + if ((index >= 0) && (index <= MAX_PRESETS)) { + Log.d(LOGTAG, "station - " + index); + if(mPresetFrequencies[index] != 0) + { + mTunedFrequency = mPresetFrequencies[index]; + tuneRadio(mTunedFrequency); + view.startAnimation(mAnimation); + } + } + } + }; + + private View.OnLongClickListener mPresetButtonOnLongClickListener = new View.OnLongClickListener() { + public boolean onLongClick(View view) { + Integer indexInteger = (Integer) view.getTag(); + int index = indexInteger.intValue(); + if ((index >= 0) && (index <= MAX_PRESETS)) { + int frequency = mPresetFrequencies[index]; + mPresetButtonIndex = index; + if (frequency > 0) { + showDialog(DIALOG_PRESET_OPTIONS); + } else { + mPresetFrequencies[index] = mTunedFrequency; + view.startAnimation(mAnimation); + setupPresetLayout(); + SavePreferences(); + } + } + return true; + } + }; + + final FrequencyPickerDialog.OnFrequencySetListener mFrequencyChangeListener = new FrequencyPickerDialog.OnFrequencySetListener() { + public void onFrequencySet(FrequencyPicker view, int frequency) { + Log.d(LOGTAG, "mFrequencyChangeListener: onFrequencyChanged to : " + + frequency); + tuneRadio(frequency); + } + }; + + private View.OnClickListener mTurnOnOffClickListener = new View.OnClickListener() { + public void onClick(View v) { + + if (isFmOn()) { + enableRadioHandler.removeCallbacks(enableRadioTask); + disableRadioHandler.removeCallbacks(disableRadioTask); + disableRadioHandler.postDelayed(disableRadioTask, 0); + } else { + enableRadioHandler.removeCallbacks(enableRadioTask); + disableRadioHandler.removeCallbacks(disableRadioTask); + enableRadioHandler.postDelayed(enableRadioTask, 0); + } + setTurnOnOffButtonImage(); + } + }; + + private void setTurnOnOffButtonImage() { + if (isFmOn() == true) { + mOnOffButton.setImageResource(R.drawable.ic_btn_onoff); + } else { + /* Find a icon to indicate off */ + mOnOffButton.setImageResource(R.drawable.ic_btn_onoff); + } + } + + private void enableRadioOnOffButton() { + if(mOnOffButton != null) { + mOnOffButton.setEnabled(true); + mOnOffButton.setClickable(true); + } + } + + private void disableRadioOnOffButton() { + if(mOnOffButton != null) { + mOnOffButton.setEnabled(false); + mOnOffButton.setClickable(false); + } + } + private void setHsPluggedInMsg() { + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(View.VISIBLE); + mRadioTextTV.setText(getString(R.string.msg_headsetpluggedin)); + } + } + private Runnable enableRadioTask = new Runnable() { + public void run() { + enableRadio(); + } + }; + + private Runnable disableRadioTask = new Runnable() { + public void run() { + disableRadio(); + } + }; + + private void enableRadio() { + mIsSearching = false; + disableRadioOnOffButton(); + if (mService != null) { + try { + if(mService.isHeadsetPlugged()) { + setHsPluggedInMsg(); + enableRadioOnOffButton(); + } else if(mService.isCallActive()) { + enableRadioOnOffButton(); + } else if(!mService.fmOn()) { + enableRadioOnOffButton(); + } + } catch (RemoteException e) { + enableRadioOnOffButton(); + e.printStackTrace(); + } + } + + } + + private void disableRadio() { + enableRadioOnOffUI(false); + cancelSearch(); + if (mService != null) { + try { + if(!mService.fmOff()) { + enableRadioOnOffButton(); + } + } catch (RemoteException e) { + enableRadioOnOffButton(); + e.printStackTrace(); + } + } + } + + public static void fmConfigure() { + if (mService != null) { + try { + mService.fmReconfigure(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private void enableRadioOnOffUI() { + boolean bEnable = true; + //decide enable or disable UI based on + //Tx Service status.This is valid only + // when search is not in progress. When + // search is in progress UI should be active + if( false == isSearchActive() ) { + bEnable = isFmOn(); + } + /* Disable if no antenna/headset is available */ + if (!readInternalAntennaAvailable()) { + Log.d(LOGTAG,"finding internal antenna avialable as false"); + bEnable = false; + } + enableRadioOnOffUI(bEnable); + } + + private void enableRadioOnOffUI(boolean bEnable) { + if (bEnable) { + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV + .setOnLongClickListener(mFrequencyViewClickListener); + } + if (mRadioTextScroller != null) { + mRadioTextScroller.startScroll(); + } + } else { + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setOnLongClickListener(null); + } + if (mRadioTextScroller != null) { + mRadioTextScroller.stopScroll(); + } + } + + if (mForwardButton != null) { + mForwardButton.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mBackButton != null) { + mBackButton.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mTransmitStaticMsgTV != null) { + mTransmitStaticMsgTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(((bEnable == true) ? View.VISIBLE + : View.INVISIBLE)); + } + + try { + if( null != mService) { + if ( mService.isHeadsetPlugged()) { + Log.d(LOGTAG,"headset plugged in"); + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(View.VISIBLE); + mRadioTextTV.setText(getString(R.string.msg_headsetpluggedin)); + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(false); + } + } else if (mService.isCallActive()) { + Log.d(LOGTAG,"call active"); + if (mRadioTextTV != null) { + mRadioTextTV.setVisibility(View.VISIBLE); + mRadioTextTV.setText(getString(R.string.msg_callactive)); + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(false); + } + } else { + if (mRadioTextTV != null) { + mRadioTextTV.setText(""); + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(true); + } + } + } else { + Log.d(LOGTAG,"Service null"); + if (mRadioTextTV != null) { + mRadioTextTV.setText(""); + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(true); + } + } + } catch (RemoteException e) { + e.printStackTrace(); + } + + for (int nButton = 0; nButton < MAX_PRESETS; nButton++) { + if (mPresetButtons[nButton] != null) { + mPresetButtons[nButton].setEnabled(bEnable); + } + } + } + + private void updateSearchProgress() { + boolean searchActive = isSearchActive(); + if (searchActive) { + synchronized (this) { + if (mProgressDialog == null) { + showDialog(DIALOG_PROGRESS_PROGRESS); + } else { + Message msg = new Message(); + msg.what = UPDATE_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + } + } else { + Message msg = new Message(); + msg.what = END_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + } + + private void resetSearchProgress() { + Message msg = new Message(); + msg.what = END_PROGRESS_DLG; + mSearchProgressHandler.sendMessage(msg); + } + + private void setupPresetLayout() { + /* + * For every station, save the station as a tag and update the display + * on the preset Button. + */ + for (int buttonIndex = 0; (buttonIndex < MAX_PRESETS); buttonIndex++) { + if (mPresetButtons[buttonIndex] != null) { + String display = ""; + int frequency = mPresetFrequencies[buttonIndex]; + if (frequency != 0) { + display = PresetStation.getFrequencyString(frequency); + } + mPresetButtons[buttonIndex].setText(display); + mPresetButtons[buttonIndex].setTag(new Integer(buttonIndex)); + } + } + } + + private void updateStationInfoToUI() { + mTuneStationFrequencyTV.setText(PresetStation.getFrequencyString(mTunedFrequency)); + mRadioTextTV.setText(""); + setupPresetLayout(); + } + + private boolean isFmOn() { + boolean bOn = false; + if (mService != null) { + try { + bOn = mService.isFmOn(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + return (bOn); + } + + private boolean isSearchActive() { + return (mIsSearching); + } + + public static int getCurrentTunedFrequency() { + return mTunedFrequency; + } + + private void cancelSearch() { + synchronized (this) { + if (mService != null) { + try { + if (mIsSearching == true) { + if (true == mService.cancelSearch()) { + } + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + updateSearchProgress(); + } + + /** get Weakest Stations */ + private void initiateSearchList() { + synchronized (this) { + mIsSearching = true; + if (mService != null) { + try { + mSearchingResultStatus = false; + mIsSearching = mService.searchWeakStationList(MAX_PRESETS); + } catch (RemoteException e) { + e.printStackTrace(); + } + updateSearchProgress(); + } + } + } + + /** get Internal Antenna Available mode Stations */ + private boolean readInternalAntennaAvailable() { + mInternalAntennaAvailable = false; + if (mService != null) { + try { + mInternalAntennaAvailable = mService + .isInternalAntennaAvailable(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + Log.e(LOGTAG, "readInternalAntennaAvailable: internalAntenna : " + + mInternalAntennaAvailable); + return mInternalAntennaAvailable; + } + + private static final int UPDATE_PROGRESS_DLG = 1; + private static final int END_PROGRESS_DLG = 2; + private static final int TIMEOUT_PROGRESS_DLG = 3; + private static final int SHOWBUSY_TIMEOUT = 300000; + private Handler mSearchProgressHandler = new Handler() { + public void handleMessage(Message msg) { + if (msg.what == UPDATE_PROGRESS_DLG) { + // Log.d(LOGTAG, "mTimeoutHandler: UPDATE_PROGRESS_DLG"); + if (mProgressDialog != null) { + mTuneStationFrequencyTV.setText(PresetStation.getFrequencyString(mTunedFrequency)); + String titleStr = getString( R.string.msg_search_title, + PresetStation.getFrequencyString(mTunedFrequency)); + + mProgressDialog.setTitle(titleStr); + } + } else if (msg.what == END_PROGRESS_DLG) { + // Log.d(LOGTAG, "mTimeoutHandler: END_PROGRESS_DLG"); + mSearchProgressHandler.removeMessages(END_PROGRESS_DLG); + mSearchProgressHandler.removeMessages(UPDATE_PROGRESS_DLG); + mSearchProgressHandler.removeMessages(TIMEOUT_PROGRESS_DLG); + removeDialog(DIALOG_PROGRESS_PROGRESS); + mProgressDialog = null; + } else if (msg.what == TIMEOUT_PROGRESS_DLG) { + cancelSearch(); + } + } + }; + + private void tuneRadio(int frequency) { + if (mService != null) { + try { + // Set Tune Frequency + mService.tune(frequency); + updateStationInfoToUI(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private void resetFMStationInfoUI() { + mRadioTextTV.setText(""); + mRadioTextScroller.stopScroll(); + updateStationInfoToUI(); + } + + + final Runnable mUpdateStationInfo = new Runnable() { + public void run() { + updateSearchProgress(); + resetFMStationInfoUI(); + } + }; + + final Runnable mSearchListComplete = new Runnable() { + public void run() { + Log.d(LOGTAG, "mSearchListComplete: "); + mIsSearching = false; + + /* Now get the list */ + if (mService != null) { + try { + if(mSearchingResultStatus) { + int[] searchList = mService.getSearchList(); + if (searchList != null) { + for (int station = 0; (station < searchList.length) + && (station < MAX_PRESETS); station++) { + Log.d(LOGTAG, "mSearchListComplete: [" + station + + "] = " + searchList[station]); + mPresetFrequencies[station] = searchList[station]; + } + } + } + /* Restart FM into Transmit mode */ + if(!mService.isHeadsetPlugged()) { + mService.fmRestart(); + /* Tune to last tuned frequency */ + tuneRadio(mTunedFrequency); + updateSearchProgress(); + resetFMStationInfoUI(); + setupPresetLayout(); + SavePreferences(); + } else { + updateSearchProgress(); + enableRadioHandler.removeCallbacks(enableRadioTask); + disableRadioHandler.removeCallbacks(disableRadioTask); + disableRadioHandler.postDelayed(disableRadioTask, 0); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + }; + + + + final Runnable mUpdateRadioText = new Runnable() { + public void run() { + String str = ""; + if ((mService != null) && isFmOn()) { + try { + /* Get Radio Text and update the display */ + str = mService.getRadioText(); + if(null != mMetaData) { + Log.d(LOGTAG,"meta data is "+mMetaData); + str = str.concat(mMetaData); + } else { + str = str.concat("..."); + } + /* Update only if all the characters are printable */ + if (TextUtils.isGraphic(str)) + { + Log.d(LOGTAG, "mUpdateRadioText: Updatable string: [" + + str + "]"); + mRadioTextTV.setText(str); + } + /* Rest the string to empty */ + else if (TextUtils.isEmpty(str)) { + mRadioTextTV.setText(""); + } else { + Log.d(LOGTAG, "RDS has non printable stuff"); + mRadioTextTV.setText(""); + } + + mRadioTextScroller.startScroll(); + String szRTStr = getString(R.string.transmit_msg_string); + mPSData = mService.getPSData(); + if(null != mPSData ) { + szRTStr = mPSData.concat("\n").concat(szRTStr); + } + else + Log.d(LOGTAG, "mPSData is NULL"); + mTransmitStaticMsgTV.setText(szRTStr); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + }; + + private void DebugToasts(String str, int duration) { + Toast.makeText(this, str, duration).show(); + } + + /** + * This Handler will scroll the text view. On startScroll, the scrolling + * starts after SCROLLER_START_DELAY_MS The Text View is scrolled left one + * character after every SCROLLER_UPDATE_DELAY_MS When the entire text is + * scrolled, the scrolling will restart after SCROLLER_RESTART_DELAY_MS + */ + private static final class ScrollerText extends Handler { + + private static final byte SCROLLER_STOPPED = 0x51; + private static final byte SCROLLER_STARTING = 0x52; + private static final byte SCROLLER_RUNNING = 0x53; + + private static final int SCROLLER_MSG_START = 0xF1; + private static final int SCROLLER_MSG_TICK = 0xF2; + private static final int SCROLLER_MSG_RESTART = 0xF3; + + private static final int SCROLLER_START_DELAY_MS = 1000; + private static final int SCROLLER_RESTART_DELAY_MS = 3000; + private static final int SCROLLER_UPDATE_DELAY_MS = 200; + + private final WeakReference<TextView> mView; + + private byte mStatus = SCROLLER_STOPPED; + String mOriginalString; + int mStringlength = 0; + int mIteration = 0; + + ScrollerText(TextView v) { + mView = new WeakReference<TextView>(v); + } + + /** + * Scrolling Message Handler + */ + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case SCROLLER_MSG_START: + mStatus = SCROLLER_RUNNING; + updateText(); + break; + case SCROLLER_MSG_TICK: + updateText(); + break; + case SCROLLER_MSG_RESTART: + if (mStatus == SCROLLER_RUNNING) { + startScroll(); + } + break; + } + } + + /** + * Moves the text left by one character and posts a delayed message for + * next update after SCROLLER_UPDATE_DELAY_MS. If the entire string is + * scrolled, then it displays the entire string and waits for + * SCROLLER_RESTART_DELAY_MS for scrolling restart + */ + void updateText() { + if (mStatus != SCROLLER_RUNNING) { + return; + } + + removeMessages(SCROLLER_MSG_TICK); + + final TextView textView = mView.get(); + if (textView != null) { + String szStr2 = ""; + if (mStringlength > 0) { + mIteration++; + if (mIteration >= mStringlength) { + mIteration = 0; + sendEmptyMessageDelayed(SCROLLER_MSG_RESTART, + SCROLLER_RESTART_DELAY_MS); + } else { + sendEmptyMessageDelayed(SCROLLER_MSG_TICK, + SCROLLER_UPDATE_DELAY_MS); + } + // String szStr1 = mOriginalString.substring(0, mTick); + szStr2 = mOriginalString.substring(mIteration); + } + // textView.setText(szStr2+" "+szStr1); + textView.setText(szStr2); + } + } + + /** + * Stops the scrolling The textView will be set to the original string. + */ + void stopScroll() { + mStatus = SCROLLER_STOPPED; + removeMessages(SCROLLER_MSG_TICK); + removeMessages(SCROLLER_MSG_RESTART); + removeMessages(SCROLLER_MSG_START); + resetScroll(); + } + + /** + * Resets the scroll to display the original string. + */ + private void resetScroll() { + mIteration = 0; + final TextView textView = mView.get(); + if (textView != null) { + textView.setText(mOriginalString); + } + } + + /** + * Starts the Scrolling of the TextView after a delay of + * SCROLLER_START_DELAY_MS Starts only if Length > 0 + */ + void startScroll() { + final TextView textView = mView.get(); + if (textView != null) { + mOriginalString = (String) textView.getText(); + mStringlength = mOriginalString.length(); + if (mStringlength > 0) { + mStatus = SCROLLER_STARTING; + sendEmptyMessageDelayed(SCROLLER_MSG_START, + SCROLLER_START_DELAY_MS); + } + } + } + } + + public static IFMTransmitterService sService = null; + private static HashMap<Context, ServiceBinder> sConnectionMap = new HashMap<Context, ServiceBinder>(); + + public static boolean bindToService(Context context) { + Log.e(LOGTAG, "bindToService: Context"); + return bindToService(context, null); + } + + public static boolean bindToService(Context context, + ServiceConnection callback) { + Log.e(LOGTAG, "bindToService: Context with serviceconnection callback"); + context.startService(new Intent(context, FMTransmitterService.class)); + ServiceBinder sb = new ServiceBinder(callback); + sConnectionMap.put(context, sb); + return context.bindService((new Intent()).setClass(context, + FMTransmitterService.class), sb, 0); + } + + public static void unbindFromService(Context context) { + ServiceBinder sb = (ServiceBinder) sConnectionMap.remove(context); + Log.e(LOGTAG, "unbindFromService: Context"); + if (sb == null) { + Log.e(LOGTAG, "Trying to unbind for unknown Context"); + return; + } + context.unbindService(sb); + if (sConnectionMap.isEmpty()) { + // presumably there is nobody interested in the service at this + // point, + // so don't hang on to the ServiceConnection + sService = null; + } + } + + private static class ServiceBinder implements ServiceConnection { + ServiceConnection mCallback; + + ServiceBinder(ServiceConnection callback) { + mCallback = callback; + } + + public void onServiceConnected(ComponentName className, + android.os.IBinder service) { + sService = IFMTransmitterService.Stub.asInterface(service); + if (mCallback != null) { + Log.e(LOGTAG, "onServiceConnected: mCallback"); + mCallback.onServiceConnected(className, service); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (mCallback != null) { + mCallback.onServiceDisconnected(className); + } + sService = null; + } + } + + private ServiceConnection osc = new ServiceConnection() { + public void onServiceConnected(ComponentName classname, IBinder obj) { + mService = IFMTransmitterService.Stub.asInterface(obj); + Log.e(LOGTAG, "ServiceConnection: onServiceConnected: "); + if (mService != null) { + try { + mService.registerCallbacks(mServiceCallbacks); + if(false == mService.isHeadsetPlugged()) { + Log.e(LOGTAG, "return for isHeadsetPlugged is false"); + if (SavedDataAndState == null) { + enableRadioHandler.removeCallbacks(enableRadioTask); + disableRadioHandler.removeCallbacks(disableRadioTask); + enableRadioHandler.postDelayed(enableRadioTask, 0); + } else if (SavedDataAndState.onOrOff) { + enableRadioOnOffUI(true); + } else { + enableRadioOnOffUI(false); + } + } else { + enableRadioOnOffUI(false); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + return; + } else { + Log.e(LOGTAG, + "IFMTransmitterService onServiceConnected failed"); + } + // startPlayback(); + // Service is dead or not playing anything. If we got here as part + // of a "play this file" Intent, exit. Otherwise go to the Music + // app start screen. + if (getIntent().getData() == null) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setClass(FMTransmitterActivity.this, + FMTransmitterActivity.class); + startActivity(intent); + } + finish(); + } + + public void onServiceDisconnected(ComponentName classname) { + } + }; + + private IFMTransmitterServiceCallbacks.Stub mServiceCallbacks = new IFMTransmitterServiceCallbacks.Stub() { + + public void onDisabled() throws RemoteException { + mHandler.post(mRadioStateUpdated); + } + + public void onRadioReset() throws RemoteException { + mHandler.post(mRadioReset); + } + + public void onEnabled(boolean status) throws RemoteException { + mHandler.post(mRadioStateUpdated); + } + + public void onRadioTextChanged() throws RemoteException { + } + + public void onSearchListComplete(boolean status) throws RemoteException { + mIsSearching = false; + mSearchingResultStatus = status; + mHandler.post(mSearchListComplete); + } + + public void onTuneStatusChanged(int frequency) throws RemoteException { + mTunedFrequency = frequency; + Log.d(LOGTAG, "onTuneStatusChanged: Frequency : " + mTunedFrequency); + FmSharedPreferences.setTunedFrequency(mTunedFrequency); + SavePreferences(); + mHandler.post(mUpdateStationInfo); + } + public void onReconfigured() throws RemoteException { + RestoreDefaults(); + } + public void onMetaDataChanged(String metaStr) throws RemoteException { + Log.d(LOGTAG,"meta data is "+metaStr); + mMetaData = new String (metaStr); + mHandler.post(mUpdateRadioText); + } + public void onPSInfoSent(String psStr ) throws RemoteException { + Log.d(LOGTAG,"PS String data is "+psStr); + mPSData = new String (psStr); + mHandler.post(mUpdateRadioText); + } + }; + final Runnable mRadioStateUpdated = new Runnable() { + public void run() { + enableRadioOnOffButton(); + /* Update UI to FM On State */ + if(isFmOn()) { + enableRadioOnOffUI(true); + /* Tune to the last tuned frequency */ + LoadPreferences(); + tuneRadio(mTunedFrequency); + } else { + /* Save the existing frequency */ + FmSharedPreferences.setTunedFrequency(mTunedFrequency); + SavePreferences(); + removeDialog(DIALOG_PRESET_LIST_AUTO_SET); + enableRadioOnOffUI(false); + } + + } + }; + final Runnable mRadioReset = new Runnable() { + public void run() { + /* Save the existing frequency */ + resetSearchProgress(); + FmSharedPreferences.setTunedFrequency(mTunedFrequency); + SavePreferences(); + enableRadioOnOffUI(false); + } + }; +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMTransmitterConfigReceiver.java b/fmapp/src/com/codeaurora/fmradio/FMTransmitterConfigReceiver.java new file mode 100644 index 0000000..2abd3ca --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMTransmitterConfigReceiver.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import android.content.Intent; +import android.content.BroadcastReceiver; +import android.content.pm.PackageManager; +import android.content.Context; +import android.content.ComponentName; +import android.util.Log; +import android.os.SystemProperties; +import java.io.FileReader; +import java.lang.String; + + +public class FMTransmitterConfigReceiver extends BroadcastReceiver { + + private static FileReader socinfo_fd; + private static char[] socinfo = new char[20]; + private static String build_id = "1"; + + private static final String TAG = "FMFolderConfigReceiver"; + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d(TAG, "Received intent: " + action); + if((action != null) && action.equals("android.intent.action.BOOT_COMPLETED")) { + Log.d(TAG, "boot complete intent received"); + boolean isFmTransmitterSupported = SystemProperties.getBoolean("ro.fm.transmitter",true); + + if ("msm7630_surf".equals(SystemProperties.get("ro.board.platform"))) { + Log.d(TAG,"this is msm7630_surf"); + try { + socinfo_fd = new FileReader("/sys/devices/system/soc/soc0/build_id"); + socinfo_fd.read(socinfo,0,20); + socinfo_fd.close(); + } catch(Exception e) { + Log.e(TAG,"Exception in FileReader"); + } + Log.d(TAG, "socinfo=" +socinfo); + build_id = new String(socinfo,17,1); + Log.d(TAG, "build_id=" +build_id); + } + if ((!isFmTransmitterSupported) || (build_id.equals("0"))) { + PackageManager pManager = context.getPackageManager(); + if (pManager != null) { + Log.d(TAG, "disableing the FM Transmitter"); + ComponentName fmTransmitter = new ComponentName("com.codeaurora.fmradio", "com.codeaurora.fmradio.FMTransmitterActivity"); + pManager.setComponentEnabledSetting(fmTransmitter, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + } + } + } +} diff --git a/fmapp/src/com/codeaurora/fmradio/FMTransmitterService.java b/fmapp/src/com/codeaurora/fmradio/FMTransmitterService.java new file mode 100644 index 0000000..df1de6d --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FMTransmitterService.java @@ -0,0 +1,1192 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import java.lang.ref.WeakReference; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.os.SystemProperties; +import android.os.PowerManager.WakeLock; +import android.os.RemoteException; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.widget.RemoteViews; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.BroadcastReceiver; + +import qcom.fmradio.FmReceiver; +import qcom.fmradio.FmTransmitter; +import qcom.fmradio.FmRxEvCallbacksAdaptor; +import qcom.fmradio.FmTransmitterCallbacksAdaptor; +import qcom.fmradio.FmRxRdsData; +import qcom.fmradio.FmConfig; +import com.codeaurora.utils.A2dpDeviceStatus; + +/** + * Provides "background" FM Radio (that uses the hardware) capabilities, + * allowing the user to switch between activities without stopping playback. + */ +public class FMTransmitterService extends Service +{ + private static final int FMTRANSMITTERSERVICE_STATUS = 102; + private static final int FM_TX_PROGRAM_TYPE = 0; + private static final int FM_TX_PROGRAM_ID = 0x1234; + private static final int FM_TX_PS_REPEAT_COUNT = 1; + + private static final String FMRADIO_DEVICE_FD_STRING = "/dev/radio0"; + private static final String LOGTAG = "FMTxService";//FMRadio.LOGTAG; + private static final String QFM_STRING ="QFMRADIO"; + + private static FmReceiver mReceiver; + private static FmTransmitter mTransmitter; + private int mTunedFrequency = 0; + + private static FmSharedPreferences mPrefs; + private IFMTransmitterServiceCallbacks mCallbacks; + private WakeLock mWakeLock; + private int mServiceStartId = -1; + private boolean mServiceInUse = false; + private boolean mMuted = false; + private boolean mResumeAfterCall = false; + + private boolean mFMOn = false; + private int mFMSearchStations = 0; + + private FmRxRdsData mFMRxRDSData=null; + final Handler mHandler = new Handler(); + private BroadcastReceiver mHeadsetReceiver = null; + boolean mHeadsetPlugged = false; + // Track A2dp Device status changes + private A2dpDeviceStatus mA2dpDeviceState = null; + // interval after which we stop the service when idle + private static final int IDLE_DELAY = 60000; + + private static String RText = " "; + + public FMTransmitterService() { + } + + @Override + public void onCreate() { + super.onCreate(); + + mCallbacks = null; + mPrefs = new FmSharedPreferences(this); + + PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName()); + mWakeLock.setReferenceCounted(false); + + // If the service was idle, but got killed before it stopped itself, the + // system will relaunch it. Make sure it gets stopped again in that case. + + TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + + //register for A2DP utility interface + mA2dpDeviceState = new A2dpDeviceStatus(getApplicationContext()); + Message msg = mDelayedStopHandler.obtainMessage(); + mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY); + registerHeadsetListener(); + } + + @Override + public void onDestroy() { + Log.d(LOGTAG, "onDestroy"); + if (isFmOn()) + { + Log.e(LOGTAG, "Service being destroyed while still playing."); + } + + // make sure there aren't any other messages coming + mDelayedStopHandler.removeCallbacksAndMessages(null); +/* + if (mMetaStatusListener != null) { + unregisterReceiver(mMetaStatusListener); + mMetaStatusListener = null; + } */ + + /* Unregister the headset Broadcase receiver */ + if (mHeadsetReceiver != null) { + unregisterReceiver(mHeadsetReceiver); + mHeadsetReceiver = null; + } + /* Since the service is closing, disable the receiver */ + fmOff(); + + TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + tmgr.listen(mPhoneStateListener, 0); + + mWakeLock.release(); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + mDelayedStopHandler.removeCallbacksAndMessages(null); + mServiceInUse = true; + /* Application/UI is attached, so get out of lower power mode */ + setLowPowerMode(false); + Log.d(LOGTAG, "onBind"); + return mBinder; + } + + @Override + public void onRebind(Intent intent) { + mDelayedStopHandler.removeCallbacksAndMessages(null); + mServiceInUse = true; + /* Application/UI is attached, so get out of lower power mode */ + setLowPowerMode(false); + Log.d(LOGTAG, "onRebind"); + } + + /* + *Listen to Meta data events + */ +/* private BroadcastReceiver mMetaStatusListener = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals("com.android.music.metachanged")) { + // redraw the artist/title info and + // set new max for progress bar + Log.d(LOGTAG, " MetaData changed\n "); + Log.d(LOGTAG, "Service: artist: "+ intent.getStringExtra("artist")); + Log.d(LOGTAG, " Service:Album: "+ intent.getStringExtra("album")); + Log.d(LOGTAG, " Service:Track: "+ intent.getStringExtra("track")); + + //Set if PI and PTY + //as desired + if( mTransmitter != null ){ + RText = intent.getStringExtra("album") +":" + intent.getStringExtra("track") +":" + + intent.getStringExtra("artist"); + Log.d(LOGTAG,"RT string size is "+RText.length()); + mTransmitter.startRTInfo(RText, FM_TX_PROGRAM_TYPE, FM_TX_PROGRAM_ID ); + } + try { + if( mCallbacks != null ) { + mCallbacks.onMetaDataChanged(RText); + } else { + Log.d(LOGTAG, "callback is not there"); + } + } catch (RemoteException ex){ + ex.printStackTrace(); + } + } + else if ( action.equals("com.android.music.playstatechanged")){ + // current "command" extra string is not supported + // but found in open source this info will be filled + // for this intent, in that case long pause can be handled + // for turning of FM Tx. Yet to be implemented + String cmd = intent.getStringExtra("command"); + Log.d(LOGTAG, "playstatechanged event received"); + } + else if (action.equals("com.android.music.playbackcomplete")){ + // need to implement timer logic to turn off FM. + Log.d(LOGTAG,"playbackcomplete event received"); + } + + } + }; */ + @Override + public void onStart(Intent intent, int startId) { + Log.d(LOGTAG, "onStart"); + mServiceStartId = startId; + mDelayedStopHandler.removeCallbacksAndMessages(null); + + // make sure the service will shut down on its own if it was + // just started but not bound to and nothing is playing + mDelayedStopHandler.removeCallbacksAndMessages(null); + Message msg = mDelayedStopHandler.obtainMessage(); + mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY); +/* + IntentFilter f = new IntentFilter(); + f.addAction("com.android.music.metachanged"); + f.addAction("com.android.music.playstatechanged"); + f.addAction("com.android.music.playbackcomplete"); + registerReceiver(mMetaStatusListener, f); */ + + } + + @Override + public boolean onUnbind(Intent intent) { + mServiceInUse = false; + Log.d(LOGTAG, "onUnbind"); + + /* Application/UI is not attached, so go into lower power mode */ + unregisterCallbacks(); + setLowPowerMode(true); + if (isFmOn()) + { + // something is currently playing, or will be playing once + // an in-progress call ends, so don't stop the service now. + return true; + } + + stopSelf(mServiceStartId); + return true; + } + + /* Handle Phone Call + FM Concurrency */ + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + Log.d(LOGTAG, "onCallStateChanged: State - " + state ); + Log.d(LOGTAG, "onCallStateChanged: incomingNumber - " + incomingNumber ); + fmActionOnCallState(state ); + } + + // NEED TO CHECK ACTION TO BE TAKEN ON DATA ACTIVITY + }; + private void fmActionOnCallState( int state ) { + //if Call Status is non IDLE we need to Mute FM as well stop recording if + //any. Similarly once call is ended FM should be unmuted. + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + if((TelephonyManager.CALL_STATE_OFFHOOK == state)|| + (TelephonyManager.CALL_STATE_RINGING == state)) { + if (state == TelephonyManager.CALL_STATE_RINGING) { + int ringvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING); + if (ringvolume == 0) { + return; + } + } + if( mFMOn == true) { + Log.d(LOGTAG, "posting for call state change"); + mHandler.post(mChangeFMTxState); + mResumeAfterCall = true; + } + } + else if (state == TelephonyManager.CALL_STATE_IDLE) { + // start playing again + if (mResumeAfterCall) + { + Log.d(LOGTAG, "posting for call state change"); + mHandler.post(mChangeFMTxState); + mResumeAfterCall = false; + } + }//idle + } + + private Handler mDelayedStopHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // Check again to make sure nothing is playing right now + if (isFmOn() || mServiceInUse) + { + return; + } + Log.d(LOGTAG, "mDelayedStopHandler: stopSelf"); + stopSelf(mServiceStartId); + } + }; + + /* Show the FM Notification */ + public void startNotification() { + RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar); + views.setImageViewResource(R.id.icon, R.drawable.ic_status_fm_tx); + if (isFmOn()) + { + views.setTextViewText(R.id.frequency, getTunedFrequencyString()); + } else + { + views.setTextViewText(R.id.frequency, ""); + } + + Notification status = new Notification(); + status.contentView = views; + status.flags |= Notification.FLAG_ONGOING_EVENT; + status.icon = R.drawable.ic_status_fm_tx; + status.contentIntent = PendingIntent.getActivity(this, 0, + new Intent("com.codeaurora.fmradio.FMTRANSMITTER_ACTIVITY"), 0); + startForeground(FMTRANSMITTERSERVICE_STATUS, status); + //NotificationManager nm = (NotificationManager) + // getSystemService(Context.NOTIFICATION_SERVICE); + //nm.notify(FMTRANSMITTERSERVICE_STATUS, status); + //setForeground(true); + mFMOn = true; + } + + private void stop() { + gotoIdleState(); + mFMOn = false; + } + + private void gotoIdleState() { + mDelayedStopHandler.removeCallbacksAndMessages(null); + Message msg = mDelayedStopHandler.obtainMessage(); + mDelayedStopHandler.sendMessageDelayed(msg, IDLE_DELAY); + //NotificationManager nm = + //(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + //nm.cancel(FMTRANSMITTERSERVICE_STATUS); + //setForeground(false); + stopForeground(true); + } + + /* + * By making this a static class with a WeakReference to the Service, we + * ensure that the Service can be GCd even when the system process still + * has a remote reference to the stub. + */ + static class ServiceStub extends IFMTransmitterService.Stub + { + WeakReference<FMTransmitterService> mService; + + ServiceStub(FMTransmitterService service) + { + mService = new WeakReference<FMTransmitterService>(service); + } + + public boolean fmOn() throws RemoteException + { + return(mService.get().fmOn()); + } + + public boolean fmOff() throws RemoteException + { + return(mService.get().fmOff()); + } + public boolean fmRadioReset() throws RemoteException + { + return(mService.get().fmRadioReset()); + } + public boolean fmRestart() throws RemoteException + { + return(mService.get().fmRestart()); + } + + public boolean isFmOn() + { + return(mService.get().isFmOn()); + } + public boolean fmReconfigure() + { + return(mService.get().fmReconfigure()); + } + + public void registerCallbacks(IFMTransmitterServiceCallbacks cb) + throws RemoteException { + mService.get().registerCallbacks(cb); + } + + public boolean searchWeakStationList(int numStations) + throws RemoteException { + return(mService.get().searchWeakStationList(numStations)); + } + + public void unregisterCallbacks() throws RemoteException + { + mService.get().unregisterCallbacks(); + } + + public boolean tune(int frequency) + { + return(mService.get().tune(frequency)); + } + + public boolean cancelSearch() + { + return(mService.get().cancelSearch()); + } + + public String getRadioText() + { + return(mService.get().getRadioText()); + } + + public int[] getSearchList() + { + return(mService.get().getSearchList()); + } + + public boolean isInternalAntennaAvailable() + { + return(mService.get().isInternalAntennaAvailable()); + } + public boolean isHeadsetPlugged() + { + return(mService.get().isHeadsetPlugged()); + } + public boolean isCallActive() + { + return(mService.get().isCallActive()); + } + public String getPSData() + { + return(mService.get().getPSData()); + } + } + + private final IBinder mBinder = new ServiceStub(this); + /* + * Turn ON FM: Powers up FM hardware, and initializes the FM module + * . + * @return true if fm Enable api was invoked successfully, false if the api failed. + */ + private boolean fmOn() { + boolean bStatus=false; + + Log.d(LOGTAG, "fmOn"); + mWakeLock.acquire(10*1000); + if (mTransmitter == null) { + try { + mTransmitter = new FmTransmitter(FMRADIO_DEVICE_FD_STRING, transmitCallbacks); + Log.d(LOGTAG, "new transmitter created"); + } catch (InstantiationException e) { + throw new RuntimeException("FmTx service not available!"); + } + } + if (mTransmitter != null) + { + if (isFmOn()) + { + /* FM Is already on,*/ + bStatus = true; + try { + if(mCallbacks != null) { + mCallbacks.onEnabled(true); + } + } catch(RemoteException e) { + e.printStackTrace(); + } + Log.d(LOGTAG, "mTransmitter is enabled"); + } + else + { + // This sets up the FM radio device + FmConfig config = FmSharedPreferences.getFMConfiguration(); + Log.d(LOGTAG, "fmOn: RadioBand :"+ config.getRadioBand()); + Log.d(LOGTAG, "fmOn: Emphasis :"+ config.getEmphasis()); + Log.d(LOGTAG, "fmOn: ChSpacing :"+ config.getChSpacing()); + Log.d(LOGTAG, "fmOn: RdsStd :"+ config.getRdsStd()); + Log.d(LOGTAG, "fmOn: LowerLimit :"+ config.getLowerLimit()); + Log.d(LOGTAG, "fmOn: UpperLimit :"+ config.getUpperLimit()); + + boolean bFmRxEnabled = false; + + if (!mA2dpDeviceState.isDeviceAvailable()) { + bStatus = mTransmitter.enable(config); + } + if( false == bStatus ) { + Log.e(LOGTAG,"FM Enable failed"); + return bStatus; + } + bStatus = mTransmitter.setTxPowerLevel(FmTransmitter.FM_TX_PWR_LEVEL_7); + + if( false == bStatus ) { + Log.e(LOGTAG,"FM setPowerLevel failed"); + return bStatus; + } + + Log.e(LOGTAG, "FMTx is on: Requesting to start FM Tx"); + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_TX, + AudioSystem.DEVICE_STATE_AVAILABLE, ""); + + } + + if(true == bStatus ) + { + bStatus = mTransmitter.setRdsOn(); + if( true != bStatus ) { + Log.d(LOGTAG, "FMTx setRdsOn failed"); + } else { + if(false == mTransmitter.getInternalAntenna()) { + Log.d(LOGTAG, "Setting internal antenna explicitly"); + mTransmitter.setInternalAntenna(true); + } + startNotification(); + } + } + else + { + stop(); + } + } + return(bStatus); + } + + /* + * Turn OFF FM Operations: This disables all the current FM operations . + */ + private void fmOperationsOff() { + + Log.d(LOGTAG, "fmOperationsOff" ); + + Log.e(LOGTAG, "FMTx is off: requesting to stop FM Tx"); + AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_TX, + AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); + } + /* + * Turn OFF FM: Disable the FM Host and hardware . + * . + * @return true if fm Disable api was invoked successfully, false if the api failed. + */ + private boolean fmOff() { + boolean bStatus=false; + Log.d(LOGTAG, "fmOff"); + + fmOperationsOff(); + + // This will disable the FM radio device + if (mTransmitter != null) + { + bStatus = mTransmitter.disable(); + mTransmitter = null; + } + /* Disable Receiver */ + if (mReceiver != null) + { + bStatus = mReceiver.disable(); + mReceiver = null; + } + RText = " "; + stop(); + return(bStatus); + } + /* + * Turn OFF FM: Disable the FM Host when hardware resets asynchronously . + * . + * @return true if fm Reset api was invoked successfully, false if the api failed . + */ + private boolean fmRadioReset() { + boolean bStatus=false; + Log.d(LOGTAG, "fmRadioReset"); + fmOperationsOff(); + + // This will disable the FM radio device + if (mTransmitter != null) + { + bStatus = mTransmitter.reset(); + mTransmitter = null; + } + /* Disable Receiver */ + if (mReceiver != null) + { + bStatus = mReceiver.reset(); + mReceiver = null; + } + stop(); + return(bStatus); + } + + /* + * Restart FM Transmitter: Disables FM receiver mode or transmitter is already active + * and Powers up FM hardware, and initializes the FM module + * + * @return true if fm Enable api was invoked successfully, false if the api failed. + */ + + private boolean fmRestart() { + boolean bStatus=false; + Log.d(LOGTAG, "fmRestart"); + + /* First Disable Transmitter, if enabled */ + if (mTransmitter != null) + { + bStatus = mTransmitter.disable(); + mTransmitter = null; + mFMOn = false; + } + + /* Disable Receiver */ + if (mReceiver != null) + { + bStatus = mReceiver.disable(); + mReceiver = null; + } + try { + Thread.sleep(100);//sleep needed for SoC to switch mode + } + catch ( Exception ex ) { + Log.d( LOGTAG, "RunningThread InterruptedException"); + } + bStatus = fmOn(); + return(bStatus); + } + + /* Returns whether FM hardware is ON. + * + * @return true if FM was tuned, searching. (at the end of + * the search FM goes back to tuned). + * + */ + public boolean isFmOn() { + return mFMOn; + } + + /* + * ReConfigure the FM Setup parameters + * - Band + * - Channel Spacing (50/100/200 KHz) + * - Emphasis (50/75) + * - Frequency limits + * - RDS/RBDS standard + * + * @return true if configure api was invoked successfully, false if the api failed. + */ + public boolean fmReconfigure() { + boolean bStatus=false; + Log.d(LOGTAG, "fmReconfigure"); + if (mTransmitter != null) + { + // This sets up the FM radio device + FmConfig config = FmSharedPreferences.getFMConfiguration(); + Log.d(LOGTAG, "RadioBand :"+ config.getRadioBand()); + Log.d(LOGTAG, "Emphasis :"+ config.getEmphasis()); + Log.d(LOGTAG, "ChSpacing :"+ config.getChSpacing()); + Log.d(LOGTAG, "RdsStd :"+ config.getRdsStd()); + Log.d(LOGTAG, "LowerLimit :"+ config.getLowerLimit()); + Log.d(LOGTAG, "UpperLimit :"+ config.getUpperLimit()); + bStatus = mTransmitter.configure(config); + } + if (mCallbacks != null) + { + try + { + mCallbacks.onReconfigured(); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return(bStatus); + } + + /* + * Register UI/Activity Callbacks + */ + public void registerCallbacks(IFMTransmitterServiceCallbacks cb) + { + mCallbacks = cb; + } + + /* + * unRegister UI/Activity Callbacks + */ + public void unregisterCallbacks() + { + mCallbacks=null; + } + + /* Tunes to the specified frequency + * + * @return true if Tune command was invoked successfully, false if not muted. + * Note: Callback FmRxEvRadioTuneStatus will be called when the tune + * is complete + */ + public boolean tune(int frequency) { + boolean bCommandSent=false; + double doubleFrequency = frequency/1000.00; + + Log.d(LOGTAG, "tune: " + doubleFrequency); + if (mTransmitter != null) + { + mTransmitter.setStation(frequency); + mTunedFrequency = frequency; + bCommandSent = true; + } + return bCommandSent; + } + + /* Search for the weakest 12 FM Stations in the current band. + * + * It searches in the forward direction relative to the current tuned station. + * int numStations: maximum number of stations to search. + * + * @return true if Search command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchListComplete will be called when the Search + * is complete + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to + * the previously tuned station. + */ + public boolean searchWeakStationList(int numStations) + { + + boolean bStatus=false; + FmConfig config = FmSharedPreferences.getFMConfiguration(); + + if(null != mTransmitter) { + mTransmitter.disable(); + mTransmitter = null; + mFMOn = false; + } + if(null != mReceiver) { + mReceiver.disable(); + mReceiver = null; + } + try { + Thread.sleep(100);//SoC needs a delay to switch mode + } catch (Exception ex) { + Log.d( LOGTAG, "RunningThread InterruptedException"); + } + + + if(null == mReceiver) { + try { + mReceiver = new FmReceiver(FMRADIO_DEVICE_FD_STRING, fmCallbacks); + } + catch (InstantiationException e){ + throw new RuntimeException("FmTx service not available!"); + } + if (mReceiver.getFMState() == mReceiver.FMState_Turned_Off) { + bStatus = mReceiver.enable(config); + } else { + try { + Thread.sleep(100); + } catch (Exception ex) { + Log.d( LOGTAG, "RunningThread InterruptedException"); + } + bStatus = mReceiver.enable(config); + } + if (!bStatus) { + Log.e( LOGTAG, "Search for weak station failed"); + return false; + } + } + + bStatus = mReceiver.setStation(config.getLowerLimit()); + + Log.d(LOGTAG, "mReceiver.setStation: bStatus: " + bStatus); + bStatus = mReceiver.searchStationList( FmReceiver.FM_RX_SRCHLIST_MODE_WEAKEST, + FmReceiver.FM_RX_SEARCHDIR_UP, + numStations, + 0); + + mFMSearchStations = 0;//numStations; + if(bStatus == false) + { + try + { + if (mCallbacks != null) + { + mCallbacks.onSearchListComplete(false); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + return bStatus; + } + + /* Cancel any ongoing Search (Seek/Scan/SearchStationList). + * + * @return true if Search command was invoked successfully, false if not muted. + * Note: 1. Callback FmRxEvSearchComplete will be called when the Search + * is complete/cancelled. + * 2. Callback FmRxEvRadioTuneStatus will also be called when tuned to a station + * at the end of the Search or if the seach was cancelled. + */ + public boolean cancelSearch() + { + boolean bStatus=false; + if (mReceiver != null) + { + bStatus = mReceiver.cancelSearch(); + Log.d(LOGTAG, "mReceiver.cancelSearch: bStatus: " + bStatus); + try + { + if (mCallbacks != null) + { + mCallbacks.onSearchListComplete(false); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + + } + return bStatus; + } + + /* Retrieves the basic String to be displayed on UI + * Other than this static string the RDS String will be + * queried by Tx Activity to update on UI + */ + public String getRadioText() { + String str = "Radio Text: Transmitting "; + Log.d(LOGTAG, "Radio Text: [" + str + "]"); + return str; + } + + /* Retrieves the station list from the SearchStationlist. + * + * @return Array of integers that represents the station frequencies. + * Note: 1. This is a synchronous call that should typically called when + * Callback onSearchListComplete. + */ + public int[] getSearchList() + { + int[] frequencyList = null; + if (mReceiver != null) + { + Log.d(LOGTAG, "getSearchList: "); + frequencyList = mReceiver.getStationList(); + } + return frequencyList; + } + /* Set the FM Power Mode on the FM hardware SoC. + * Typically used when UI/Activity is in the background, so the Host is interrupted less often. + * + * boolean bLowPower: true: Enable Low Power mode on FM hardware. + * false: Disable Low Power mode on FM hardware. (Put into normal power mode) + * @return true if set power mode api was invoked successfully, false if the api failed. + */ + public boolean setLowPowerMode(boolean bLowPower) + { + boolean bCommandSent=false; + if (mTransmitter != null) + { + Log.d(LOGTAG, "setLowPowerMode: " + bLowPower); + if(bLowPower) + { + bCommandSent = mTransmitter.setPowerMode(FmTransmitter.FM_TX_LOW_POWER_MODE); + } + else + { + bCommandSent = mTransmitter.setPowerMode(FmTransmitter.FM_TX_NORMAL_POWER_MODE); + } + } + return bCommandSent; + } + /** Determines if an internal Antenna is available. + * + * @return true if internal antenna is available, false if + * internal antenna is not available. + */ + public boolean isInternalAntennaAvailable() + { + boolean bAvailable = false; + /* Update this when the API is available */ + + if(null != mTransmitter ) { + bAvailable = mTransmitter.getInternalAntenna(); + Log.d(LOGTAG, "internalAntennaAvailable: " + bAvailable); + } else if( null != mReceiver ) { + bAvailable = mReceiver.getInternalAntenna(); + Log.d(LOGTAG, "internalAntennaAvailable: " + bAvailable); + } + return bAvailable; + } + + + private FmTransmitterCallbacksAdaptor transmitCallbacks = new FmTransmitterCallbacksAdaptor() { + public void FmTxEvRDSGroupsAvailable() { + // Do nothing + } + + public void FmTxEvRDSGroupsComplete() { + // Do nothing + } + public void FmTxEvContRDSGroupsComplete() { + } + + public void FmTxEvTuneStatusChange(int freq) { + + Log.d(LOGTAG, "onTuneStatusChange\n"); + if (mCallbacks != null) + { + try + { + mCallbacks.onTuneStatusChanged(freq); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + /* Update the frequency in the StatusBar's Notification */ + startNotification(); + + String s = getPSData(); + if( true == mTransmitter.startPSInfo( + s, FM_TX_PROGRAM_TYPE, FM_TX_PROGRAM_ID, FM_TX_PS_REPEAT_COUNT ) ) { + if (mCallbacks != null) + { + try + { + mCallbacks.onPSInfoSent(s); + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + if (mTransmitter != null ){ + mTransmitter.startRTInfo(RText, FM_TX_PROGRAM_TYPE, FM_TX_PROGRAM_ID ); + } + + try { + if (mCallbacks != null ) { + mCallbacks.onMetaDataChanged(RText); + } else { + Log.d(LOGTAG, "callback is not there"); + } + } catch (RemoteException ex){ + ex.printStackTrace(); + } + + } + + public void FmTxEvRadioDisabled() { + Log.d(LOGTAG, "onRadioDisabled"); + mFMOn = false; + if((mServiceInUse) && (mCallbacks != null) ) { + try { + mCallbacks.onDisabled(); + } catch(RemoteException e) { + e.printStackTrace(); + } + } + } + public void FmTxEvRadioEnabled() { + mFMOn = true; + if((mServiceInUse) && (mCallbacks != null) ) { + try { + mCallbacks.onEnabled(true); + } catch(RemoteException e) { + e.printStackTrace(); + } + } + } + public void FmTxEvRadioReset() { + if(isFmOn()) { + // Received Radio Disable event unexpectedly + Log.d(LOGTAG, "FM is ON, reset FM"); + fmRadioReset(); + try { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null)) { + mCallbacks.onRadioReset(); + } + } + catch (RemoteException e) { + e.printStackTrace(); + } + } + } + }; + + /* Receiver callbacks back from the FM Stack */ + FmRxEvCallbacksAdaptor fmCallbacks = new FmRxEvCallbacksAdaptor() + { + public void FmRxEvEnableReceiver() + { + Log.d(LOGTAG, "FmRxEvEnableReceiver"); + } + public void FmRxEvDisableReceiver() + { + Log.d(LOGTAG, "FmRxEvDisableReceiver"); + } + public void FmRxEvRadioReset() + { + Log.d(LOGTAG, "FmRxEvRadioReset"); + if(isFmOn()) { + // Received Radio Reset event + Log.d(LOGTAG, "FM is ON, reset FM"); + fmRadioReset(); + try + { + /* Notify the UI/Activity, only if the service is "bound" + by an activity and if Callbacks are registered + */ + if((mServiceInUse) && (mCallbacks != null) ) + { + mCallbacks.onRadioReset(); + } + } + catch (RemoteException e) + { + e.printStackTrace(); + } + } + } + + public void FmRxEvRadioTuneStatus(int frequency) + { + Log.d(LOGTAG, "FmRxEvRadioTuneStatus: Tuned Frequency: " +frequency); + } + + public void FmRxEvSearchListComplete() + { + Log.d(LOGTAG, "FmRxEvSearchListComplete"); + try + { + if (mCallbacks != null) { + mCallbacks.onSearchListComplete(true); + } else if(mReceiver != null) { + mReceiver.disable(); + } + } catch (RemoteException e) + { + e.printStackTrace(); + } + } + }; + + + /* + * Read the Tuned Frequency from the FM module. + */ + private String getTunedFrequencyString() { + double frequency = mTunedFrequency / 1000.0; + String frequencyString = getString(R.string.stat_notif_tx_frequency, (""+frequency)); + return frequencyString; + } + /** + * Registers an intent to listen for ACTION_HEADSET_PLUG + * notifications. This intent is called to know if the headset + * was plugged in/out + */ + public void registerHeadsetListener() { + if (mHeadsetReceiver == null) { + mHeadsetReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_HEADSET_PLUG)) { + Log.d(LOGTAG, "ACTION_HEADSET_PLUG Intent received"); + // Listen for ACTION_HEADSET_PLUG broadcasts. + Log.d(LOGTAG, "mTransmitter: ACTION_HEADSET_PLUG"); + Log.d(LOGTAG, "==> intent: " + intent); + Log.d(LOGTAG, " state: " + intent.getIntExtra("state", 0)); + Log.d(LOGTAG, " name: " + intent.getStringExtra("name")); + mHeadsetPlugged = (intent.getIntExtra("state", 0) == 1); + mHandler.post(mChangeFMTxState); + + } else if (mA2dpDeviceState.isA2dpStateChange(action)) { + if (mA2dpDeviceState.isConnected(intent)){ + Log.d(LOGTAG, " A2DP connected"); + mHeadsetPlugged = true; + if( mFMOn == true) + { + mHandler.post(mChangeFMTxState); + } + } else if( !mA2dpDeviceState.isConnected(intent)) { + Log.d(LOGTAG, "A2DP disconnected"); + mHeadsetPlugged = false; + if( mFMOn == false) // when FM Tx App open, DISC BT + { + Log.d(LOGTAG, "posting for a2dp state change"); + mHandler.post(mChangeFMTxState); + } + } + } else if (action.equals("HDMI_CONNECTED")) { + if( mFMOn == true) { + Log.d(LOGTAG, "posting for hdmi state change"); + mHandler.post(mChangeFMTxState); + } + } + } + }; + IntentFilter iFilter = new IntentFilter(); + iFilter.addAction(Intent.ACTION_HEADSET_PLUG); + iFilter.addAction(mA2dpDeviceState.getActionSinkStateChangedString()); + iFilter.addAction(mA2dpDeviceState.getActionPlayStateChangedString()); + iFilter.addAction("HDMI_CONNECTED"); + iFilter.addCategory(Intent.CATEGORY_DEFAULT); + registerReceiver(mHeadsetReceiver, iFilter); + } + } + final Runnable mChangeFMTxState = new Runnable() { + public void run() { + boolean bStatus = false; + + Log.d(LOGTAG, "Enter change FM Tx State"); + /* Update the UI based on the state change of the headset/antenna*/ + if(mHeadsetPlugged) { + bStatus = cancelSearch(); + if(bStatus == false) + Log.e(LOGTAG, "Error in cancelling the search"); + if(isFmOn()) { + Log.d(LOGTAG, "disable called from headset handler"); + bStatus = fmOff(); + if(mServiceInUse && (mCallbacks != null) && (bStatus == true)) { + try { + mCallbacks.onDisabled(); + } catch(RemoteException e) { + e.printStackTrace(); + } + } else if(bStatus == false) { + Log.e(LOGTAG, "Error in turning off the FM TX "); + } + } else if(mReceiver != null) { + bStatus = mReceiver.disable(); + if(bStatus == true) + mReceiver = null; + else + Log.e(LOGTAG, "Error in disabling the FM RX"); + } + }else { + if(!isFmOn()) { + bStatus = fmOn(); + if(mServiceInUse && (mCallbacks != null) && (bStatus == true)) { + try { + mCallbacks.onEnabled(true); + } catch(RemoteException e) { + e.printStackTrace(); + } + } else if(bStatus == false) { + Log.e(LOGTAG, "Error in enabling the FM TX"); + } + } + } + } + }; + public boolean isHeadsetPlugged() { + if (mA2dpDeviceState.isDeviceAvailable()) + mHeadsetPlugged = true; + return mHeadsetPlugged; + } + public boolean isCallActive() { + TelephonyManager tmgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + if( TelephonyManager.CALL_STATE_IDLE !=tmgr.getCallState() ) + return true; + return false; + } + public String getPSData(){ + return QFM_STRING; + } +} diff --git a/fmapp/src/com/codeaurora/fmradio/FmSharedPreferences.java b/fmapp/src/com/codeaurora/fmradio/FmSharedPreferences.java new file mode 100644 index 0000000..0e5484c --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FmSharedPreferences.java @@ -0,0 +1,1117 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import android.content.Context; +import android.content.SharedPreferences; +import qcom.fmradio.FmReceiver; +import qcom.fmradio.FmConfig; +import android.util.Log; + +public class FmSharedPreferences +{ + public static final int REGIONAL_BAND_NORTH_AMERICA = 0; + public static final int REGIONAL_BAND_EUROPE = 1; + public static final int REGIONAL_BAND_JAPAN = 2; + public static final int REGIONAL_BAND_JAPAN_WIDE = 3; + public static final int REGIONAL_BAND_AUSTRALIA = 4; + public static final int REGIONAL_BAND_AUSTRIA = 5; + public static final int REGIONAL_BAND_BELGIUM = 6; + public static final int REGIONAL_BAND_BRAZIL = 7; + public static final int REGIONAL_BAND_CHINA = 8; + public static final int REGIONAL_BAND_CZECH = 9; + public static final int REGIONAL_BAND_DENMARK = 10; + public static final int REGIONAL_BAND_FINLAND = 11; + public static final int REGIONAL_BAND_FRANCE = 12; + public static final int REGIONAL_BAND_GERMANY = 13; + public static final int REGIONAL_BAND_GREECE = 14; + public static final int REGIONAL_BAND_HONGKONG = 15; + public static final int REGIONAL_BAND_INDIA = 16; + public static final int REGIONAL_BAND_IRELAND = 17; + public static final int REGIONAL_BAND_ITALY = 18; + public static final int REGIONAL_BAND_KOREA = 19; + public static final int REGIONAL_BAND_MEXICO = 20; + public static final int REGIONAL_BAND_NETHERLANDS = 21; + public static final int REGIONAL_BAND_NEWZEALAND = 22; + public static final int REGIONAL_BAND_NORWAY = 23; + public static final int REGIONAL_BAND_POLAND = 24; + public static final int REGIONAL_BAND_PORTUGAL = 25; + public static final int REGIONAL_BAND_RUSSIA = 26; + public static final int REGIONAL_BAND_SINGAPORE = 27; + public static final int REGIONAL_BAND_SLOVAKIA = 28; + public static final int REGIONAL_BAND_SPAIN = 29; + public static final int REGIONAL_BAND_SWITZERLAND = 30; + public static final int REGIONAL_BAND_SWEDEN = 31; + public static final int REGIONAL_BAND_TAIWAN = 32; + public static final int REGIONAL_BAND_TURKEY = 33; + public static final int REGIONAL_BAND_UNITEDKINGDOM = 34; + public static final int REGIONAL_BAND_UNITED_STATES = 35; + + public static final int RECORD_DUR_INDEX_0_VAL = 5; + public static final int RECORD_DUR_INDEX_1_VAL = 15; + public static final int RECORD_DUR_INDEX_2_VAL = 30; + public static final int RECORD_DUR_INDEX_3_VAL = -1; + + private static final String LOGTAG = FMRadio.LOGTAG; + + private static final String SHARED_PREFS = "fmradio_prefs"; + private static final String LIST_NUM = "list_number"; + private static final String LIST_NAME = "list_name"; + private static final String STATION_NAME = "station_name"; + private static final String STATION_FREQUENCY = "station_freq"; + private static final String STATION_ID = "station_id"; + private static final String STATION_PTY = "station_pty"; + private static final String STATION_RDS = "station_rds"; + private static final String STATION_NUM = "preset_number"; + + private static final String FMCONFIG_COUNTRY = "fmconfig_country"; + //private static final String FMCONFIG_BAND = "fmconfig_band"; + //private static final String FMCONFIG_MIN = "fmconfig_min"; + //private static final String FMCONFIG_MAX = "fmconfig_max"; + //private static final String FMCONFIG_STEP = "fmconfig_step"; + //private static final String FMCONFIG_EMPH = "fmconfig_emphasis"; + //private static final String FMCONFIG_RDSSTD = "fmconfig_rdsstd"; + /* Storage key String */ + private static final String LAST_LIST_INDEX = "last_list_index"; + public static final int MAX_NUM_TAG_TYPES = 64; + public static final int NUM_TAG_CATEGORY = 8; + public static final String []TAG_CATEGORIES = { "DUMMY", "ITEM", "INFO", "PROGRAMME", + "INTERACTIVITY", "RFU", "PRIVATE_CLASSES", + "DESCRIPTOR" }; + public static final int [][]TAG_CATEGORY_RANGE = { {0, 0}, {1, 11}, {12, 30}, + {31, 40}, {41, 53}, {54, 55}, + {56, 58}, {59, 63} }; + public static final String []TAG_NAMES = { "DUMMY", "ITEM.TITLE", "ITEM.ALBUM", "ITEM.TRACKNUM", + "ITEM.ARTIST", "ITEM.COMPOSITION", "ITEM.MOVEMENT", + "ITEM.CONDUCTOR", "ITEM.COMPOSER", "ITEM.BAND", "ITEM.COMMENT", + "ITEM.GENERE", "INFO.NEWS", "INFO.NEWS_LOCAL", "INFO.STOCK", + "INFO.SPORT", "INFO.LOTTERY", "INFO.HOROSCOPE", + "INFO.DAILY_DIVERSION", "INFO.HEALTH", "INFO.EVENT", + "INFO.SCENE", "INFO.CINEMA", "INFO.TV", "INFO.DATE_TIME", + "INFO.WEATHER", "INFO.TRAFFIC", "INFO.ALARM", "INFO.ADS", + "INFO.URL", "INFO.OTHER", "PROGRAMME.STATIONNAME_SHORT", + "PROGRAMME.NOW", "PROGRAMME.NEXT", "PROGRAMME.PART", + "PROGRAMME.HOST", "PROGRAMME.EDITORIAL_STAFF", + "PROGRAMME.FREQUENCY", "PROGRAMME.HOMEPAGE", + "PROGRAMME.SUBCHANNEL", "PHONE.HOTLINE", "PHONE.STUDIO", + "PHONE.OTHER", "SMS.STUDIO", "SMS.OTHER", "EMAIL.HOTLINE", + "EMAIL.STUDIO", "EMAIL.OTHER", "MMS.OTHER", "CHAT", + "CHAT.CENTRE", "VOTE.QUESTION", "VOTE.CENTRE", "RFU.1", + "RFU.2", "PRIVATE.1", "PRIVATE.2", "PRIVATE.3", + "PLACE", "APPOINTMENT", "IDENTIFIER", "PURCHASE", "GET_DATA" }; + + private static final String PREF_LAST_TUNED_FREQUENCY = "last_frequency"; + private static final String LAST_RECORD_DURATION = "last_record_duration"; + private static String LAST_AF_JUMP_VALUE = "last_af_jump_value"; + + private static Map<String, String> mNameMap = new HashMap<String, String>(); + private static List<PresetList> mListOfPlists = new ArrayList<PresetList>(); + public static Set[] tagList = new TreeSet[FmSharedPreferences.MAX_NUM_TAG_TYPES]; + public static int num_tags = 0; + private static FmConfig mFMConfiguration; + + private static final String DEFAULT_NO_NAME = ""; + private static final int DEFAULT_NO_FREQUENCY = 98100; + private static final int DEFAULT_NO_PTY = 0; + private static final int DEFAULT_NO_STATIONID = 0; + private static final int DEFAULT_NO_RDSSUP = 0; + private static CharSequence[] mListEntries; + private static CharSequence[] mListValues; + private static int mListIndex; + private Context mContext; + private static int mTunedFrequency = 98100; + private static int mFrequencyBand_Stepsize = 200; + + private static int mCountry=0; + /* true = Stereo and false = "force Mono" even if Station is transmitting a + * Stereo signal + */ + private static boolean mAudioOutputMode=true; + private static boolean mAFAutoSwitch=true; + private static int mRecordDuration=0; + + FmSharedPreferences(Context context){ + mContext = context.getApplicationContext(); + mFMConfiguration = new FmConfig(); + Load(); + } + + public static void removeStation(int listIndex, int stationIndex){ + if (listIndex < getNumList()) + { + mListOfPlists.get(listIndex).removeStation(stationIndex); + } + } + public static void removeStation(int listIndex, PresetStation station){ + if (listIndex < getNumList()) + { + mListOfPlists.get(listIndex).removeStation(station); + } + } + + public static void setListName(int listIndex, String name){ + if (listIndex < getNumList()) + { + mListOfPlists.get(listIndex).setName(name); + } + } + + public static void setStationName(int listIndex, int stationIndex, String name){ + if (listIndex < getNumList()) + { + mListOfPlists.get(listIndex).setStationName(stationIndex, name); + } + } + + public static String getListName(int listIndex){ + String name = ""; + addListIfEmpty(listIndex); + if (listIndex < getNumList()) + { + name= mListOfPlists.get(listIndex).getName(); + } + return name; + } + + public static String getStationName(int listIndex, int stationIndex){ + String name = ""; + if (listIndex < getNumList()) + { + name = mListOfPlists.get(listIndex).getStationName(stationIndex); + } + return name; + } + + public static double getStationFrequency(int listIndex, int stationIndex){ + double frequency = 0; + if (listIndex < getNumList()) + { + frequency = mListOfPlists.get(listIndex).getStationFrequency(stationIndex); + } + return frequency; + } + + public static PresetList getStationList(int listIndex){ + if (listIndex < getNumList()) + { + return mListOfPlists.get(listIndex); + } + return null; + } + + public static PresetStation getselectedStation(){ + int listIndex = getCurrentListIndex(); + PresetStation station = null; + if (listIndex < getNumList()) + { + station = mListOfPlists.get(listIndex).getSelectedStation(); + } + return station; + } + + public static PresetStation getStationInList(int index){ + int listIndex = getCurrentListIndex(); + PresetStation station = null; + if (listIndex < getNumList()) + { + station = mListOfPlists.get(listIndex).getStationFromIndex(index); + } + return station; + } + public static PresetStation getStationFromFrequency(int frequency){ + int listIndex = getCurrentListIndex(); + PresetStation station = null; + if (listIndex < getNumList()) + { + station = mListOfPlists.get(listIndex).getStationFromFrequency(frequency); + } + return station; + } + + public static PresetStation selectNextStation(){ + int listIndex = getCurrentListIndex(); + PresetStation station = null; + if (listIndex < getNumList()) + { + station = mListOfPlists.get(listIndex).selectNextStation(); + } + return station; + } + + public static PresetStation selectPrevStation(){ + int listIndex = getCurrentListIndex(); + PresetStation station = null; + if (listIndex < getNumList()) + { + station = mListOfPlists.get(listIndex).selectPrevStation(); + } + return station; + } + + public static void selectStation(PresetStation station){ + int listIndex = getCurrentListIndex(); + if (listIndex < getNumList()) + { + mListOfPlists.get(listIndex).selectStation(station); + } + } + + public static int getNumList(){ + return mListOfPlists.size(); + } + + public static int getCurrentListIndex(){ + return mListIndex; + } + + public static void setListIndex(int index){ + mListIndex = index; + } + + public static Map<String, String> getNameMap(){ + return mNameMap; + } + + private static void addListIfEmpty(int listIndex){ + if ( (listIndex < 1) && (getNumList() == 0)) + { + createPresetList("FM"); + } + } + + public static void addStation(String name, int freq, int listIndex){ + /* If no lists exists and a new station is added, add a new Preset List + * if "listIndex" requested was "0" + */ + addListIfEmpty(listIndex); + if (getNumList() > listIndex) + { + mListOfPlists.get(listIndex).addStation(name, freq); + } + } + + /** Add "station" into the Preset List indexed by "listIndex" */ + public static void addStation(int listIndex, PresetStation station){ + /* If no lists exists and a new station is added, add a new Preset List + * if "listIndex" requested was "0" + */ + addListIfEmpty(listIndex); + if (getNumList() > listIndex) + { + mListOfPlists.get(listIndex).addStation(station); + } + } + public static void addTags(int index, String s) { + if ((index >= 0) && (index <FmSharedPreferences.MAX_NUM_TAG_TYPES)) { + if(tagList[index] == null) { + tagList[index] = new TreeSet<String>(); + } + if (tagList[index].add(s)) + num_tags++; + } + } + public static void clearTags() { + for(int i = 0; i <FmSharedPreferences.MAX_NUM_TAG_TYPES; i++) { + if(tagList[i] != null) { + tagList[i].clear(); + Log.d(LOGTAG, "cleared tags of type" + i); + } + } + num_tags = 0; + } + + /** Does "station" already exist in the Preset List indexed by "listIndex" */ + public static boolean sameStationExists(int listIndex, PresetStation station){ + boolean exists = false; + if (getNumList() > listIndex) + { + exists = mListOfPlists.get(listIndex).sameStationExists(station); + } + return exists; + } + + /** Does "station" already exist in the current Preset List*/ + public static boolean sameStationExists( PresetStation station){ + int listIndex = getCurrentListIndex(); + boolean exists = false; + if (getNumList() > listIndex) + { + exists = mListOfPlists.get(listIndex).sameStationExists(station); + } + return exists; + } + + /** Does "station" already exist in the current Preset List*/ + public static int getListStationCount( ){ + int listIndex = getCurrentListIndex(); + int numStations = 0; + if (getNumList() > listIndex) + { + numStations = mListOfPlists.get(listIndex).getStationCount(); + } + return numStations; + } + + public static void renamePresetList(String newName, int listIndex){ + PresetList curList = mListOfPlists.get(listIndex); + if (curList != null) + { + String oldListName = curList.getName(); + curList.setName(newName); + String index = mNameMap.get(oldListName); + mNameMap.remove(oldListName); + mNameMap.put((String) newName, index); + repopulateEntryValueLists(); + } + } + + /* Returns the index of the list just created */ + public static int createPresetList(String name) { + int numLists = mListOfPlists.size(); + mListOfPlists.add(new PresetList(name)); + String index = String.valueOf(numLists); + mNameMap.put(name, index); + repopulateEntryValueLists(); + return numLists; + } + + + public static void createFirstPresetList(String name) { + mListIndex = 0; + createPresetList(name); + } + + public static CharSequence[] repopulateEntryValueLists() { + ListIterator<PresetList> presetIter; + presetIter = mListOfPlists.listIterator(); + int numLists = mListOfPlists.size(); + + mListEntries = new CharSequence[numLists]; + mListValues = new CharSequence[numLists]; + for (int i = 0; i < numLists; i++) + { + PresetList temp = presetIter.next(); + mListEntries[i] = temp.getName(); + mListValues[i] = temp.getName(); + } + return mListEntries; + } + + public static List<PresetList> getPresetLists() { + return mListOfPlists; + } + + + public void Load(){ + Log.d(LOGTAG, "Load preferences "); + if(mContext == null) + { + return; + } + SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE); + mTunedFrequency = sp.getInt(PREF_LAST_TUNED_FREQUENCY, DEFAULT_NO_FREQUENCY); + mRecordDuration = sp.getInt(LAST_RECORD_DURATION, RECORD_DUR_INDEX_0_VAL); + mAFAutoSwitch = sp.getBoolean(LAST_AF_JUMP_VALUE, true); + + int num_lists = sp.getInt(LIST_NUM, 1); + if (mListOfPlists.size() == 0) { + + for (int listIter = 0; listIter < num_lists; listIter++) + { + String listName = sp.getString(LIST_NAME + listIter, "FM - " + (listIter+1)); + int numStations = sp.getInt(STATION_NUM + listIter, 1); + if (listIter == 0) + { + createFirstPresetList(listName); + } else + { + createPresetList(listName); + } + + PresetList curList = mListOfPlists.get(listIter); + for (int stationIter = 0; stationIter < numStations; stationIter++) + { + String stationName = sp.getString(STATION_NAME + listIter + "x" + stationIter, + DEFAULT_NO_NAME); + int stationFreq = sp.getInt(STATION_FREQUENCY + listIter + "x" + stationIter, + DEFAULT_NO_FREQUENCY); + PresetStation station = curList.addStation(stationName, stationFreq); + + int stationId = sp.getInt(STATION_ID + listIter + "x" + stationIter, + DEFAULT_NO_STATIONID); + station.setPI(stationId); + + int pty = sp.getInt(STATION_PTY + listIter + "x" + stationIter, DEFAULT_NO_PTY); + station.setPty(pty); + + int rdsSupported = sp.getInt(STATION_RDS + listIter + "x" + stationIter, + DEFAULT_NO_RDSSUP); + if (rdsSupported != 0) + { + station.setRDSSupported(true); + } else + { + station.setRDSSupported(false); + } + } + } + } + /* Load Configuration */ + setCountry(sp.getInt(FMCONFIG_COUNTRY, 0)); + /* Last list the user was navigating */ + mListIndex = sp.getInt(LAST_LIST_INDEX, 0); + if(mListIndex >= num_lists) + { + mListIndex=0; + } + } + + public void Save() { + if(mContext == null) + { + return; + } + Log.d(LOGTAG, "Save preferences "); + + int numLists = mListOfPlists.size(); + SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor ed = sp.edit(); + + ed.putInt(PREF_LAST_TUNED_FREQUENCY, mTunedFrequency); + + ed.putInt(LIST_NUM, numLists); + /* Last list the user was navigating */ + ed.putInt(LAST_LIST_INDEX, mListIndex); + + for (int listIter = 0; listIter < numLists; listIter++) + { + PresetList curList = mListOfPlists.get(listIter); + ed.putString(LIST_NAME + listIter, curList.getName()); + int numStations = curList.getStationCount(); + ed.putInt(STATION_NUM + listIter, numStations); + int numStation = 0; + for (int stationIter = 0; stationIter < numStations; stationIter++) + { + PresetStation station = curList.getStationFromIndex(stationIter); + if (station != null) + { + ed.putString(STATION_NAME + listIter + "x" + numStation, + station.getName()); + ed.putInt(STATION_FREQUENCY + listIter + "x" + numStation, + station.getFrequency()); + ed.putInt(STATION_ID + listIter + "x" + numStation, + station.getPI()); + ed.putInt(STATION_PTY + listIter + "x" + numStation, + station.getPty()); + ed.putInt(STATION_RDS + listIter + "x" + numStation, + (station.getRDSSupported() == true? 1:0)); + numStation ++; + } + } + } + + /* Save Configuration */ + ed.putInt(FMCONFIG_COUNTRY, mCountry); + ed.putInt(LAST_RECORD_DURATION, mRecordDuration); + ed.putBoolean(LAST_AF_JUMP_VALUE, mAFAutoSwitch); + ed.commit(); + } + + public static void SetDefaults() { + mListIndex=0; + mListOfPlists.clear(); + setCountry(REGIONAL_BAND_NORTH_AMERICA); + setRadioBand(0); + setChSpacing(0); + setEmphasis(0); + setRdsStd(0); + mFMConfiguration.setLowerLimit(87500); + mFMConfiguration.setUpperLimit(107900); + } + + public static void removeStationList(int listIndex) { + mListIndex = listIndex; + PresetList toRemove = mListOfPlists.get(mListIndex); + + mNameMap.remove(toRemove.getName()); + mListOfPlists.remove(mListIndex); + int numLists = mListOfPlists.size(); + + /* Remove for others */ + for (int i = mListIndex; i < numLists; i++) + { + PresetList curList = mListOfPlists.get(i); + if (curList!=null) + { + String listName = curList.getName(); + /* Removals */ + mNameMap.remove(listName); + mNameMap.put(listName, String.valueOf(i)); + } + } + mListIndex = 0; + repopulateEntryValueLists(); + } + + + public static void setTunedFrequency(int frequency) { + mTunedFrequency = frequency; + } + + public static int getTunedFrequency() { + return mTunedFrequency; + } + + public static int getNextTuneFrequency(int frequency) { + int nextFrequency = (frequency + mFrequencyBand_Stepsize); + if (nextFrequency > getUpperLimit()) + { + nextFrequency = getLowerLimit(); + } + return nextFrequency; + } + + public static int getNextTuneFrequency() { + int nextFrequency = (mTunedFrequency + mFrequencyBand_Stepsize); + if (nextFrequency > getUpperLimit()) + { + nextFrequency = getLowerLimit(); + } + return nextFrequency; + } + + public static int getPrevTuneFrequency(int frequency) { + int prevFrequency = (frequency - mFrequencyBand_Stepsize); + if (prevFrequency < getLowerLimit()) + { + prevFrequency = getUpperLimit(); + } + return prevFrequency; + } + + public static int getPrevTuneFrequency() { + int prevFrequency = (mTunedFrequency - mFrequencyBand_Stepsize); + if (prevFrequency < getLowerLimit()) + { + prevFrequency = getUpperLimit(); + } + return prevFrequency; + } + + /** + * @param mFMConfiguration the mFMConfiguration to set + */ + public static void setFMConfiguration(FmConfig mFMConfig) { + FmSharedPreferences.mFMConfiguration = mFMConfig; + } + + /** + * @return the mFMConfiguration + */ + public static FmConfig getFMConfiguration() { + return mFMConfiguration; + } + + public static void setRadioBand(int band) + { + switch (band) + { + case FmReceiver.FM_JAPAN_WIDE_BAND: + { + mFrequencyBand_Stepsize = 50; + mFMConfiguration.setLowerLimit(76000); + mFMConfiguration.setUpperLimit(108000); + break; + } + case FmReceiver.FM_JAPAN_STANDARD_BAND: + { + mFrequencyBand_Stepsize = 100; + mFMConfiguration.setLowerLimit(76000); + mFMConfiguration.setUpperLimit(90000); + break; + } + case FmReceiver.FM_USER_DEFINED_BAND: + { + break; + } + case FmReceiver.FM_US_BAND: + case FmReceiver.FM_EU_BAND: + default: + { + band = FmReceiver.FM_US_BAND; + mFMConfiguration.setLowerLimit(87500); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 100; + } + } + mFMConfiguration.setRadioBand(band); + } + + public static int getRadioBand() + { + return mFMConfiguration.getRadioBand(); + } + + public static void setChSpacing(int spacing) + { + if( (spacing>=FmReceiver.FM_CHSPACE_200_KHZ) + && (spacing<=FmReceiver.FM_CHSPACE_50_KHZ)) + { + mFrequencyBand_Stepsize = 200; + switch (spacing) + { + case FmReceiver.FM_CHSPACE_100_KHZ: + { + mFrequencyBand_Stepsize = 100; + break; + } + case FmReceiver.FM_CHSPACE_50_KHZ: + { + mFrequencyBand_Stepsize = 50; + break; + } + } + mFMConfiguration.setChSpacing(spacing); + } + } + + public static int getChSpacing() + { + return mFMConfiguration.getChSpacing(); + } + + public static void setRdsStd(int std) + { + if( (std>=FmReceiver.FM_RDS_STD_RBDS) + && (std<=FmReceiver.FM_RDS_STD_NONE)) + { + mFMConfiguration.setRdsStd(std); + } + } + + public static int getRdsStd() + { + return mFMConfiguration.getRdsStd(); + } + + /* North America */ + public static boolean isRDSStd() + { + return(FmReceiver.FM_RDS_STD_RDS == mFMConfiguration.getRdsStd()); + } + + public static boolean isRBDSStd() + { + return(FmReceiver.FM_RDS_STD_RBDS == mFMConfiguration.getRdsStd()); + } + + public static void setEmphasis(int emph) + { + if( (emph>=FmReceiver.FM_DE_EMP75) + && (emph<=FmReceiver.FM_DE_EMP50)) + { + mFMConfiguration.setEmphasis(emph); + } + } + + public static int getEmphasis() + { + return mFMConfiguration.getEmphasis(); + } + + public static int getUpperLimit() + { + return mFMConfiguration.getUpperLimit(); + } + + public static int getLowerLimit() + { + return mFMConfiguration.getLowerLimit(); + } + + public static void setLowerLimit(int lowLimit){ + mFMConfiguration.setLowerLimit(lowLimit); + } + + public static void setUpperLimit(int upLimit){ + mFMConfiguration.setUpperLimit(upLimit); + } + + public static void setCountry(int nCountryCode){ + + // Default: 87500 TO 10800 IN 100 KHZ STEPS + mFMConfiguration.setRadioBand(FmReceiver.FM_USER_DEFINED_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_100_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP50); + mFMConfiguration.setRdsStd(FmReceiver.FM_RDS_STD_RDS); + mFMConfiguration.setLowerLimit(87500); + mFMConfiguration.setUpperLimit(108000); + + switch(nCountryCode) + { + case REGIONAL_BAND_NORTH_AMERICA: + { + /*NORTH_AMERICA : 87500 TO 108000 IN 200 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75); + mFMConfiguration.setRdsStd(FmReceiver.FM_RDS_STD_RBDS); + mFMConfiguration.setLowerLimit(87500); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 200; + break; + } + case REGIONAL_BAND_EUROPE: + { + /*EUROPE/Default : 87500 TO 10800 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + + case REGIONAL_BAND_JAPAN: + { + /*JAPAN : 76000 TO 90000 IN 100 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_JAPAN_STANDARD_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_100_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75); + mFMConfiguration.setLowerLimit(76000); + mFMConfiguration.setUpperLimit(90000); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_JAPAN_WIDE: + { + /*JAPAN_WB : 90000 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_JAPAN_WIDE_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75); + mFMConfiguration.setLowerLimit(90000); + mFMConfiguration.setUpperLimit(108000); + mFrequencyBand_Stepsize = 50; + break; + } + + /* Country specific */ + case REGIONAL_BAND_AUSTRALIA: + { + /*AUSTRALIA : 87700 TO 108000 IN 100 KHZ STEPS*/ + mFMConfiguration.setLowerLimit(87700); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_AUSTRIA: + { + /*AUSTRIA : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP50); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_BELGIUM: + { + /*BELGIUM : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_BRAZIL: + { + /*BRAZIL : 87500 TO 108000 IN 200 KHZ STEP*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ); + mFMConfiguration.setLowerLimit(87500); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 200; + break; + } + case REGIONAL_BAND_CHINA: + { + /*CHINA : 87000 TO 108000 IN 100 KHZ STEPS*/ + mFMConfiguration.setLowerLimit(87000); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_CZECH: + { + /*CZECH : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_DENMARK: + { + /*DENMARK : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_FINLAND: + { + /*FINLAND : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_FRANCE: + { + /* FRANCE : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_GERMANY: + /*GERMANY : 87500 TO 108000 IN 50 KHZ STEPS*/ + case REGIONAL_BAND_GREECE: + /*GREECE : 87500 TO 108000 IN 50 KHZ STEPS*/ + { + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_HONGKONG: + { + /*HONG KONG : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_INDIA: + { + /*INDIA : 91000 TO 106400 IN 100 KHZ STEPS*/ + mFMConfiguration.setLowerLimit(91000); + mFMConfiguration.setUpperLimit(106400); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_IRELAND: + { + /*IRELAND : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_ITALY: + { + /*ITALY : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_KOREA: + { + /*KOREA : 87500 TO 108000 IN 200 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 200; + break; + } + case REGIONAL_BAND_MEXICO: + { + /*MEXICO : 88100 TO 107900 IN 200 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75); + mFMConfiguration.setRdsStd(FmReceiver.FM_RDS_STD_RBDS); + mFMConfiguration.setLowerLimit(88100); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 200; + break; + } + case REGIONAL_BAND_NETHERLANDS: + { + /*NETHERLANDS : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_NEWZEALAND: + { + /*NEW ZEALAND : 88000 TO 107000 IN 100 KHZ STEPS*/ + mFMConfiguration.setLowerLimit(88000); + mFMConfiguration.setUpperLimit(107000); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_NORWAY: + { + /*NORWAY : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_POLAND: + { + /*POLAND : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFMConfiguration.setLowerLimit(87500); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_PORTUGAL: + { + /*PORTUGAL : 87500 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFrequencyBand_Stepsize = 50; + break; + } + case REGIONAL_BAND_RUSSIA: + { + /*RUSSIA : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_SINGAPORE: + { + /*SINGAPORE : 88000 TO 108000 IN 100 KHZ STEPS*/ + mFMConfiguration.setLowerLimit(88000); + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_SLOVAKIA: + { + /*SLOVAKIA : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_SPAIN: + { + /*SPAIN : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_SWITZERLAND: + { + /*SWITZERLAND : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_SWEDEN: + { + /*SWEDEN : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_TAIWAN: + { + /*TAIWAN : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_TURKEY: + { + /*TURKEY : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_UNITEDKINGDOM: + { + /*UNITED KINGDOM : 87500 TO 108000 IN 100 KHZ STEPS*/ + mFrequencyBand_Stepsize = 100; + break; + } + case REGIONAL_BAND_UNITED_STATES: + { + /*UNITED STATES : 88100 TO 107900 IN 200 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ); + mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75); + mFMConfiguration.setRdsStd(FmReceiver.FM_RDS_STD_RBDS); + mFMConfiguration.setLowerLimit(88100); + mFMConfiguration.setUpperLimit(107900); + mFrequencyBand_Stepsize = 200; + break; + } + default: + { + Log.d(LOGTAG, "Invalid: countryCode: "+nCountryCode); + nCountryCode=0; + } + } + mCountry = nCountryCode; + Log.d(LOGTAG, "====================================================="); + Log.d(LOGTAG, "Country :"+nCountryCode); + Log.d(LOGTAG, "RadioBand :"+ mFMConfiguration.getRadioBand()); + Log.d(LOGTAG, "Emphasis :"+ mFMConfiguration.getEmphasis()); + Log.d(LOGTAG, "ChSpacing :"+ mFMConfiguration.getChSpacing()); + Log.d(LOGTAG, "RdsStd :"+ mFMConfiguration.getRdsStd()); + Log.d(LOGTAG, "LowerLimit :"+ mFMConfiguration.getLowerLimit()); + Log.d(LOGTAG, "UpperLimit :"+ mFMConfiguration.getUpperLimit()); + Log.d(LOGTAG, "====================================================="); + } + + + public static int getCountry() { + return mCountry; + } + + + public static void setAudioOutputMode(boolean bStereo) { + mAudioOutputMode = bStereo; + } + + public static boolean getAudioOutputMode() { + return mAudioOutputMode; + } + + public static void setRecordDuration(int durationIndex) { + + Log.d(LOGTAG, "setRecordDuration "+durationIndex); + switch( durationIndex ) { + + case 0: mRecordDuration = RECORD_DUR_INDEX_0_VAL; break; + case 1: mRecordDuration = RECORD_DUR_INDEX_1_VAL; break; + case 2: mRecordDuration = RECORD_DUR_INDEX_2_VAL; break; + case 3: mRecordDuration = RECORD_DUR_INDEX_3_VAL; break; + default: + { + Log.d(LOGTAG, "Invalid: durationIndex "+durationIndex); + } + + } + return; + } + + public static int getRecordDuration() { + return mRecordDuration; + } + + + public static void setAutoAFSwitch(boolean bAFAutoSwitch) { + mAFAutoSwitch = bAFAutoSwitch; + } + + public static boolean getAutoAFSwitch() { + return mAFAutoSwitch; + } +} diff --git a/fmapp/src/com/codeaurora/fmradio/FmTags.java b/fmapp/src/com/codeaurora/fmradio/FmTags.java new file mode 100644 index 0000000..c54196a --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/FmTags.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011-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 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import java.util.*; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Handler; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.Spinner; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; +import android.widget.ListView; + + +public class FmTags extends Activity { + private ListView la; + private ArrayAdapter<String> adapter; + private final Handler mHandler = new Handler(); + private Iterator itr; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fmtags); + la = (ListView)findViewById(R.id.TagListView); + } + @Override + public void onStart() { + super.onStart(); + } + @Override + public void onResume() { + super.onResume(); + mHandler.post(mDisplayTagList); + } + final Runnable mDisplayTagList = new Runnable() { + public void run() { + String[] tags; + int l = 0; + + tags = new String[FmSharedPreferences.num_tags]; + for(int i = 0; i < FmSharedPreferences.MAX_NUM_TAG_TYPES; i++) { + if (FmSharedPreferences.tagList[i] != null) { + itr = FmSharedPreferences.tagList[i].iterator(); + while(itr.hasNext()) + tags[l++] = ((FmSharedPreferences.TAG_NAMES[i]) + "\n" + + "\t" + (String)itr.next()); + } + } + adapter = new ArrayAdapter(la.getContext(), android.R.layout.simple_list_item_1, tags); + la.setAdapter(adapter); + } + }; +} + + diff --git a/fmapp/src/com/codeaurora/fmradio/IFMRadioService.aidl b/fmapp/src/com/codeaurora/fmradio/IFMRadioService.aidl new file mode 100644 index 0000000..79d83f7 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/IFMRadioService.aidl @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import com.codeaurora.fmradio.IFMRadioServiceCallbacks; + +interface IFMRadioService +{ + boolean fmOn(); + boolean fmOff(); + boolean fmRadioReset(); + boolean isFmOn(); + boolean isAnalogModeEnabled(); + boolean isFmRecordingOn(); + boolean isSpeakerEnabled(); + boolean fmReconfigure(); + void registerCallbacks(IFMRadioServiceCallbacks cb); + void unregisterCallbacks(); + boolean mute(); + boolean routeAudio(int device); + boolean unMute(); + boolean isMuted(); + boolean startRecording(); + void stopRecording(); + boolean tune(int frequency); + boolean seek(boolean up); + void enableSpeaker(boolean speakerOn); + boolean scan(int pty); + boolean seekPI(int piCode); + boolean searchStrongStationList(int numStations); + int[] getSearchList(); + boolean cancelSearch(); + String getProgramService(); + String getRadioText(); + int getProgramType(); + int getProgramID(); + boolean setLowPowerMode(boolean bLowPower); + int getPowerMode(); + boolean enableAutoAF(boolean bEnable); + boolean enableStereo(boolean bEnable); + boolean isAntennaAvailable(); + boolean isWiredHeadsetAvailable(); + boolean isCallActive(); + int getRssi(); + int getIoC(); + int getMpxDcc(); + int getIntDet(); + int getSINR(); + void setHiLoInj(int inj); + void delayedStop(long nDuration, int nType); + void cancelDelayedStop (int nType); + void requestFocus(); + boolean setSinrSamplesCnt(int samplesCnt); + boolean setSinrTh(int sinr); + boolean setIntfDetLowTh(int intfLowTh); + boolean setIntfDetHighTh(int intfHighTh); + String getExtenRadioText(); + int getSinrSamplesCnt(); + int getSinrTh(); + int getSearchAlgoType(); + boolean setSearchAlgoType(int searchType); + int getSinrFirstStage(); + boolean setSinrFirstStage(int sinr); + int getRmssiFirstStage(); + boolean setRmssiFirstStage(int rmssi); + int getCFOMeanTh(); + boolean setCFOMeanTh(int cf0); + boolean setAfJmpRmssiTh(int afJmpRmssiTh); + boolean setGoodChRmssiTh(int gdChRmssiTh); + boolean setAfJmpRmssiSamplesCnt(int afJmpRmssiSmplsCnt); + int getAfJmpRmssiTh(); + int getGoodChRmssiTh(); + int getAfJmpRmssiSamplesCnt(); + boolean setRxRepeatCount(int count); +} + diff --git a/fmapp/src/com/codeaurora/fmradio/IFMRadioServiceCallbacks.aidl b/fmapp/src/com/codeaurora/fmradio/IFMRadioServiceCallbacks.aidl new file mode 100644 index 0000000..eb782e8 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/IFMRadioServiceCallbacks.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +interface IFMRadioServiceCallbacks +{ + void onEnabled(); + void onDisabled(); + void onRadioReset(); + void onTuneStatusChanged(); + void onProgramServiceChanged(); + void onRadioTextChanged(); + void onAlternateFrequencyChanged(); + void onSignalStrengthChanged(); + void onSearchComplete(); + void onSearchListComplete(); + void onMute(boolean bMuted); + void onAudioUpdate(boolean bStereo); + void onStationRDSSupported(boolean bRDSSupported); + void onRecordingStopped(); + void onExtenRadioTextChanged(); +} diff --git a/fmapp/src/com/codeaurora/fmradio/IFMTransmitterService.aidl b/fmapp/src/com/codeaurora/fmradio/IFMTransmitterService.aidl new file mode 100644 index 0000000..39bc70b --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/IFMTransmitterService.aidl @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import com.codeaurora.fmradio.IFMTransmitterServiceCallbacks; +interface IFMTransmitterService +{ + boolean fmOn(); + boolean fmOff(); + boolean fmRestart(); + boolean isFmOn(); + boolean fmReconfigure(); + void registerCallbacks(IFMTransmitterServiceCallbacks cb); + void unregisterCallbacks(); + boolean tune(int frequency); + boolean searchWeakStationList(int numStations); + int[] getSearchList(); + boolean cancelSearch(); + String getRadioText(); + boolean isInternalAntennaAvailable(); + boolean isHeadsetPlugged(); + boolean isCallActive(); + String getPSData(); +} + diff --git a/fmapp/src/com/codeaurora/fmradio/IFMTransmitterServiceCallbacks.aidl b/fmapp/src/com/codeaurora/fmradio/IFMTransmitterServiceCallbacks.aidl new file mode 100644 index 0000000..27a7249 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/IFMTransmitterServiceCallbacks.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009,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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +interface IFMTransmitterServiceCallbacks +{ + void onEnabled(boolean bStatus); + void onDisabled(); + void onRadioReset(); + void onTuneStatusChanged(int frequency); + void onRadioTextChanged(); + void onSearchListComplete(boolean bStatus); + void onReconfigured(); + void onMetaDataChanged(String str); + void onPSInfoSent(String str); +} diff --git a/fmapp/src/com/codeaurora/fmradio/PresetList.java b/fmapp/src/com/codeaurora/fmradio/PresetList.java new file mode 100644 index 0000000..a2fd1a9 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/PresetList.java @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import java.util.ArrayList; +import java.util.List; + +public class PresetList{ + private List<PresetStation> mPresetList = new ArrayList<PresetStation>(); + private int mCurrentStation = 0; + private String mListName = ""; + + public PresetList(String name) { + mListName = name; + } + + public String getName(){ + return mListName; + } + + public String toString(){ + return mListName; + } + + public synchronized int getStationCount(){ + return mPresetList.size(); + } + + public synchronized String getStationName(int stationNum){ + String name = ""; + if (mPresetList.size() > stationNum){ + name = mPresetList.get(stationNum).getName(); + } + return name; + } + + public synchronized int getStationFrequency(int stationNum){ + int frequency = 102100; + if (mPresetList.size() > stationNum){ + frequency = mPresetList.get(stationNum).getFrequency(); + } + return frequency; + } + + public void setName(String name){ + mListName = name; + } + + public synchronized void setStationFrequency(int stationNum, int frequency){ + PresetStation mStation = mPresetList.get(stationNum); + mStation.setFrequency(frequency); + } + + public synchronized void setStationName(int stationNum, String name){ + PresetStation mStation = mPresetList.get(stationNum); + mStation.setName(name); + } + + public synchronized PresetStation getStationFromIndex(int index){ + int totalPresets = mPresetList.size(); + PresetStation station = null; + if (index < totalPresets) { + station = mPresetList.get(index); + } + return station; + } + + public synchronized PresetStation getStationFromFrequency(int frequency){ + int totalPresets = mPresetList.size(); + for (int presetNum = 0; presetNum < totalPresets; presetNum++ ) { + PresetStation station = mPresetList.get(presetNum); + if (station != null) { + if(frequency == station.getFrequency()) { + return station; + } + } + } + return null; + } + + public synchronized PresetStation addStation(String name, int freq){ + PresetStation addStation = new PresetStation(name, freq); + if(addStation != null) { + mPresetList.add(addStation); + } + return addStation; + } + + public synchronized PresetStation addStation(PresetStation station){ + PresetStation addStation = null; + if(station != null) { + addStation = new PresetStation (station); + mPresetList.add(addStation); + } + return addStation; + } + + public synchronized void removeStation(int index){ + int totalPresets = mPresetList.size(); + if((index >= 0) && (index < totalPresets)) + { + mPresetList.remove(index); + } + } + + public synchronized void removeStation(PresetStation station){ + int index = mPresetList.indexOf(station); + int totalPresets = mPresetList.size(); + if((index >= 0) && (index < totalPresets)) + { + mPresetList.remove(index); + } + } + public synchronized void clear(){ + mPresetList.clear(); + } + + /* If a user selects a new station in this list, this routine will be called to + * to update the list. + */ + public synchronized boolean setSelectedStation(PresetStation selectStation){ + int totalPresets = mPresetList.size(); + if (selectStation != null) { + for (int presetNum = 0; presetNum < totalPresets; presetNum++ ) { + PresetStation station = mPresetList.get(presetNum); + if (station != null) { + if(selectStation.getFrequency() == station.getFrequency()) { + if(selectStation.getName().equalsIgnoreCase(station.getName())) { + mCurrentStation = presetNum; + return true; + } + } + } + } + } + return false; + } + + /* Check if the same station already exists in a List + * to update the list. + */ + public synchronized boolean sameStationExists(PresetStation compareStation){ + int totalPresets = mPresetList.size(); + if (compareStation != null) { + for (int presetNum = 0; presetNum < totalPresets; presetNum++ ) { + PresetStation station = mPresetList.get(presetNum); + if (station != null) { + if(compareStation.getFrequency() == station.getFrequency()) { + return true; + } + } + } + } + return false; + } + + /* If a user selects a new station in this list, this routine will be called to + * to update the list. + */ + public synchronized boolean setSelectedStation(int stationIndex){ + boolean foundStation = false; + int totalPresets = mPresetList.size(); + if (stationIndex < totalPresets) { + mCurrentStation = stationIndex; + foundStation = true; + } + return foundStation; + } + + public synchronized PresetStation getSelectedStation(){ + int totalPresets = mPresetList.size(); + PresetStation station = null; + if (mCurrentStation < totalPresets) { + station = mPresetList.get(mCurrentStation); + } + return station; + } + + public synchronized PresetStation selectNextStation(){ + int totalPresets = mPresetList.size(); + PresetStation station = null; + if(totalPresets > 0) { + mCurrentStation ++; + if ( (mCurrentStation) >= totalPresets) { + mCurrentStation =0; + } + station = mPresetList.get(mCurrentStation); + } + return station; + } + + public synchronized PresetStation selectPrevStation(){ + int totalPresets = mPresetList.size(); + PresetStation station = null; + if(totalPresets > 0) { + mCurrentStation --; + if ( mCurrentStation < 0) { + mCurrentStation = totalPresets-1; + } + station = mPresetList.get(mCurrentStation); + } + return station; + } + + public synchronized void selectStation(PresetStation selectStation){ + int totalPresets = mPresetList.size(); + if (selectStation != null) { + for (int presetNum = 0; presetNum < totalPresets; presetNum++ ) { + PresetStation station = mPresetList.get(presetNum); + if (station != null) { + if(selectStation.getFrequency() == station.getFrequency()) { + mCurrentStation = presetNum; + return; + } + } + } + } + } + + /* Test Code */ + public void addDummyStations() { + PresetStation station; + int freq; + String name ; + int pty ; + + mPresetList.clear(); + freq = 89500; + name = "KPBS"; + pty = 22; //public + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(false); + } + + freq = 96500; + name = "KYXY"; + //pty = "Soft Rock"; + pty = 8; + + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(false); + } + + freq = 98100; + name = "KIFM"; + //pty = "Smooth Jazz"; + pty = 14; + + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(false); + } + + freq = 101500; + name = "KGB"; + //pty = "Classic Rock"; + pty = 6; + + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(false); + } + + freq = 102100; + name = "KPRI"; + //pty = "Rock"; + pty = 5; + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(true); + } + + freq = 105300; + name = "KIOZ"; + //pty = "Rock"; + pty = 5; + station = addStation(name, freq); + if(station != null) { + station.setPty(pty); + station.setPI(0); + station.setRDSSupported(true); + } + } + +} diff --git a/fmapp/src/com/codeaurora/fmradio/PresetStation.java b/fmapp/src/com/codeaurora/fmradio/PresetStation.java new file mode 100644 index 0000000..01a3ba0 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/PresetStation.java @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import qcom.fmradio.FmReceiver; + +import android.text.TextUtils; +//import android.util.Log; + +public class PresetStation +{ + private String mName = ""; + private int mFrequency = 102100; + private int mPty = 0; + private int mPI = 0; + private String mPtyStr = ""; + private String mPIStr = ""; + private boolean mRDSSupported = false; + + public PresetStation(String name, int frequency) { + mName = name; + /* + * setFrequency set the name to + * "Frequency" String if Name is empty + */ + setFrequency(frequency); + } + + public PresetStation(PresetStation station) { + Copy(station); + /* + * setFrequency set the name to + * "Frequency" String if Name is empty + */ + setFrequency(station.getFrequency()); + } + + public void Copy(PresetStation station) { + /* Let copy just do a copy + * without any manipulation + */ + mName = station.getName(); + mFrequency = station.getFrequency(); + mPI = station.getPI(); + mPty = station.getPty(); + mRDSSupported = station.getRDSSupported(); + + mPtyStr = station.getPtyString(); + mPIStr = station.getPIString(); + } + + public boolean equals(PresetStation station) { + boolean equal = false; + if (mFrequency == station.getFrequency()) + { + if (mPty == (station.getPty())) + { + if (mPI == (station.getPI())) + { + if (mRDSSupported == (station.getRDSSupported())) + { + equal = true; + } + } + } + } + return equal; + } + + public void setName(String name){ + if (!TextUtils.isEmpty(name)) + { + mName = name; + } else + { + mName = ""+mFrequency/1000.0; + } + } + + public void setFrequency(int freq){ + mFrequency = freq; + /* If no name set it to the frequency */ + if (TextUtils.isEmpty(mName)) + { + mName = ""+mFrequency/1000.0; + } + return; + } + + public void setPty(int pty){ + mPty = pty; + mPtyStr = PresetStation.parsePTY(mPty); + } + + public void setPI(int pi){ + mPI = pi; + mPIStr = PresetStation.parsePI(mPI); + } + + public void setRDSSupported(boolean rds){ + mRDSSupported = rds; + } + + public String getName(){ + return mName; + } + + public int getFrequency(){ + return mFrequency; + } + + /** + * Routine to get the Frequency in String from an integer + * + * @param frequency : Frequency to be converted (ex: 96500) + * + * @return String : Frequency in String form (ex: 96.5) + */ + public static String getFrequencyString(int frequency) { + double frequencyDbl = frequency / 1000.0; + String frequencyString =""+frequencyDbl; + return frequencyString; + } + + public int getPty(){ + return mPty; + } + + public String getPtyString(){ + return mPtyStr; + } + + public int getPI(){ + return mPI; + } + + public String getPIString(){ + return mPIStr; + } + + public boolean getRDSSupported(){ + return mRDSSupported; + } + + /** Routine parses the PI Code from Integer to Call Sign String + * Example: 0x54A6 -> KZZY + */ + public static String parsePI(int piCode) + { + String callSign = ""; + if ( (piCode >> 8) == 0xAF) + {//CALL LETTERS THAT MAP TO PI CODES = _ _ 0 0. + piCode = ((piCode & 0xFF) << 8); + } + /* Run the second exception + NOTE: For 9 special cases 1000,2000,..,9000 a double mapping + occurs utilizing exceptions 1 and 2: + 1000->A100->AFA1;2000->A200->AFA2; ... ; + 8000->A800->AFA8;9000->A900->AFA9 + */ + if ( (piCode >> 12) == 0xA) + {//CALL LETTERS THAT MAP TO PI CODES = _ 0 _ _. + piCode = ((piCode & 0xF00) << 4) + (piCode & 0xFF); + } + if ( (piCode >= 0x1000) && (piCode <= 0x994E)) + { + String ShartChar; + /* KAAA - KZZZ */ + if ( (piCode >= 0x1000) && (piCode <= 0x54A7)) + { + piCode -= 0x1000; + ShartChar = "K"; + } else + { /* WAAA - WZZZ*/ + piCode -= 0x54A8; + ShartChar = "W"; + } + int CharDiv = piCode / 26; + int CharPos = piCode - (CharDiv * 26); + char c3 = (char)('A'+CharPos); + + piCode = CharDiv; + CharDiv = piCode / 26; + CharPos = piCode - (CharDiv * 26); + char c2 = (char)('A'+CharPos); + + piCode = CharDiv; + CharDiv = piCode / 26; + CharPos = piCode - (CharDiv * 26); + char c1 = (char)('A'+CharPos); + callSign = ShartChar + c1+ c2+ c3; + } else if ( (piCode >= 0x9950) && (piCode <= 0x9EFF)) + {//3-LETTER-ONLY CALL LETTERS + callSign = get3LetterCallSign(piCode); + } else + {//NATIONALLY-LINKED RADIO STATIONS CARRYING DIFFERENT CALL LETTERS + callSign = getOtherCallSign(piCode); + } + return callSign; + } + + private static String getOtherCallSign(int piCode) + { + String callSign = ""; + if ( (piCode >= 0xB001) && (piCode <= 0xBF01)) + { + callSign = "NPR"; + } else if ( (piCode >= 0xB002) && (piCode <= 0xBF02)) + { + callSign = "CBC English"; + } else if ( (piCode >= 0xB003) && (piCode <= 0xBF03)) + { + callSign = "CBC French"; + } + return callSign; + } + + private static String get3LetterCallSign(int piCode) + { + String callSign = ""; + switch (piCode) + { + case 0x99A5: + { + callSign = "KBW"; + break; + } + case 0x9992: + { + callSign = "KOY"; + break; + } + case 0x9978: + { + callSign = "WHO"; + break; + } + case 0x99A6: + { + callSign = "KCY"; + break; + } + case 0x9993: + { + callSign = "KPQ"; + break; + } + case 0x999C: + { + callSign = "WHP"; + break; + } + case 0x9990: + { + callSign = "KDB"; + break; + } + case 0x9964: + { + callSign = "KQV"; + break; + } + case 0x999D: + { + callSign = "WIL"; + break; + } + case 0x99A7: + { + callSign = "KDF"; + break; + } + case 0x9994: + { + callSign = "KSD"; + break; + } + case 0x997A: + { + callSign = "WIP"; + break; + } + case 0x9950: + { + callSign = "KEX"; + break; + } + case 0x9965: + { + callSign = "KSL"; + break; + } + case 0x99B3: + { + callSign = "WIS"; + break; + } + case 0x9951: + { + callSign = "KFH"; + break; + } + case 0x9966: + { + callSign = "KUJ"; + break; + } + case 0x997B: + { + callSign = "WJR"; + break; + } + case 0x9952: + { + callSign = "KFI"; + break; + } + case 0x9995: + { + callSign = "KUT"; + break; + } + case 0x99B4: + { + callSign = "WJW"; + break; + } + case 0x9953: + { + callSign = "KGA"; + break; + } + case 0x9967: + { + callSign = "KVI"; + break; + } + case 0x99B5: + { + callSign = "WJZ"; + break; + } + case 0x9991: + { + callSign = "KGB"; + break; + } + case 0x9968: + { + callSign = "KWG"; + break; + } + case 0x997C: + { + callSign = "WKY"; + break; + } + case 0x9954: + { + callSign = "KGO"; + break; + } + case 0x9996: + { + callSign = "KXL"; + break; + } + case 0x997D: + { + callSign = "WLS"; + break; + } + case 0x9955: + { + callSign = "KGU"; + break; + } + case 0x9997: + { + callSign = "KXO"; + break; + } + case 0x997E: + { + callSign = "WLW"; + break; + } + case 0x9956: + { + callSign = "KGW"; + break; + } + case 0x996B: + { + callSign = "KYW"; + break; + } + case 0x999E: + { + callSign = "WMC"; + break; + } + case 0x9957: + { + callSign = "KGY"; + break; + } + case 0x9999: + { + callSign = "WBT"; + break; + } + case 0x999F: + { + callSign = "WMT"; + break; + } + case 0x99AA: + { + callSign = "KHQ"; + break; + } + case 0x996D: + { + callSign = "WBZ"; + break; + } + case 0x9981: + { + callSign = "WOC"; + break; + } + case 0x9958: + { + callSign = "KID"; + break; + } + case 0x996E: + { + callSign = "WDZ"; + break; + } + case 0x99A0: + { + callSign = "WOI"; + break; + } + case 0x9959: + { + callSign = "KIT"; + break; + } + case 0x996F: + { + callSign = "WEW"; + break; + } + case 0x9983: + { + callSign = "WOL"; + break; + } + case 0x995A: + { + callSign = "KJR"; + break; + } + case 0x999A: + { + callSign = "WGH"; + break; + } + case 0x9984: + { + callSign = "WOR"; + break; + } + case 0x995B: + { + callSign = "KLO"; + break; + } + case 0x9971: + { + callSign = "WGL"; + break; + } + case 0x99A1: + { + callSign = "WOW"; + break; + } + case 0x995C: + { + callSign = "KLZ"; + break; + } + case 0x9972: + { + callSign = "WGN"; + break; + } + case 0x99B9: + { + callSign = "WRC"; + break; + } + case 0x995D: + { + callSign = "KMA"; + break; + } + case 0x9973: + { + callSign = "WGR"; + break; + } + case 0x99A2: + { + callSign = "WRR"; + break; + } + case 0x995E: + { + callSign = "KMJ"; + break; + } + case 0x999B: + { + callSign = "WGY"; + break; + } + case 0x99A3: + { + callSign = "WSB"; + break; + } + case 0x995F: + { + callSign = "KNX"; + break; + } + case 0x9975: + { + callSign = "WHA"; + break; + } + case 0x99A4: + { + callSign = "WSM"; + break; + } + case 0x9960: + { + callSign = "KOA"; + break; + } + case 0x9976: + { + callSign = "WHB"; + break; + } + case 0x9988: + { + callSign = "WWJ"; + break; + } + case 0x99AB: + { + callSign = "KOB"; + break; + } + case 0x9977: + { + callSign = "WHK"; + break; + } + case 0x9989: + { + callSign = "WWL"; + break; + } + } + return callSign; + } + + /** + * Get the Text String for the Program type Code + */ + public static String parsePTY(int pty) + { + String ptyStr=""; + int rdsStd = FmSharedPreferences.getFMConfiguration().getRdsStd(); + if(rdsStd == FmReceiver.FM_RDS_STD_RBDS) + { + ptyStr = getRBDSPtyString(pty); + } + else if(rdsStd == FmReceiver.FM_RDS_STD_RDS) + { + ptyStr = getRDSPtyString(pty); + } + return (ptyStr); + } + + /** + * get the Text String for the RBDS Program type Code + */ + public static String getRBDSPtyString(int pty) + { + String ptyStr = ""; + switch (pty) + { + case 1: + { + ptyStr = "News"; + break; + } + case 2: + { + ptyStr = "Information"; + break; + } + case 3: + { + ptyStr = "Sports"; + break; + } + case 4: + { + ptyStr = "Talk"; + break; + } + case 5: + { + ptyStr = "Rock"; + break; + } + case 6: + { + ptyStr = "Classic Rock"; + break; + } + case 7: + { + ptyStr = "Adult Hits"; + break; + } + case 8: + { + ptyStr = "Soft Rock"; + break; + } + case 9: + { + ptyStr = "Top 40"; + break; + } + case 10: + { + ptyStr = "Country"; + break; + } + case 11: + { + ptyStr = "Oldies"; + break; + } + case 12: + { + ptyStr = "Soft"; + break; + } + case 13: + { + ptyStr = "Nostalgia"; + break; + } + case 14: + { + ptyStr = "Jazz"; + break; + } + case 15: + { + ptyStr = "Classical"; + break; + } + case 16: + { + ptyStr = "Rhythm and Blues"; + break; + } + case 17: + { + ptyStr = "Soft Rhythm and Blues"; + break; + } + case 18: + { + ptyStr = "Foreign Language"; + break; + } + case 19: + { + ptyStr = "Religious Music"; + break; + } + case 20: + { + ptyStr = "Religious Talk"; + break; + } + case 21: + { + ptyStr = "Personality"; + break; + } + case 22: + { + ptyStr = "Public"; + break; + } + case 23: + { + ptyStr = "College"; + break; + } + case 29: + { + ptyStr = "Weather"; + break; + } + case 30: + { + ptyStr = "Emergency Test"; + break; + } + case 31: + { + ptyStr = "Emergency"; + break; + } + default: + { + ptyStr = ""; + //Log.e(FMRadio.LOGTAG, "Unknown RBDS ProgramType [" + pty + "]"); + break; + } + } + return ptyStr; + } + + /** get the Text String for the Program type Code */ + public static String getRDSPtyString(int pty) + { + String ptyStr = ""; + switch (pty) + { + case 1: + { + ptyStr = "News"; + break; + } + case 2: + { + ptyStr = "Current Affairs"; + break; + } + case 3: + { + ptyStr = "Information"; + break; + } + case 4: + { + ptyStr = "Sport"; + break; + } + case 5: + { + ptyStr = "Education"; + break; + } + case 6: + { + ptyStr = "Drama"; + break; + } + case 7: + { + ptyStr = "Culture"; + break; + } + case 8: + { + ptyStr = "Science"; + break; + } + case 9: + { + ptyStr = "Varied"; + break; + } + case 10: + { + ptyStr = "Pop Music"; + break; + } + case 11: + { + ptyStr = "Rock Music"; + break; + } + case 12: + { + ptyStr = "Easy Listening Music"; + break; + } + case 13: + { + ptyStr = "Light classical"; + break; + } + case 14: + { + ptyStr = "Serious classical"; + break; + } + case 15: + { + ptyStr = "Other Music"; + break; + } + case 16: + { + ptyStr = "Weather"; + break; + } + case 17: + { + ptyStr = "Finance"; + break; + } + case 18: + { + ptyStr = "Children programs"; + break; + } + case 19: + { + ptyStr = "Social Affairs"; + break; + } + case 20: + { + ptyStr = "Religion"; + break; + } + case 21: + { + ptyStr = "Phone In"; + break; + } + case 22: + { + ptyStr = "Travel"; + break; + } + case 23: + { + ptyStr = "Leisure"; + break; + } + case 24: + { + ptyStr = "Jazz Music"; + break; + } + case 25: + { + ptyStr = "Country Music"; + break; + } + case 26: + { + ptyStr = "National Music"; + break; + } + case 27: + { + ptyStr = "Oldies Music"; + break; + } + case 28: + { + ptyStr = "Folk Music"; + break; + } + case 29: + { + ptyStr = "Documentary"; + break; + } + case 30: + { + ptyStr = "Emergency Test"; + break; + } + case 31: + { + ptyStr = "Emergency"; + break; + } + default: + { + ptyStr = ""; + //Log.e(FMRadio.LOGTAG, "Unknown RDS ProgramType [" + pty + "]"); + break; + } + } + return ptyStr; + } + + +} diff --git a/fmapp/src/com/codeaurora/fmradio/Settings.java b/fmapp/src/com/codeaurora/fmradio/Settings.java new file mode 100644 index 0000000..b721a15 --- /dev/null +++ b/fmapp/src/com/codeaurora/fmradio/Settings.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2009-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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.fmradio; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.Intent; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.preference.PreferenceScreen; +import android.preference.ListPreference; +import android.preference.CheckBoxPreference; +import android.preference.PreferenceCategory; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; + +import android.util.Log; + +public class Settings extends PreferenceActivity implements + OnSharedPreferenceChangeListener, OnPreferenceClickListener { + public static final String RX_MODE = "rx_mode"; + + + public static final String REGIONAL_BAND_KEY = "regional_band"; + public static final String AUDIO_OUTPUT_KEY = "audio_output_mode"; + public static final String RECORD_DURATION_KEY = "record_duration"; + public static final String AUTO_AF = "af_checkbox_preference"; + public static final String RESTORE_FACTORY_DEFAULT = "revert_to_fac"; + public static final int RESTORE_FACTORY_DEFAULT_INT = 1; + public static final String RESTORE_FACTORY_DEFAULT_ACTION = "com.codeaurora.fmradio.settings.revert_to_defaults"; + + private static final String LOGTAG = FMRadio.LOGTAG; + + private ListPreference mBandPreference; + private ListPreference mAudioPreference; + private ListPreference mRecordDurPreference; + private CheckBoxPreference mAfPref; + private Preference mRestoreDefaultPreference; + + private FmSharedPreferences mPrefs = null; + private boolean mRxMode = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + if (intent != null) { + mRxMode = intent.getBooleanExtra(RX_MODE, false); + } + mPrefs = new FmSharedPreferences(this); + if (mPrefs != null) { + setPreferenceScreen(createPreferenceHierarchy()); + } + } + + private PreferenceScreen createPreferenceHierarchy() { + int index = 0; + if (mPrefs == null) { + return null; + } + // Root + PreferenceScreen root = getPreferenceManager().createPreferenceScreen( + this); + + // Band/Country + String[] summaryBandItems = getResources().getStringArray( + R.array.regional_band_summary); + mBandPreference = new ListPreference(this); + mBandPreference.setEntries(R.array.regional_band_entries); + mBandPreference.setEntryValues(R.array.regional_band_values); + mBandPreference.setDialogTitle(R.string.sel_band_menu); + mBandPreference.setKey(REGIONAL_BAND_KEY); + mBandPreference.setTitle(R.string.regional_band); + index = FmSharedPreferences.getCountry(); + Log.d(LOGTAG, "createPreferenceHierarchy: Country: " + index); + // Get the preference and list the value. + if ((index < 0) || (index >= summaryBandItems.length)) { + index = 0; + } + Log.d(LOGTAG, "createPreferenceHierarchy: CountrySummary: " + + summaryBandItems[index]); + mBandPreference.setSummary(summaryBandItems[index]); + mBandPreference.setValueIndex(index); + root.addPreference(mBandPreference); + + if (mRxMode) { + // Audio Output (Stereo or Mono) + String[] summaryAudioModeItems = getResources().getStringArray( + R.array.ster_mon_entries); + mAudioPreference = new ListPreference(this); + mAudioPreference.setEntries(R.array.ster_mon_entries); + mAudioPreference.setEntryValues(R.array.ster_mon_values); + mAudioPreference.setDialogTitle(R.string.sel_audio_output); + mAudioPreference.setKey(AUDIO_OUTPUT_KEY); + mAudioPreference.setTitle(R.string.aud_output_mode); + boolean audiomode = FmSharedPreferences.getAudioOutputMode(); + if (audiomode) { + index = 0; + } else { + index = 1; + } + Log.d(LOGTAG, "createPreferenceHierarchy: audiomode: " + audiomode); + mAudioPreference.setSummary(summaryAudioModeItems[index]); + mAudioPreference.setValueIndex(index); + root.addPreference(mAudioPreference); + + // AF Auto Enable (Checkbox) + mAfPref = new CheckBoxPreference(this); + mAfPref.setKey(AUTO_AF); + mAfPref.setTitle(R.string.auto_select_af); + mAfPref.setSummaryOn(R.string.auto_select_af_enabled); + mAfPref.setSummaryOff(R.string.auto_select_af_disabled); + boolean bAFAutoSwitch = FmSharedPreferences.getAutoAFSwitch(); + Log.d(LOGTAG, "createPreferenceHierarchy: bAFAutoSwitch: " + + bAFAutoSwitch); + mAfPref.setChecked(bAFAutoSwitch); + root.addPreference(mAfPref); + + + if(FMRadio.RECORDING_ENABLE) + { + String[] summaryRecordItems = getResources().getStringArray( + R.array.record_durations_entries); + int nRecordDuration = 0; + mRecordDurPreference = new ListPreference(this); + mRecordDurPreference.setEntries(R.array.record_durations_entries); + mRecordDurPreference.setEntryValues(R.array.record_duration_values); + mRecordDurPreference.setDialogTitle(R.string.sel_rec_dur); + mRecordDurPreference.setKey(RECORD_DURATION_KEY); + mRecordDurPreference.setTitle(R.string.record_dur); + nRecordDuration = FmSharedPreferences.getRecordDuration(); + Log + .d(LOGTAG, "createPreferenceHierarchy: recordDuration: " + + nRecordDuration); + switch( nRecordDuration ) { + case FmSharedPreferences.RECORD_DUR_INDEX_0_VAL: + index =0; + break; + case FmSharedPreferences.RECORD_DUR_INDEX_1_VAL: + index =1; + break; + case FmSharedPreferences.RECORD_DUR_INDEX_2_VAL: + index =2; + break; + case FmSharedPreferences.RECORD_DUR_INDEX_3_VAL: + index =3; + break; + } + // Get the preference and list the value. + if ((index < 0) || (index >= summaryRecordItems.length)) { + index = 0; + } + Log.d(LOGTAG, "createPreferenceHierarchy: recordDurationSummary: " + + summaryRecordItems[index]); + mRecordDurPreference.setSummary(summaryRecordItems[index]); + mRecordDurPreference.setValueIndex(index); + root.addPreference(mRecordDurPreference); + } + } + + // Add a new category + PreferenceCategory prefCat = new PreferenceCategory(this); + root.addPreference(prefCat); + + mRestoreDefaultPreference = new Preference(this); + mRestoreDefaultPreference + .setTitle(R.string.settings_revert_defaults_title); + mRestoreDefaultPreference.setKey(RESTORE_FACTORY_DEFAULT); + mRestoreDefaultPreference + .setSummary(R.string.settings_revert_defaults_summary); + mRestoreDefaultPreference.setOnPreferenceClickListener(this); + root.addPreference(mRestoreDefaultPreference); + return root; + } + + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + int mTunedFreq = 0; + boolean bStatus = false; + if (key.equals(REGIONAL_BAND_KEY)) { + int curListIndex = FmSharedPreferences.getCurrentListIndex(); + PresetList curList = FmSharedPreferences.getStationList(curListIndex); + String[] summaryBandItems = getResources().getStringArray( + R.array.regional_band_summary); + String valueStr = sharedPreferences.getString(key, ""); + int index = 0; + if (valueStr != null) { + index = mBandPreference.findIndexOfValue(valueStr); + } + if ((index < 0) || (index >= summaryBandItems.length)) { + index = 0; + mBandPreference.setValueIndex(0); + } + Log.d(LOGTAG, "onSharedPreferenceChanged: Country Change: " + + index); + mBandPreference.setSummary(summaryBandItems[index]); + FmSharedPreferences.setCountry(index); + bStatus = FMRadio.fmConfigure(); + FMTransmitterActivity.fmConfigure(); + if (curList != null) { + curList.clear(); + } + } else { + if (mRxMode) { + if (key.equals(AUTO_AF)) { + boolean bAFAutoSwitch = mAfPref.isChecked(); + Log.d(LOGTAG, "onSharedPreferenceChanged: Auto AF Enable: " + + bAFAutoSwitch); + FmSharedPreferences.setAutoAFSwitch(bAFAutoSwitch); + FMRadio.fmAutoAFSwitch(); + mPrefs.Save(); + } else if (key.equals(RECORD_DURATION_KEY)) { + if(FMRadio.RECORDING_ENABLE) + { + String[] recordItems = getResources().getStringArray( + R.array.record_durations_entries); + String valueStr = mRecordDurPreference.getValue(); + int index = 0; + if (valueStr != null) { + index = mRecordDurPreference.findIndexOfValue(valueStr); + } + if ((index < 0) || (index >= recordItems.length)) { + index = 0; + mRecordDurPreference.setValueIndex(index); + } + Log.d(LOGTAG, "onSharedPreferenceChanged: recorddur: " + + recordItems[index]); + mRecordDurPreference.setSummary(recordItems[index]); + FmSharedPreferences.setRecordDuration(index); + } + } else if (key.equals(AUDIO_OUTPUT_KEY)) { + String[] bandItems = getResources().getStringArray( + R.array.ster_mon_entries); + String valueStr = mAudioPreference.getValue(); + int index = 0; + if (valueStr != null) { + index = mAudioPreference.findIndexOfValue(valueStr); + } + if (index != 1) { + if (index != 0) { + index = 0; + /* It shud be 0(Stereo) or 1(Mono) */ + mAudioPreference.setValueIndex(index); + } + } + Log.d(LOGTAG, "onSharedPreferenceChanged: audiomode: " + + bandItems[index]); + mAudioPreference.setSummary(bandItems[index]); + if (index == 0) { + // Stereo + FmSharedPreferences.setAudioOutputMode(true); + } else { + // Mono + FmSharedPreferences.setAudioOutputMode(false); + } + FMRadio.fmAudioOutputMode(); + } + } + } + if (mPrefs != null) + { + if(bStatus) + mPrefs.Save(); + else { + mTunedFreq = FmSharedPreferences.getTunedFrequency(); + if (mTunedFreq > FmSharedPreferences.getUpperLimit() || mTunedFreq < FmSharedPreferences.getLowerLimit()) { + FmSharedPreferences.setTunedFrequency(FmSharedPreferences.getLowerLimit()); + } + mPrefs.Save(); + } + } + } + + public boolean onPreferenceClick(Preference preference) { + boolean handled = false; + if (preference == mRestoreDefaultPreference) { + showDialog(RESTORE_FACTORY_DEFAULT_INT); + } + return handled; + } + + @Override + protected Dialog onCreateDialog(int id) { + switch (id) { + case RESTORE_FACTORY_DEFAULT_INT: + return new AlertDialog.Builder(this).setIcon( + R.drawable.alert_dialog_icon).setTitle( + R.string.settings_revert_confirm_title).setMessage( + R.string.settings_revert_confirm_msg).setPositiveButton( + R.string.alert_dialog_ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + Intent data = new Intent( + RESTORE_FACTORY_DEFAULT_ACTION); + setResult(RESULT_OK, data); + restoreSettingsDefault(); + finish(); + } + + }).setNegativeButton(R.string.alert_dialog_cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, + int whichButton) { + } + }).create(); + default: + break; + } + return null; + } + + private void restoreSettingsDefault() { + if (mPrefs != null) { + mBandPreference.setValueIndex(0); + if (mRxMode) { + mAudioPreference.setValueIndex(0); + if(FMRadio.RECORDING_ENABLE) + { + mRecordDurPreference.setValueIndex(0); + } + mAfPref.setChecked(false); + FmSharedPreferences.SetDefaults(); + } + else + { + FmSharedPreferences.setCountry(FmSharedPreferences.REGIONAL_BAND_NORTH_AMERICA); + } + mPrefs.Save(); + } + } + + @Override + protected void onResume() { + super.onResume(); + PreferenceScreen preferenceScreen = getPreferenceScreen(); + SharedPreferences sharedPreferences = null; + if (preferenceScreen != null) { + sharedPreferences = preferenceScreen.getSharedPreferences(); + } + if (sharedPreferences != null) { + sharedPreferences.registerOnSharedPreferenceChangeListener(this); + } + } + + @Override + protected void onPause() { + super.onPause(); + PreferenceScreen preferenceScreen = getPreferenceScreen(); + SharedPreferences sharedPreferences = null; + if (preferenceScreen != null) { + sharedPreferences = preferenceScreen.getSharedPreferences(); + } + if (sharedPreferences != null) { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + } + } + +} diff --git a/fmapp/src/com/codeaurora/hc_utils/A2dpDeviceStatus.java b/fmapp/src/com/codeaurora/hc_utils/A2dpDeviceStatus.java new file mode 100644 index 0000000..46bcecd --- /dev/null +++ b/fmapp/src/com/codeaurora/hc_utils/A2dpDeviceStatus.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011-2012, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.utils; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothProfile; +import android.content.Intent; +import android.content.Context; + +public class A2dpDeviceStatus { + private BluetoothAdapter mAdapter; + private BluetoothA2dp mA2dp = null; + public String getActionSinkStateChangedString (){ + return BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED; + } + public String getActionPlayStateChangedString (){ + return BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED; + } + public boolean isA2dpStateChange( String action) { + if(action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED) ) { + return true; + } + return false; + } + public boolean isA2dpPlayStateChange( String action) { + if(action.equals(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED) ) { + return true; + } + return false; + } + public boolean isConnected(Intent intent) { + boolean isConnected = false; + int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, + BluetoothA2dp.STATE_DISCONNECTED); + if((state == BluetoothA2dp.STATE_CONNECTED) || (state == BluetoothProfile.STATE_CONNECTED)) { + isConnected = true; + } + return isConnected; + } + public boolean isPlaying(Intent intent) { + boolean isPlaying = false; + int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, + BluetoothA2dp.STATE_DISCONNECTED); + if(state == BluetoothA2dp.STATE_PLAYING ) { + isPlaying = true; + } + return isPlaying; + } + public boolean isDeviceAvailable() { + if(null != mA2dp) { + if( 0 != mA2dp.getConnectedDevices().size() ) { + return true; + } + } + return false; + } + + public A2dpDeviceStatus(Context mContext) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mAdapter.getProfileProxy(mContext, mProfileListener, + BluetoothProfile.A2DP); + } + private BluetoothProfile.ServiceListener mProfileListener = + new BluetoothProfile.ServiceListener() { + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.A2DP) { + mA2dp = (BluetoothA2dp) proxy; + } + } + public void onServiceDisconnected(int profile) { + if (profile == BluetoothProfile.A2DP) { + mA2dp = null; + } + } +}; +} + diff --git a/fmapp/src/com/codeaurora/hc_utils/FrequencyPicker.java b/fmapp/src/com/codeaurora/hc_utils/FrequencyPicker.java new file mode 100644 index 0000000..8a1deaa --- /dev/null +++ b/fmapp/src/com/codeaurora/hc_utils/FrequencyPicker.java @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * 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.codeaurora.utils; + + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Log; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.codeaurora.fmradio.R; +import android.widget.NumberPicker; +import android.widget.NumberPicker.OnValueChangeListener; + +/** + * A view for selecting the frequency + * + * For a dialog using this view, see {FrequencyPickerDialog}. + */ + +public class FrequencyPicker extends FrameLayout { + + /* UI Components */ + private final NumberPicker mMHzPicker; + private final NumberPicker mKHzPicker; + + /** + * How we notify users the Frequency has changed. + */ + private OnFrequencyChangedListener mOnFrequencyChangedListener; + + private int mFrequency; + private int mMin; + private int mMax; + private int mStep; + private int mMhz; + private int mKhz; + + /** + * The callback used to indicate the user changes the Frequency. + */ + public interface OnFrequencyChangedListener { + + /** + * @param view The view associated with this listener. + * @param frequency The Frequency that was set. + */ + void onFrequencyChanged(FrequencyPicker view, int frequency); + } + + public FrequencyPicker(Context context) { + this(context, null); + } + + public FrequencyPicker(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FrequencyPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.frequency_picker, this, true); + + mMHzPicker = (NumberPicker) findViewById(R.id.mhz); + if (mMHzPicker != null) { + mMHzPicker.setOnLongPressUpdateInterval(100); + + mMHzPicker.setOnValueChangedListener(new OnValueChangeListener() { + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mMhz = newVal; + mFrequency = (mMhz*1000) + (getFrequencyKHz(mKhz, mMin, mStep)); + validateFrequencyRange(); + if (mOnFrequencyChangedListener != null) { + mOnFrequencyChangedListener.onFrequencyChanged(FrequencyPicker.this, mFrequency); + } + } + }); + } + mKHzPicker = (NumberPicker) findViewById(R.id.khz); + if (mKHzPicker != null) { + mKHzPicker.setOnLongPressUpdateInterval(100); + mKHzPicker.setOnValueChangedListener(new OnValueChangeListener() { + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + mKhz = newVal; + mFrequency = (mMhz*1000) + (getFrequencyKHz(mKhz, mMin, mStep)); + + validateFrequencyRange(); + + if (mOnFrequencyChangedListener != null) { + mOnFrequencyChangedListener.onFrequencyChanged(FrequencyPicker.this, mFrequency); + } + } + }); + } + + updateSpinnerRange(); + + if (!isEnabled()) { + setEnabled(false); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mMHzPicker.setEnabled(enabled); + mKHzPicker.setEnabled(enabled); + } + + + public void updateFrequency(int frequency) { + mFrequency = frequency; + updateSpinners(); + } + + private static class SavedState extends BaseSavedState { + + private final int mMHZ; + private final int mKHZ; + + /** + * Constructor called from {@link FrequencyPicker#onSaveInstanceState()} + */ + private SavedState(Parcelable superState, int mhz, int khz) { + super(superState); + mMHZ = mhz; + mKHZ = khz; + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + mMHZ = in.readInt(); + mKHZ = in.readInt(); + } + + public int getMHz() { + return mMHZ; + } + + public int getKHz() { + return mKHZ; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mMHZ); + dest.writeInt(mKHZ); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Creator<SavedState>() { + + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + + /** + * Override so we are in complete control of save / restore for this widget. + */ + @Override + protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { + dispatchThawSelfOnly(container); + } + + @Override + protected Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + return new SavedState(superState, mMhz, mKhz); + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mMhz = ss.getMHz(); + mKhz = ss.getKHz(); + } + + private String[] getKHzStrings(int min, int stepSize) + { + if(stepSize == 100) + { + return (get100KHzStrings()); + } + else if(stepSize == 50) + { + return (get50KHzStrings()); + } + return (get200KHzStrings(min)); + } + + private int getKHzCount(int stepSize) + { + if(stepSize == 100) + { + return (10); + } + else if(stepSize == 50) + { + return (20); + } + return (5); + } + private int getCurrentKHz(int frequency, int min, int stepSize) + { + if(stepSize == 100) + { + return (getCurrent100KHz(frequency)); + } + else if(stepSize == 50) + { + return (getCurrent50KHz(frequency)); + } + return (getCurrent200KHz(frequency, min)); + } + + private int getFrequencyKHz(int kHz, int min, int stepSize) + { + if(stepSize == 100) + { + return (getFrequency100KHz(kHz)); + } + else if(stepSize == 50) + { + return (getFrequency50KHz(kHz)); + } + return (getFrequency200KHz(kHz, min)); + } + + private int getFrequency100KHz(int kHz) + { + int frequencykhz = ((kHz-1)*100) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getFrequency50KHz(int kHz) + { + int frequencykhz = ((kHz-1)*50) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getFrequency200KHz(int kHz, int min) + { + int frequencykhz = ((kHz-1)*200) ; + if(min%200 != 0) + { + frequencykhz = ((kHz-1)*200)+100 ; + } + //Log.d("FMRadio", "FP: getCurrent200KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getCurrent100KHz(int frequency) + { + int khz = ((frequency%1000)/100) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + khz); + return (khz+1); + } + + private int getCurrent50KHz(int frequency) + { + int khz = ((frequency%1000)/50) ; + //Log.d("FMRadio", "FP: getCurrent50KHz: " + khz); + return (khz+1); + } + + private int getCurrent200KHz(int frequency, int min) + { + int khz = ((frequency%1000)/200); + //Log.d("FMRadio", "FP: getCurrent200KHz: " + khz); + return (khz+1); + } + + private String[] get50KHzStrings() + { + String[] khzStrings= {"00","05","10","15","20","25","30","35","40","45", + "50","55","60","65","70","75","80","85","90","95"}; + //Log.d("FMRadio", "FP: get50KHzStrings"); + return khzStrings; + } + + private String[] get100KHzStrings() + { + String[] khzStrings= {"0","1","2","3","4","5", + "6","7","8","9"}; + //Log.d("FMRadio", "FP: get100KHzStrings"); + return khzStrings; + } + + private String[] get200KHzStrings(int min) + { + if(min%200 == 0) + { + return (getEven200KHzStrings()); + } + return (getOdd200KHzStrings()); + } + + private String[] getEven200KHzStrings() + { + String[] khzStrings= {"0","2","4","6","8"}; + //Log.d("FMRadio", "FP: getEven200KHzStrings"); + return khzStrings; + } + private String[] getOdd200KHzStrings() + { + String[] khzStrings= {"1","3","5","7","9"}; + //Log.d("FMRadio", "FP: getOdd200KHzStrings"); + return khzStrings; + } + public void updateSteps(int steps) + { + mStep = steps; + } + public void updateMinFreq(int freq) + { + mMin = freq; + } + public void updateMaxFreq(int freq) + { + mMax = freq; + } + /** + * Initialize the state. + * @param year The initial year. + * @param monthOfYear The initial month. + * @param dayOfMonth The initial day of the month. + * @param onDateChangedListener How user is notified date is changed by user, can be null. + */ + public void init(int min, int max, int step, int frequency, + OnFrequencyChangedListener onFrequencyChangedListener) { + + mMin = min; + mMax = max; + mStep = step; + mFrequency = frequency; + mOnFrequencyChangedListener = onFrequencyChangedListener; + + updateSpinners(); + } + private void updateSpinnerRange() { + String[] khzStrings = getKHzStrings(mMin, mStep); + int khzNumSteps = getKHzCount(mStep); + + if (mMHzPicker != null) { + mMHzPicker.setMinValue(mMin/1000); + mMHzPicker.setMaxValue(mMax/1000); + } + if (mKHzPicker != null) { + mKHzPicker.setDisplayedValues(get50KHzStrings()); + mKHzPicker.setMinValue(1); + mKHzPicker.setMaxValue(khzNumSteps); + mKHzPicker.setDisplayedValues(khzStrings); + } + } + + private void updateSpinners() { + int khzNumSteps = getKHzCount(mStep); + updateSpinnerRange(); + mMhz = (int)(mFrequency/1000); + mKhz = getCurrentKHz(mFrequency, mMin, mStep) ; + if((mMin/1000 <=mMhz) && (mMax/1000 >=mMhz)) + { + mMHzPicker.setValue(mMhz); + } + if(mKhz <= khzNumSteps) + { + mKHzPicker.setValue(mKhz); + } + } + private void validateFrequencyRange() + { + boolean bUpdateSpinner=false; + if(mFrequency < mMin) + { + mFrequency = mMin; + bUpdateSpinner = true; + } + if(mFrequency > mMax) + { + mFrequency = mMax; + bUpdateSpinner = true; + } + if(bUpdateSpinner == true) + { + updateSpinners(); + } + } + + public int getFrequency() { + return (mFrequency); + } +} diff --git a/fmapp/src/com/codeaurora/hc_utils/FrequencyPickerDialog.java b/fmapp/src/com/codeaurora/hc_utils/FrequencyPickerDialog.java new file mode 100644 index 0000000..06ac7b3 --- /dev/null +++ b/fmapp/src/com/codeaurora/hc_utils/FrequencyPickerDialog.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * Copyright (c) 2012, The Linux Foundation. All rights reserved. + * Not a Contribution. + * 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.codeaurora.utils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import qcom.fmradio.FmConfig; +import qcom.fmradio.FmReceiver; + +import com.codeaurora.fmradio.R; +import com.codeaurora.utils.FrequencyPicker.OnFrequencyChangedListener; + +/** + * A simple dialog containing an FrequencyPicker. + */ +public class FrequencyPickerDialog extends AlertDialog implements OnClickListener, +OnFrequencyChangedListener { + + private static final String FREQUENCY = "FREQUENCY"; + private static final String FREQ_MIN = "FREQ_MIN"; + private static final String FREQ_MAX = "FREQ_MAX"; + private static final String FREQ_STEP = "FREQ_STEP"; + + private final FrequencyPicker mFrequencyPicker; + private final OnFrequencySetListener mCallBack; + private int mMinFrequency; + private int mMaxFrequency; + private int mChannelSpacing; + + /** + * The callback used to indicate the user is done filling in the date. + */ + public interface OnFrequencySetListener { + + void onFrequencySet(FrequencyPicker view, int frequency); + } + /** + */ + public FrequencyPickerDialog(Context context, + FmConfig fmConfig, + int frequency, + OnFrequencySetListener callback) { + //this(context, android.R.style.Theme_Dialog, fmConfig, frequency, callback); + this(context, com.android.internal.R.style.Theme_Dialog_Alert, fmConfig, frequency, callback); + } + + /** + */ + public FrequencyPickerDialog(Context context, + int theme, + FmConfig fmConfig, + int frequency, + OnFrequencySetListener callback) { + super(context, theme); + mMinFrequency = fmConfig.getLowerLimit(); + mMaxFrequency = fmConfig.getUpperLimit(); + mChannelSpacing = 200; + if(FmReceiver.FM_CHSPACE_200_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 200; + } + else if(FmReceiver.FM_CHSPACE_100_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 100; + } + else if(FmReceiver.FM_CHSPACE_50_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 50; + } + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + mCallBack = callback; + + setButton("Set", this); + setButton2("Cancel", (OnClickListener) null); + setIcon(R.drawable.alert_dialog_icon); + + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.frequency_picker_dialog, null); + setView(view); + mFrequencyPicker = (FrequencyPicker) view.findViewById(R.id.frequencyPicker); + if(mFrequencyPicker != null) + { + mFrequencyPicker.init(mMinFrequency, mMaxFrequency, mChannelSpacing, frequency, this); + } + else + { + Log.e("fmRadio", "Failed to find ID: R.id.frequencyPicker"); + } + } + + public void UpdateFrequency(int frequency) + { + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + mFrequencyPicker.updateFrequency(frequency); + } + + public void onClick(DialogInterface dialog, int which) { + if (mCallBack != null) { + mFrequencyPicker.clearFocus(); + int frequency = mFrequencyPicker.getFrequency(); + mCallBack.onFrequencySet(mFrequencyPicker, frequency); + } + } + + public void onFrequencyChanged(FrequencyPicker view, int frequency) { + UpdateFrequency(frequency); + } + + @Override + public Bundle onSaveInstanceState() { + Bundle state = super.onSaveInstanceState(); + state.putInt(FREQUENCY, mFrequencyPicker.getFrequency()); + state.putInt(FREQ_MIN, mMinFrequency); + state.putInt(FREQ_MAX, mMaxFrequency); + state.putInt(FREQ_STEP, mChannelSpacing); + return state; + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + int frequency = savedInstanceState.getInt(FREQUENCY); + mMinFrequency = savedInstanceState.getInt(FREQ_MIN); + mMaxFrequency = savedInstanceState.getInt(FREQ_MAX ); + mChannelSpacing = savedInstanceState.getInt(FREQ_STEP); + mFrequencyPicker.init(mMinFrequency, mMaxFrequency, mChannelSpacing, frequency, this); + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + } + public void updateSteps(int steps) + { + mChannelSpacing = 200; + if(FmReceiver.FM_CHSPACE_200_KHZ == steps) + { + mChannelSpacing = 200; + } + else if(FmReceiver.FM_CHSPACE_100_KHZ == steps) + { + mChannelSpacing = 100; + } + else if(FmReceiver.FM_CHSPACE_50_KHZ == steps) + { + mChannelSpacing = 50; + } + mFrequencyPicker.updateSteps(mChannelSpacing); + } + public void updateMinFreq(int freq) + { + mMinFrequency = freq; + mFrequencyPicker.updateMinFreq(mMinFrequency); + } + public void updateMaxFreq(int freq) + { + mMaxFrequency = freq; + mFrequencyPicker.updateMaxFreq(mMaxFrequency); + } +} diff --git a/fmapp/src/com/codeaurora/utils/A2dpDeviceStatus.java b/fmapp/src/com/codeaurora/utils/A2dpDeviceStatus.java new file mode 100644 index 0000000..b6d5a88 --- /dev/null +++ b/fmapp/src/com/codeaurora/utils/A2dpDeviceStatus.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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.codeaurora.utils; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.content.Intent; + +import java.util.Set; + + +public class A2dpDeviceStatus { + private BluetoothA2dp mA2dp = null; + public String getActionSinkStateChangedString (){ + return BluetoothA2dp.ACTION_SINK_STATE_CHANGED; + } + public String getActionPlayStateChangedString (){ + return BluetoothA2dp.ACTION_SINK_STATE_CHANGED; + } + public boolean isA2dpStateChange( String action) { + if(action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ) { + return true; + } + return false; + } + public boolean isA2dpPlayStateChange( String action) { + return isA2dpStateChange(action); + } + public boolean isConnected(Intent intent) { + boolean isConnected = false; + int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, + BluetoothA2dp.STATE_DISCONNECTED); + if (state == BluetoothA2dp.STATE_CONNECTED || + state == BluetoothA2dp.STATE_PLAYING){ + isConnected = true; + } + return isConnected; + } + public boolean isPlaying(Intent intent) { + boolean isPlaying = false; + int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, + BluetoothA2dp.STATE_DISCONNECTED); + if(state == BluetoothA2dp.STATE_PLAYING){ + isPlaying = true; + } + return isPlaying; + } + public boolean isDeviceAvailable() { + if(null == mA2dp) return false; + Set<BluetoothDevice> sinks = mA2dp.getConnectedSinks(); + if (sinks != null && sinks.size() != 0) { + return true; + } + return false; + } + + public A2dpDeviceStatus(Context mContext) { + mA2dp = new BluetoothA2dp(mContext); + } + +} diff --git a/fmapp/src/com/codeaurora/utils/FrequencyPicker.java b/fmapp/src/com/codeaurora/utils/FrequencyPicker.java new file mode 100644 index 0000000..845ed90 --- /dev/null +++ b/fmapp/src/com/codeaurora/utils/FrequencyPicker.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2007 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.codeaurora.utils; + + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +//import android.util.Log; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.codeaurora.fmradio.R; +import android.widget.NumberPicker; +import android.widget.NumberPicker.OnChangedListener; + +/** + * A view for selecting the frequency + * + * For a dialog using this view, see {FrequencyPickerDialog}. + */ + +public class FrequencyPicker extends FrameLayout { + + /* UI Components */ + private final NumberPicker mMHzPicker; + private final NumberPicker mKHzPicker; + + /** + * How we notify users the Frequency has changed. + */ + private OnFrequencyChangedListener mOnFrequencyChangedListener; + + private int mFrequency; + private int mMin; + private int mMax; + private int mStep; + private int mMhz; + private int mKhz; + + /** + * The callback used to indicate the user changes the Frequency. + */ + public interface OnFrequencyChangedListener { + + /** + * @param view The view associated with this listener. + * @param frequency The Frequency that was set. + */ + void onFrequencyChanged(FrequencyPicker view, int frequency); + } + + public FrequencyPicker(Context context) { + this(context, null); + } + + public FrequencyPicker(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FrequencyPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.frequency_picker, this, true); + + mMHzPicker = (NumberPicker) findViewById(R.id.mhz); + if (mMHzPicker != null) { + mMHzPicker.setSpeed(100); + mMHzPicker.setOnChangeListener(new OnChangedListener() { + public void onChanged(NumberPicker picker, int oldVal, int newVal) { + mMhz = newVal; + mFrequency = (mMhz*1000) + (getFrequencyKHz(mKhz, mMin, mStep)); + validateFrequencyRange(); + if (mOnFrequencyChangedListener != null) { + mOnFrequencyChangedListener.onFrequencyChanged(FrequencyPicker.this, mFrequency); + } + } + }); + } + mKHzPicker = (NumberPicker) findViewById(R.id.khz); + if (mKHzPicker != null) { + mKHzPicker.setSpeed(100); + mKHzPicker.setOnChangeListener(new OnChangedListener() { + public void onChanged(NumberPicker picker, int oldVal, int newVal) { + mKhz = newVal; + mFrequency = (mMhz*1000) + (getFrequencyKHz(mKhz, mMin, mStep)); + + validateFrequencyRange(); + + if (mOnFrequencyChangedListener != null) { + mOnFrequencyChangedListener.onFrequencyChanged(FrequencyPicker.this, mFrequency); + } + } + }); + } + + updateSpinnerRange(); + + if (!isEnabled()) { + setEnabled(false); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (mMHzPicker != null) { + mMHzPicker.setEnabled(enabled); + } + if (mKHzPicker != null) { + mKHzPicker.setEnabled(enabled); + } + } + + + public void updateFrequency(int frequency) { + mFrequency = frequency; + updateSpinners(); + } + + private static class SavedState extends BaseSavedState { + + private final int mMHZ; + private final int mKHZ; + + /** + * Constructor called from {@link FrequencyPicker#onSaveInstanceState()} + */ + private SavedState(Parcelable superState, int mhz, int khz) { + super(superState); + mMHZ = mhz; + mKHZ = khz; + } + + /** + * Constructor called from {@link #CREATOR} + */ + private SavedState(Parcel in) { + super(in); + mMHZ = in.readInt(); + mKHZ = in.readInt(); + } + + public int getMHz() { + return mMHZ; + } + + public int getKHz() { + return mKHZ; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mMHZ); + dest.writeInt(mKHZ); + } + + public static final Parcelable.Creator<SavedState> CREATOR = + new Creator<SavedState>() { + + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + + /** + * Override so we are in complete control of save / restore for this widget. + */ + @Override + protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { + dispatchThawSelfOnly(container); + } + + @Override + protected Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + return new SavedState(superState, mMhz, mKhz); + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mMhz = ss.getMHz(); + mKhz = ss.getKHz(); + } + + private String[] getKHzStrings(int min, int stepSize) + { + if(stepSize == 100) + { + return (get100KHzStrings()); + } + else if(stepSize == 50) + { + return (get50KHzStrings()); + } + return (get200KHzStrings(min)); + } + + private int getKHzCount(int stepSize) + { + if(stepSize == 100) + { + return (10); + } + else if(stepSize == 50) + { + return (20); + } + return (5); + } + private int getCurrentKHz(int frequency, int min, int stepSize) + { + if(stepSize == 100) + { + return (getCurrent100KHz(frequency)); + } + else if(stepSize == 50) + { + return (getCurrent50KHz(frequency)); + } + return (getCurrent200KHz(frequency, min)); + } + + private int getFrequencyKHz(int kHz, int min, int stepSize) + { + if(stepSize == 100) + { + return (getFrequency100KHz(kHz)); + } + else if(stepSize == 50) + { + return (getFrequency50KHz(kHz)); + } + return (getFrequency200KHz(kHz, min)); + } + + private int getFrequency100KHz(int kHz) + { + int frequencykhz = ((kHz-1)*100) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getFrequency50KHz(int kHz) + { + int frequencykhz = ((kHz-1)*50) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getFrequency200KHz(int kHz, int min) + { + int frequencykhz = ((kHz-1)*200) ; + if(min%200 != 0) + { + frequencykhz = ((kHz-1)*200)+100 ; + } + //Log.d("FMRadio", "FP: getCurrent200KHz: " + frequencykhz); + return (frequencykhz); + } + + private int getCurrent100KHz(int frequency) + { + int khz = ((frequency%1000)/100) ; + //Log.d("FMRadio", "FP: getCurrent100KHz: " + khz); + return (khz+1); + } + + private int getCurrent50KHz(int frequency) + { + int khz = ((frequency%1000)/50) ; + //Log.d("FMRadio", "FP: getCurrent50KHz: " + khz); + return (khz+1); + } + + private int getCurrent200KHz(int frequency, int min) + { + int khz = ((frequency%1000)/200); + //Log.d("FMRadio", "FP: getCurrent200KHz: " + khz); + return (khz+1); + } + + private String[] get50KHzStrings() + { + String[] khzStrings= {"00","05","10","15","20","25","30","35","40","45", + "50","55","60","65","70","75","80","85","90","95"}; + //Log.d("FMRadio", "FP: get50KHzStrings"); + return khzStrings; + } + + private String[] get100KHzStrings() + { + String[] khzStrings= {"0","1","2","3","4","5", + "6","7","8","9"}; + //Log.d("FMRadio", "FP: get100KHzStrings"); + return khzStrings; + } + + private String[] get200KHzStrings(int min) + { + if(min%200 == 0) + { + return (getEven200KHzStrings()); + } + return (getOdd200KHzStrings()); + } + + private String[] getEven200KHzStrings() + { + String[] khzStrings= {"0","2","4","6","8"}; + //Log.d("FMRadio", "FP: getEven200KHzStrings"); + return khzStrings; + } + private String[] getOdd200KHzStrings() + { + String[] khzStrings= {"1","3","5","7","9"}; + //Log.d("FMRadio", "FP: getOdd200KHzStrings"); + return khzStrings; + } + + /** + * Initialize the state. + * @param year The initial year. + * @param monthOfYear The initial month. + * @param dayOfMonth The initial day of the month. + * @param onDateChangedListener How user is notified date is changed by user, can be null. + */ + public void init(int min, int max, int step, int frequency, + OnFrequencyChangedListener onFrequencyChangedListener) { + + mMin = min; + mMax = max; + mStep = step; + mFrequency = frequency; + mOnFrequencyChangedListener = onFrequencyChangedListener; + + updateSpinners(); + } + private void updateSpinnerRange() { + String[] khzStrings = getKHzStrings(mMin, mStep); + int khzNumSteps = getKHzCount(mStep); + + if (mMHzPicker != null) { + mMHzPicker.setRange(mMin/1000, mMax/1000); + } + if (mKHzPicker != null) { + mKHzPicker.setRange(1, khzNumSteps, khzStrings); + } + } + + private void updateSpinners() { + int khzNumSteps = getKHzCount(mStep); + updateSpinnerRange(); + mMhz = (int)(mFrequency/1000); + mKhz = getCurrentKHz(mFrequency, mMin, mStep) ; + if((mMin/1000 <=mMhz) && (mMax/1000 >=mMhz)) + { + mMHzPicker.setCurrent(mMhz); + } + if(mKhz <= khzNumSteps) + { + mKHzPicker.setCurrent(mKhz); + } + } + private void validateFrequencyRange() + { + boolean bUpdateSpinner=false; + if(mFrequency < mMin) + { + mFrequency = mMin; + bUpdateSpinner = true; + } + if(mFrequency > mMax) + { + mFrequency = mMax; + bUpdateSpinner = true; + } + if(bUpdateSpinner == true) + { + updateSpinners(); + } + } + + public int getFrequency() { + return (mFrequency); + } +} diff --git a/fmapp/src/com/codeaurora/utils/FrequencyPickerDialog.java b/fmapp/src/com/codeaurora/utils/FrequencyPickerDialog.java new file mode 100644 index 0000000..6078208 --- /dev/null +++ b/fmapp/src/com/codeaurora/utils/FrequencyPickerDialog.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2007 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.codeaurora.utils; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import qcom.fmradio.FmConfig; +import qcom.fmradio.FmReceiver; + +import com.codeaurora.fmradio.R; +import com.codeaurora.utils.FrequencyPicker.OnFrequencyChangedListener; + +/** + * A simple dialog containing an FrequencyPicker. + */ +public class FrequencyPickerDialog extends AlertDialog implements OnClickListener, +OnFrequencyChangedListener { + + private static final String FREQUENCY = "FREQUENCY"; + private static final String FREQ_MIN = "FREQ_MIN"; + private static final String FREQ_MAX = "FREQ_MAX"; + private static final String FREQ_STEP = "FREQ_STEP"; + + private final FrequencyPicker mFrequencyPicker; + private final OnFrequencySetListener mCallBack; + private int mMinFrequency; + private int mMaxFrequency; + private int mChannelSpacing; + + /** + * The callback used to indicate the user is done filling in the date. + */ + public interface OnFrequencySetListener { + + void onFrequencySet(FrequencyPicker view, int frequency); + } + /** + */ + public FrequencyPickerDialog(Context context, + FmConfig fmConfig, + int frequency, + OnFrequencySetListener callback) { + //this(context, android.R.style.Theme_Dialog, fmConfig, frequency, callback); + this(context, com.android.internal.R.style.Theme_Dialog_Alert, fmConfig, frequency, callback); + } + + /** + */ + public FrequencyPickerDialog(Context context, + int theme, + FmConfig fmConfig, + int frequency, + OnFrequencySetListener callback) { + super(context, theme); + mMinFrequency = fmConfig.getLowerLimit(); + mMaxFrequency = fmConfig.getUpperLimit(); + mChannelSpacing = 200; + if(FmReceiver.FM_CHSPACE_200_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 200; + } + else if(FmReceiver.FM_CHSPACE_100_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 100; + } + else if(FmReceiver.FM_CHSPACE_50_KHZ == fmConfig.getChSpacing()) + { + mChannelSpacing = 50; + } + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + mCallBack = callback; + + setButton("Set", this); + setButton2("Cancel", (OnClickListener) null); + setIcon(R.drawable.alert_dialog_icon); + + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.frequency_picker_dialog, null); + setView(view); + mFrequencyPicker = (FrequencyPicker) view.findViewById(R.id.frequencyPicker); + if(mFrequencyPicker != null) + { + mFrequencyPicker.init(mMinFrequency, mMaxFrequency, mChannelSpacing, frequency, this); + } + else + { + Log.e("fmRadio", "Failed to find ID: R.id.frequencyPicker"); + } + } + + public void UpdateFrequency(int frequency) + { + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + mFrequencyPicker.updateFrequency(frequency); + } + + public void onClick(DialogInterface dialog, int which) { + if (mCallBack != null) { + mFrequencyPicker.clearFocus(); + int frequency = mFrequencyPicker.getFrequency(); + mCallBack.onFrequencySet(mFrequencyPicker, frequency); + } + } + + public void onFrequencyChanged(FrequencyPicker view, int frequency) { + UpdateFrequency(frequency); + } + + @Override + public Bundle onSaveInstanceState() { + Bundle state = super.onSaveInstanceState(); + state.putInt(FREQUENCY, mFrequencyPicker.getFrequency()); + state.putInt(FREQ_MIN, mMinFrequency); + state.putInt(FREQ_MAX, mMaxFrequency); + state.putInt(FREQ_STEP, mChannelSpacing); + return state; + } + + @Override + public void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + int frequency = savedInstanceState.getInt(FREQUENCY); + mMinFrequency = savedInstanceState.getInt(FREQ_MIN); + mMaxFrequency = savedInstanceState.getInt(FREQ_MAX ); + mChannelSpacing = savedInstanceState.getInt(FREQ_STEP); + mFrequencyPicker.init(mMinFrequency, mMaxFrequency, mChannelSpacing, frequency, this); + int MHz = frequency/1000; + int KHz = (frequency%1000)/100; + setTitle("FM - "+MHz+"."+KHz); + } +} |