diff options
author | Venkateshwarlu Domakonda <Venkateshwarlu@codeaurora.org> | 2013-07-24 19:56:59 +0530 |
---|---|---|
committer | Venkateshwarlu Domakonda <Venkateshwarlu@codeaurora.org> | 2013-07-25 15:33:41 +0530 |
commit | 56262fbb457c9277727a36541c1ab54c32a76d68 (patch) | |
tree | eaece285197a59100e5b1e4067f14781f56e875b /fmapp2 | |
parent | b6e25e2179d777664baa39b854f19437acba03e7 (diff) | |
download | android_hardware_qcom_fm-56262fbb457c9277727a36541c1ab54c32a76d68.tar.gz android_hardware_qcom_fm-56262fbb457c9277727a36541c1ab54c32a76d68.tar.bz2 android_hardware_qcom_fm-56262fbb457c9277727a36541c1ab54c32a76d68.zip |
fm: Move FM2 APP source tree to vendor project
- FM2 APP source moved from packages/apps to vendor/qcom-opensource/
repository.
- Remove intent mechanism to receive meta data.
- Use remote service connection mechanism to receive meta data.
Change-Id: I74ba25b259ff93097db88644f4be9e5bae20131a
Diffstat (limited to 'fmapp2')
182 files changed, 23020 insertions, 0 deletions
diff --git a/fmapp2/Android.mk b/fmapp2/Android.mk new file mode 100644 index 0000000..3985572 --- /dev/null +++ b/fmapp2/Android.mk @@ -0,0 +1,25 @@ +ifeq ($(BOARD_HAVE_QCOM_FM),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src/com/caf/fmradio) \ + src/com/caf/fmradio/IFMRadioServiceCallbacks.aidl \ + src/com/caf/fmradio/IFMRadioService.aidl \ + src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl \ + src/com/caf/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/caf/hc_utils) +else +LOCAL_SRC_FILES += $(call all-java-files-under, src/com/caf/utils) +endif +LOCAL_PACKAGE_NAME := FM2 +LOCAL_CERTIFICATE := platform +LOCAL_JNI_SHARED_LIBRARIES := libqcomfm_jni +LOCAL_JAVA_LIBRARIES := qcom.fmradio + +include $(BUILD_PACKAGE) + +endif diff --git a/fmapp2/AndroidManifest.xml b/fmapp2/AndroidManifest.xml new file mode 100644 index 0000000..4cef8c0 --- /dev/null +++ b/fmapp2/AndroidManifest.xml @@ -0,0 +1,148 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.caf.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.caf.fmradio.FMRADIO_ACTIVITY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + + </activity> + + <receiver android:name="com.caf.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.caf.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> + + <activity android:name=".StationListActivity" + android:label="@string/app_label_all_channels" + android:configChanges="orientation|keyboardHidden|screenSize|fontScale"> + + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + + </activity> + + <service android:name=".FMRadioService" + android:exported="true" /> + + </application> + +</manifest> diff --git a/fmapp2/fmtransmitter.xml b/fmapp2/fmtransmitter.xml new file mode 100644 index 0000000..5e65620 --- /dev/null +++ b/fmapp2/fmtransmitter.xml @@ -0,0 +1,223 @@ +<?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. +--> +<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/fmapp2/res/anim/preset_select.xml b/fmapp2/res/anim/preset_select.xml new file mode 100644 index 0000000..8fcf768 --- /dev/null +++ b/fmapp2/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/fmapp2/res/drawable-hdpi/bg.png b/fmapp2/res/drawable-hdpi/bg.png Binary files differnew file mode 100644 index 0000000..fd10f0b --- /dev/null +++ b/fmapp2/res/drawable-hdpi/bg.png diff --git a/fmapp2/res/drawable-hdpi/bg_landscape.png b/fmapp2/res/drawable-hdpi/bg_landscape.png Binary files differnew file mode 100644 index 0000000..83bfb23 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/bg_landscape.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_left_default.png b/fmapp2/res/drawable-hdpi/btn_arrow_left_default.png Binary files differnew file mode 100644 index 0000000..857fb7a --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_left_default.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_left_press.png b/fmapp2/res/drawable-hdpi/btn_arrow_left_press.png Binary files differnew file mode 100644 index 0000000..438e392 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_left_press.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_left_selected.png b/fmapp2/res/drawable-hdpi/btn_arrow_left_selected.png Binary files differnew file mode 100644 index 0000000..fab8ee4 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_left_selected.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_right_default.png b/fmapp2/res/drawable-hdpi/btn_arrow_right_default.png Binary files differnew file mode 100644 index 0000000..547e505 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_right_default.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_right_press.png b/fmapp2/res/drawable-hdpi/btn_arrow_right_press.png Binary files differnew file mode 100644 index 0000000..96eacae --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_right_press.png diff --git a/fmapp2/res/drawable-hdpi/btn_arrow_right_selected.png b/fmapp2/res/drawable-hdpi/btn_arrow_right_selected.png Binary files differnew file mode 100644 index 0000000..67a86f5 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_arrow_right_selected.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_left_normal.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_normal.9.png Binary files differnew file mode 100644 index 0000000..31b3970 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_normal.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_left_pressed.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_pressed.9.png Binary files differnew file mode 100644 index 0000000..3b13559 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_pressed.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_left_selected.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_selected.9.png Binary files differnew file mode 100644 index 0000000..0607d23 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_left_selected.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_normal.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_normal.9.png Binary files differnew file mode 100644 index 0000000..d9f4f84 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_normal.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_pressed.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_pressed.9.png Binary files differnew file mode 100644 index 0000000..3cc35e7 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_pressed.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_selected.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_selected.9.png Binary files differnew file mode 100644 index 0000000..736e9b2 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_middle_selected.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_right_normal.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_normal.9.png Binary files differnew file mode 100644 index 0000000..3e83fd4 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_normal.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_right_pressed.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_pressed.9.png Binary files differnew file mode 100644 index 0000000..1076681 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_pressed.9.png diff --git a/fmapp2/res/drawable-hdpi/btn_strip_trans_right_selected.9.png b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_selected.9.png Binary files differnew file mode 100644 index 0000000..b0cf219 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/btn_strip_trans_right_selected.9.png diff --git a/fmapp2/res/drawable-hdpi/count_down.png b/fmapp2/res/drawable-hdpi/count_down.png Binary files differnew file mode 100644 index 0000000..3c5f921 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/count_down.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_earphone_default.png b/fmapp2/res/drawable-hdpi/ic_btn_earphone_default.png Binary files differnew file mode 100644 index 0000000..6a5b8db --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_earphone_default.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_earphone_diable.png b/fmapp2/res/drawable-hdpi/ic_btn_earphone_diable.png Binary files differnew file mode 100644 index 0000000..f32b96e --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_earphone_diable.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_earphone_select.png b/fmapp2/res/drawable-hdpi/ic_btn_earphone_select.png Binary files differnew file mode 100644 index 0000000..67afd3e --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_earphone_select.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_onoff_default.png b/fmapp2/res/drawable-hdpi/ic_btn_onoff_default.png Binary files differnew file mode 100644 index 0000000..1c0f511 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_onoff_default.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_onoff_disable.png b/fmapp2/res/drawable-hdpi/ic_btn_onoff_disable.png Binary files differnew file mode 100644 index 0000000..12b2d28 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_onoff_disable.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_onoff_press.png b/fmapp2/res/drawable-hdpi/ic_btn_onoff_press.png Binary files differnew file mode 100644 index 0000000..efeebd3 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_onoff_press.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_spaker.png b/fmapp2/res/drawable-hdpi/ic_btn_spaker.png Binary files differnew file mode 100644 index 0000000..f439e2e --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_spaker.png diff --git a/fmapp2/res/drawable-hdpi/ic_btn_spaker_select.png b/fmapp2/res/drawable-hdpi/ic_btn_spaker_select.png Binary files differnew file mode 100644 index 0000000..bd5d57b --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_btn_spaker_select.png diff --git a/fmapp2/res/drawable-hdpi/ic_launcher_fmradio.png b/fmapp2/res/drawable-hdpi/ic_launcher_fmradio.png Binary files differnew file mode 100644 index 0000000..4815bce --- /dev/null +++ b/fmapp2/res/drawable-hdpi/ic_launcher_fmradio.png diff --git a/fmapp2/res/drawable-hdpi/recorder_start.png b/fmapp2/res/drawable-hdpi/recorder_start.png Binary files differnew file mode 100644 index 0000000..a4425e2 --- /dev/null +++ b/fmapp2/res/drawable-hdpi/recorder_start.png diff --git a/fmapp2/res/drawable-hdpi/recorder_stop.png b/fmapp2/res/drawable-hdpi/recorder_stop.png Binary files differnew file mode 100644 index 0000000..bb8b32b --- /dev/null +++ b/fmapp2/res/drawable-hdpi/recorder_stop.png diff --git a/fmapp2/res/drawable-hdpi/stat_notify_fm.png b/fmapp2/res/drawable-hdpi/stat_notify_fm.png Binary files differnew file mode 100644 index 0000000..d729ffb --- /dev/null +++ b/fmapp2/res/drawable-hdpi/stat_notify_fm.png diff --git a/fmapp2/res/drawable-mdpi/bg.png b/fmapp2/res/drawable-mdpi/bg.png Binary files differnew file mode 100644 index 0000000..fd10f0b --- /dev/null +++ b/fmapp2/res/drawable-mdpi/bg.png diff --git a/fmapp2/res/drawable-mdpi/bg_landscape.png b/fmapp2/res/drawable-mdpi/bg_landscape.png Binary files differnew file mode 100644 index 0000000..83bfb23 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/bg_landscape.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_left_default.png b/fmapp2/res/drawable-mdpi/btn_arrow_left_default.png Binary files differnew file mode 100644 index 0000000..da077e6 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_left_default.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_left_press.png b/fmapp2/res/drawable-mdpi/btn_arrow_left_press.png Binary files differnew file mode 100644 index 0000000..b18c859 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_left_press.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_left_selected.png b/fmapp2/res/drawable-mdpi/btn_arrow_left_selected.png Binary files differnew file mode 100644 index 0000000..aa5ebe4 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_left_selected.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_right_default.png b/fmapp2/res/drawable-mdpi/btn_arrow_right_default.png Binary files differnew file mode 100644 index 0000000..696f53a --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_right_default.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_right_press.png b/fmapp2/res/drawable-mdpi/btn_arrow_right_press.png Binary files differnew file mode 100644 index 0000000..069df33 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_right_press.png diff --git a/fmapp2/res/drawable-mdpi/btn_arrow_right_selected.png b/fmapp2/res/drawable-mdpi/btn_arrow_right_selected.png Binary files differnew file mode 100644 index 0000000..d7d797b --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_arrow_right_selected.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_left_normal.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_normal.9.png Binary files differnew file mode 100644 index 0000000..2d7db2b --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_normal.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_left_pressed.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_pressed.9.png Binary files differnew file mode 100644 index 0000000..ceddd76 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_pressed.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_left_selected.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_selected.9.png Binary files differnew file mode 100644 index 0000000..bc40887 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_left_selected.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_normal.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_normal.9.png Binary files differnew file mode 100644 index 0000000..77ee4be --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_normal.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_pressed.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_pressed.9.png Binary files differnew file mode 100644 index 0000000..19b33af --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_pressed.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_selected.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_selected.9.png Binary files differnew file mode 100644 index 0000000..33ca95c --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_middle_selected.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_right_normal.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_normal.9.png Binary files differnew file mode 100644 index 0000000..2de79b5 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_normal.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_right_pressed.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_pressed.9.png Binary files differnew file mode 100644 index 0000000..41d2650 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_pressed.9.png diff --git a/fmapp2/res/drawable-mdpi/btn_strip_trans_right_selected.9.png b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_selected.9.png Binary files differnew file mode 100644 index 0000000..a65e65f --- /dev/null +++ b/fmapp2/res/drawable-mdpi/btn_strip_trans_right_selected.9.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_earphone_default.png b/fmapp2/res/drawable-mdpi/ic_btn_earphone_default.png Binary files differnew file mode 100644 index 0000000..c729ed0 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_earphone_default.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_earphone_diable.png b/fmapp2/res/drawable-mdpi/ic_btn_earphone_diable.png Binary files differnew file mode 100644 index 0000000..c0929ba --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_earphone_diable.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_earphone_select.png b/fmapp2/res/drawable-mdpi/ic_btn_earphone_select.png Binary files differnew file mode 100644 index 0000000..fb51ce1 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_earphone_select.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_onoff_default.png b/fmapp2/res/drawable-mdpi/ic_btn_onoff_default.png Binary files differnew file mode 100644 index 0000000..9303cfb --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_onoff_default.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_onoff_disable.png b/fmapp2/res/drawable-mdpi/ic_btn_onoff_disable.png Binary files differnew file mode 100644 index 0000000..57e060a --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_onoff_disable.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_onoff_press.png b/fmapp2/res/drawable-mdpi/ic_btn_onoff_press.png Binary files differnew file mode 100644 index 0000000..1228a66 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_onoff_press.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_spaker.png b/fmapp2/res/drawable-mdpi/ic_btn_spaker.png Binary files differnew file mode 100644 index 0000000..b72fab9 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_spaker.png diff --git a/fmapp2/res/drawable-mdpi/ic_btn_spaker_select.png b/fmapp2/res/drawable-mdpi/ic_btn_spaker_select.png Binary files differnew file mode 100644 index 0000000..250baea --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_btn_spaker_select.png diff --git a/fmapp2/res/drawable-mdpi/ic_launcher_fmradio.png b/fmapp2/res/drawable-mdpi/ic_launcher_fmradio.png Binary files differnew file mode 100644 index 0000000..3361ad5 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/ic_launcher_fmradio.png diff --git a/fmapp2/res/drawable-mdpi/recorder_selected.png b/fmapp2/res/drawable-mdpi/recorder_selected.png Binary files differnew file mode 100644 index 0000000..1886ba8 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/recorder_selected.png diff --git a/fmapp2/res/drawable-mdpi/recorder_start.png b/fmapp2/res/drawable-mdpi/recorder_start.png Binary files differnew file mode 100644 index 0000000..a4425e2 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/recorder_start.png diff --git a/fmapp2/res/drawable-mdpi/recorder_stop.png b/fmapp2/res/drawable-mdpi/recorder_stop.png Binary files differnew file mode 100644 index 0000000..bb8b32b --- /dev/null +++ b/fmapp2/res/drawable-mdpi/recorder_stop.png diff --git a/fmapp2/res/drawable-mdpi/stat_notify_fm.png b/fmapp2/res/drawable-mdpi/stat_notify_fm.png Binary files differnew file mode 100644 index 0000000..6861a44 --- /dev/null +++ b/fmapp2/res/drawable-mdpi/stat_notify_fm.png diff --git a/fmapp2/res/drawable/album_border_large.1.png b/fmapp2/res/drawable/album_border_large.1.png Binary files differnew file mode 100644 index 0000000..e5ffbc7 --- /dev/null +++ b/fmapp2/res/drawable/album_border_large.1.png diff --git a/fmapp2/res/drawable/alert_dialog_icon.png b/fmapp2/res/drawable/alert_dialog_icon.png Binary files differnew file mode 100644 index 0000000..0a7de04 --- /dev/null +++ b/fmapp2/res/drawable/alert_dialog_icon.png diff --git a/fmapp2/res/drawable/application_border.xml b/fmapp2/res/drawable/application_border.xml new file mode 100644 index 0000000..eaf0918 --- /dev/null +++ b/fmapp2/res/drawable/application_border.xml @@ -0,0 +1,33 @@ +<!-- + * 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. +--> +<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/fmapp2/res/drawable/bg.png b/fmapp2/res/drawable/bg.png Binary files differnew file mode 100644 index 0000000..fd10f0b --- /dev/null +++ b/fmapp2/res/drawable/bg.png diff --git a/fmapp2/res/drawable/bg_landscape.png b/fmapp2/res/drawable/bg_landscape.png Binary files differnew file mode 100644 index 0000000..83bfb23 --- /dev/null +++ b/fmapp2/res/drawable/bg_landscape.png diff --git a/fmapp2/res/drawable/btn_arrow_left.xml b/fmapp2/res/drawable/btn_arrow_left.xml new file mode 100644 index 0000000..e64966c --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_left.xml @@ -0,0 +1,40 @@ +<?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. +--> +<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/fmapp2/res/drawable/btn_arrow_left_default.png b/fmapp2/res/drawable/btn_arrow_left_default.png Binary files differnew file mode 100644 index 0000000..2a35952 --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_left_default.png diff --git a/fmapp2/res/drawable/btn_arrow_left_press.png b/fmapp2/res/drawable/btn_arrow_left_press.png Binary files differnew file mode 100644 index 0000000..8b72f0a --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_left_press.png diff --git a/fmapp2/res/drawable/btn_arrow_left_selected.png b/fmapp2/res/drawable/btn_arrow_left_selected.png Binary files differnew file mode 100644 index 0000000..6cd3c9f --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_left_selected.png diff --git a/fmapp2/res/drawable/btn_arrow_right.xml b/fmapp2/res/drawable/btn_arrow_right.xml new file mode 100644 index 0000000..e4ab6f9 --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_right.xml @@ -0,0 +1,41 @@ +<?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. +--> +<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/fmapp2/res/drawable/btn_arrow_right_default.png b/fmapp2/res/drawable/btn_arrow_right_default.png Binary files differnew file mode 100644 index 0000000..ae6fd53 --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_right_default.png diff --git a/fmapp2/res/drawable/btn_arrow_right_press.png b/fmapp2/res/drawable/btn_arrow_right_press.png Binary files differnew file mode 100644 index 0000000..a4ae45b --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_right_press.png diff --git a/fmapp2/res/drawable/btn_arrow_right_selected.png b/fmapp2/res/drawable/btn_arrow_right_selected.png Binary files differnew file mode 100644 index 0000000..93bcc77 --- /dev/null +++ b/fmapp2/res/drawable/btn_arrow_right_selected.png diff --git a/fmapp2/res/drawable/btn_earphone.xml b/fmapp2/res/drawable/btn_earphone.xml new file mode 100644 index 0000000..5a185ed --- /dev/null +++ b/fmapp2/res/drawable/btn_earphone.xml @@ -0,0 +1,39 @@ +<?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 "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/ic_btn_earphone_select" /> + <item android:state_enabled="true" + android:drawable="@drawable/ic_btn_earphone_default" /> + <item android:state_enabled="false" + android:drawable="@drawable/ic_btn_earphone_diable" /> +</selector> diff --git a/fmapp2/res/drawable/btn_onoff.xml b/fmapp2/res/drawable/btn_onoff.xml new file mode 100644 index 0000000..a024238 --- /dev/null +++ b/fmapp2/res/drawable/btn_onoff.xml @@ -0,0 +1,39 @@ +<?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 "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/ic_btn_onoff_press" /> + <item android:state_enabled="true" + android:drawable="@drawable/ic_btn_onoff_default" /> + <item android:state_enabled="false" + android:drawable="@drawable/ic_btn_onoff_disable" /> +</selector> diff --git a/fmapp2/res/drawable/btn_recorder.xml b/fmapp2/res/drawable/btn_recorder.xml new file mode 100644 index 0000000..88a5302 --- /dev/null +++ b/fmapp2/res/drawable/btn_recorder.xml @@ -0,0 +1,39 @@ +<?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 "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/recorder_selected" /> + <item android:state_enabled="true" + android:drawable="@drawable/recorder_stop" /> + <item android:state_enabled="false" + android:drawable="@drawable/recorder_start" /> +</selector> diff --git a/fmapp2/res/drawable/btn_speaker.xml b/fmapp2/res/drawable/btn_speaker.xml new file mode 100644 index 0000000..a22e62a --- /dev/null +++ b/fmapp2/res/drawable/btn_speaker.xml @@ -0,0 +1,37 @@ +<?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 "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/ic_btn_spaker_select" /> + <item android:state_enabled="true" + android:drawable="@drawable/ic_btn_spaker" /> +</selector> diff --git a/fmapp2/res/drawable/btn_strip_trans_left.xml b/fmapp2/res/drawable/btn_strip_trans_left.xml new file mode 100644 index 0000000..0a30f34 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_left.xml @@ -0,0 +1,43 @@ +<?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 "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_strip_trans_left_pressed" /> + + <item android:state_focused="true" + android:drawable="@drawable/btn_strip_trans_left_selected" /> + + <item android:state_enabled="true" + android:drawable="@drawable/btn_strip_trans_left_normal" /> + +</selector> diff --git a/fmapp2/res/drawable/btn_strip_trans_left_normal.9.png b/fmapp2/res/drawable/btn_strip_trans_left_normal.9.png Binary files differnew file mode 100644 index 0000000..31b3970 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_left_normal.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_left_pressed.9.png b/fmapp2/res/drawable/btn_strip_trans_left_pressed.9.png Binary files differnew file mode 100644 index 0000000..3b13559 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_left_pressed.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_left_selected.9.png b/fmapp2/res/drawable/btn_strip_trans_left_selected.9.png Binary files differnew file mode 100644 index 0000000..0607d23 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_left_selected.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_middle.xml b/fmapp2/res/drawable/btn_strip_trans_middle.xml new file mode 100644 index 0000000..b6b95a6 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_middle.xml @@ -0,0 +1,43 @@ +<?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 "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_strip_trans_middle_pressed" /> + + <item android:state_focused="true" + android:drawable="@drawable/btn_strip_trans_middle_selected" /> + + <item android:state_enabled="true" + android:drawable="@drawable/btn_strip_trans_middle_normal" /> + +</selector> diff --git a/fmapp2/res/drawable/btn_strip_trans_middle_normal.9.png b/fmapp2/res/drawable/btn_strip_trans_middle_normal.9.png Binary files differnew file mode 100644 index 0000000..d9f4f84 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_middle_normal.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_middle_pressed.9.png b/fmapp2/res/drawable/btn_strip_trans_middle_pressed.9.png Binary files differnew file mode 100644 index 0000000..3cc35e7 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_middle_pressed.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_middle_selected.9.png b/fmapp2/res/drawable/btn_strip_trans_middle_selected.9.png Binary files differnew file mode 100644 index 0000000..736e9b2 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_middle_selected.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_right.xml b/fmapp2/res/drawable/btn_strip_trans_right.xml new file mode 100644 index 0000000..512c963 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_right.xml @@ -0,0 +1,43 @@ +<?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 "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_strip_trans_right_pressed" /> + + <item android:state_focused="true" + android:drawable="@drawable/btn_strip_trans_right_selected" /> + + <item android:state_enabled="true" + android:drawable="@drawable/btn_strip_trans_right_normal" /> + +</selector> diff --git a/fmapp2/res/drawable/btn_strip_trans_right_normal.9.png b/fmapp2/res/drawable/btn_strip_trans_right_normal.9.png Binary files differnew file mode 100644 index 0000000..3e83fd4 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_right_normal.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_right_pressed.9.png b/fmapp2/res/drawable/btn_strip_trans_right_pressed.9.png Binary files differnew file mode 100644 index 0000000..1076681 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_right_pressed.9.png diff --git a/fmapp2/res/drawable/btn_strip_trans_right_selected.9.png b/fmapp2/res/drawable/btn_strip_trans_right_selected.9.png Binary files differnew file mode 100644 index 0000000..b0cf219 --- /dev/null +++ b/fmapp2/res/drawable/btn_strip_trans_right_selected.9.png diff --git a/fmapp2/res/drawable/ic_btn_earphone_default.png b/fmapp2/res/drawable/ic_btn_earphone_default.png Binary files differnew file mode 100644 index 0000000..6a5b8db --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_earphone_default.png diff --git a/fmapp2/res/drawable/ic_btn_earphone_diable.png b/fmapp2/res/drawable/ic_btn_earphone_diable.png Binary files differnew file mode 100644 index 0000000..f32b96e --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_earphone_diable.png diff --git a/fmapp2/res/drawable/ic_btn_earphone_select.png b/fmapp2/res/drawable/ic_btn_earphone_select.png Binary files differnew file mode 100644 index 0000000..67afd3e --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_earphone_select.png diff --git a/fmapp2/res/drawable/ic_btn_forward.png b/fmapp2/res/drawable/ic_btn_forward.png Binary files differnew file mode 100644 index 0000000..0936fac --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_forward.png diff --git a/fmapp2/res/drawable/ic_btn_forward_dis.png b/fmapp2/res/drawable/ic_btn_forward_dis.png Binary files differnew file mode 100644 index 0000000..cd9a2f5 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_forward_dis.png diff --git a/fmapp2/res/drawable/ic_btn_onoff.png b/fmapp2/res/drawable/ic_btn_onoff.png Binary files differnew file mode 100644 index 0000000..4405b43 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_onoff.png diff --git a/fmapp2/res/drawable/ic_btn_onoff_default.png b/fmapp2/res/drawable/ic_btn_onoff_default.png Binary files differnew file mode 100644 index 0000000..1c0f511 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_onoff_default.png diff --git a/fmapp2/res/drawable/ic_btn_onoff_disable.png b/fmapp2/res/drawable/ic_btn_onoff_disable.png Binary files differnew file mode 100644 index 0000000..12b2d28 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_onoff_disable.png diff --git a/fmapp2/res/drawable/ic_btn_onoff_press.png b/fmapp2/res/drawable/ic_btn_onoff_press.png Binary files differnew file mode 100644 index 0000000..efeebd3 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_onoff_press.png diff --git a/fmapp2/res/drawable/ic_btn_preset.png b/fmapp2/res/drawable/ic_btn_preset.png Binary files differnew file mode 100644 index 0000000..35449b3 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_preset.png diff --git a/fmapp2/res/drawable/ic_btn_search.png b/fmapp2/res/drawable/ic_btn_search.png Binary files differnew file mode 100644 index 0000000..7eea584 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_search.png diff --git a/fmapp2/res/drawable/ic_btn_search_stop.png b/fmapp2/res/drawable/ic_btn_search_stop.png Binary files differnew file mode 100644 index 0000000..0b8c4e8 --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_search_stop.png diff --git a/fmapp2/res/drawable/ic_btn_spaker.png b/fmapp2/res/drawable/ic_btn_spaker.png Binary files differnew file mode 100644 index 0000000..f439e2e --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_spaker.png diff --git a/fmapp2/res/drawable/ic_btn_spaker_select.png b/fmapp2/res/drawable/ic_btn_spaker_select.png Binary files differnew file mode 100644 index 0000000..bd5d57b --- /dev/null +++ b/fmapp2/res/drawable/ic_btn_spaker_select.png diff --git a/fmapp2/res/drawable/ic_launcher_fm_tx.png b/fmapp2/res/drawable/ic_launcher_fm_tx.png Binary files differnew file mode 100644 index 0000000..c6e41b2 --- /dev/null +++ b/fmapp2/res/drawable/ic_launcher_fm_tx.png diff --git a/fmapp2/res/drawable/ic_launcher_fmradio.png b/fmapp2/res/drawable/ic_launcher_fmradio.png Binary files differnew file mode 100644 index 0000000..2b1250c --- /dev/null +++ b/fmapp2/res/drawable/ic_launcher_fmradio.png diff --git a/fmapp2/res/drawable/ic_menu_record.png b/fmapp2/res/drawable/ic_menu_record.png Binary files differnew file mode 100644 index 0000000..60f7a01 --- /dev/null +++ b/fmapp2/res/drawable/ic_menu_record.png diff --git a/fmapp2/res/drawable/ic_signal_2.png b/fmapp2/res/drawable/ic_signal_2.png Binary files differnew file mode 100644 index 0000000..efe4b5f --- /dev/null +++ b/fmapp2/res/drawable/ic_signal_2.png diff --git a/fmapp2/res/drawable/ic_silent_mode.png b/fmapp2/res/drawable/ic_silent_mode.png Binary files differnew file mode 100644 index 0000000..c89291a --- /dev/null +++ b/fmapp2/res/drawable/ic_silent_mode.png diff --git a/fmapp2/res/drawable/ic_silent_mode_off.png b/fmapp2/res/drawable/ic_silent_mode_off.png Binary files differnew file mode 100644 index 0000000..4748b9e --- /dev/null +++ b/fmapp2/res/drawable/ic_silent_mode_off.png diff --git a/fmapp2/res/drawable/ic_status_fm_tx.png b/fmapp2/res/drawable/ic_status_fm_tx.png Binary files differnew file mode 100644 index 0000000..29383c9 --- /dev/null +++ b/fmapp2/res/drawable/ic_status_fm_tx.png diff --git a/fmapp2/res/drawable/ic_stereo.png b/fmapp2/res/drawable/ic_stereo.png Binary files differnew file mode 100644 index 0000000..b9bfec6 --- /dev/null +++ b/fmapp2/res/drawable/ic_stereo.png diff --git a/fmapp2/res/drawable/icon.png b/fmapp2/res/drawable/icon.png Binary files differnew file mode 100644 index 0000000..7502484 --- /dev/null +++ b/fmapp2/res/drawable/icon.png diff --git a/fmapp2/res/drawable/image_border_bg_normal.9.png b/fmapp2/res/drawable/image_border_bg_normal.9.png Binary files differnew file mode 100644 index 0000000..18e3607 --- /dev/null +++ b/fmapp2/res/drawable/image_border_bg_normal.9.png diff --git a/fmapp2/res/drawable/preset_page_button.xml b/fmapp2/res/drawable/preset_page_button.xml new file mode 100644 index 0000000..cb1457b --- /dev/null +++ b/fmapp2/res/drawable/preset_page_button.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 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. +--> + <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/fmapp2/res/drawable/recorder_selected.png b/fmapp2/res/drawable/recorder_selected.png Binary files differnew file mode 100644 index 0000000..24e79ad --- /dev/null +++ b/fmapp2/res/drawable/recorder_selected.png diff --git a/fmapp2/res/drawable/recorder_start.png b/fmapp2/res/drawable/recorder_start.png Binary files differnew file mode 100644 index 0000000..5297a31 --- /dev/null +++ b/fmapp2/res/drawable/recorder_start.png diff --git a/fmapp2/res/drawable/recorder_stop.png b/fmapp2/res/drawable/recorder_stop.png Binary files differnew file mode 100644 index 0000000..6017be9 --- /dev/null +++ b/fmapp2/res/drawable/recorder_stop.png diff --git a/fmapp2/res/drawable/stat_notify_fm.png b/fmapp2/res/drawable/stat_notify_fm.png Binary files differnew file mode 100644 index 0000000..16ed9bc --- /dev/null +++ b/fmapp2/res/drawable/stat_notify_fm.png diff --git a/fmapp2/res/drawable/station_border.xml b/fmapp2/res/drawable/station_border.xml new file mode 100644 index 0000000..2aee777 --- /dev/null +++ b/fmapp2/res/drawable/station_border.xml @@ -0,0 +1,33 @@ +<!-- + * 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. +--> +<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/fmapp2/res/layout-800x480/fmradio.xml b/fmapp2/res/layout-800x480/fmradio.xml new file mode 100644 index 0000000..83b9f7c --- /dev/null +++ b/fmapp2/res/layout-800x480/fmradio.xml @@ -0,0 +1,202 @@ +<?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/bg" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="3dp" + android:layout_alignParentTop="true"> + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + 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:drawableLeft="@drawable/count_down" + android:layout_toRightOf="@+id/btn_onoff" + android:textSize="14sp" + android:layout_marginLeft="5dp" + android:textStyle="bold" + android:visibility="invisible" + android:text="@string/default_sleep" /> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_marginLeft="2dp" + android:layout_marginRight="2dp" + android:layout_toLeftOf="@+id/btn_silent" + android:layout_toRightOf="@+id/sleep_msg_tv" + android:textSize="16sp" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <ImageView android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_toLeftOf="@+id/btn_speaker_earphone" + android:layout_width="wrap_content" + android:layout_marginRight="10dp" + android:clickable="true" + android:focusable="true" + android:layout_height="wrap_content" /> + + <ImageView android:id="@+id/btn_speaker_earphone" + android:src="@drawable/btn_earphone" + android:layout_alignParentRight="true" + android:layout_marginRight="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </RelativeLayout> + + <LinearLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dp" + android:layout_below="@+id/layout_on_off"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + </LinearLayout> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="200dp" + android:layout_marginBottom="50dp" + android:layout_below="@+id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> diff --git a/fmapp2/res/layout-800x480/fmtags.xml b/fmapp2/res/layout-800x480/fmtags.xml new file mode 100644 index 0000000..352320f --- /dev/null +++ b/fmapp2/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/fmapp2/res/layout-800x480/fmtransmitter.xml b/fmapp2/res/layout-800x480/fmtransmitter.xml new file mode 100644 index 0000000..f12e808 --- /dev/null +++ b/fmapp2/res/layout-800x480/fmtransmitter.xml @@ -0,0 +1,220 @@ +<?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/bg" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <RelativeLayout android:id="@+id/on_off_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:layout_marginBottom="2dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <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/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_marginTop="150dp" > + + <RelativeLayout android:id="@+id/frequency_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="15dp" + 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="25dp" + 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="15dp" + 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> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="200dp" + android:layout_marginBottom="50dp" + android:layout_below="@id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> diff --git a/fmapp2/res/layout-800x480/navigate.xml b/fmapp2/res/layout-800x480/navigate.xml new file mode 100644 index 0000000..7e60d32 --- /dev/null +++ b/fmapp2/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/fmapp2/res/layout-800x480/station_info.xml b/fmapp2/res/layout-800x480/station_info.xml new file mode 100644 index 0000000..87a8f7e --- /dev/null +++ b/fmapp2/res/layout-800x480/station_info.xml @@ -0,0 +1,185 @@ +<?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/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="5dp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:visibility="gone" /> + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/recorder_start" + android:layout_marginLeft="20dp" + android:textSize="14sp" + android:textStyle="bold" + android:clickable="true" + android:visibility="invisible" + android:text="@string/default_record"/> + + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="16dp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="13sp" + 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="5dp" + android:paddingBottom="5dp" + 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="14sp" + android:paddingTop="2dp" + android:paddingRight="2dp" + android:paddingBottom="2dp" + android:paddingLeft="2dp" + 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:textSize="14sp" + android:paddingTop="2dp" + android:paddingRight="2dp" + android:paddingBottom="2dp" + android:paddingLeft="2dp" + android:text="@string/pty_string" /> + + </LinearLayout> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="1dp" + android:layout_marginBottom="1dp" + android:layout_marginLeft="2dp" + android:layout_marginRight="2dp"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25dp" + android:gravity="center" + android:clickable="true" + android:focusable="true" + android:layout_toLeftOf="@+id/btn_forward" + android:layout_toRightOf="@+id/btn_back" + 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="wrap_content" + android:paddingLeft="15dp" + 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="wrap_content" + android:paddingRight="15dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + + <!-- 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="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dp" + android:paddingRight="2dp" + android:paddingBottom="5dp" + android:paddingTop="20dp" + 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="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dp" + android:paddingRight="2dp" + android:paddingBottom="5dp" + android:gravity="center_horizontal" + android:text="@string/eradio_text_string"/> + + </LinearLayout> + +</merge> diff --git a/fmapp2/res/layout-land-finger-800x480/fmradio.xml b/fmapp2/res/layout-land-finger-800x480/fmradio.xml new file mode 100644 index 0000000..4a4519a --- /dev/null +++ b/fmapp2/res/layout-land-finger-800x480/fmradio.xml @@ -0,0 +1,199 @@ +<?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. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/fmradio" + android:background="@drawable/bg_landscape" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true"> + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:layout_width="wrap_content" + android:clickable="true" + android:focusable="true" + 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:drawableLeft="@drawable/recorder_start" + android:layout_marginLeft="25dp" + android:layout_toRightOf="@+id/btn_onoff" + android:textSize="14sp" + android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:visibility="invisible" + android:text="@string/default_record"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_toRightOf="@+id/record_msg_tv" + android:layout_toLeftOf="@+id/btn_silent" + android:textSize="16sp" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <ImageView android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_toLeftOf="@+id/btn_speaker_earphone" + android:layout_width="wrap_content" + android:layout_marginRight="10dp" + android:clickable="true" + android:focusable="true" + android:layout_height="wrap_content" /> + + <ImageView android:id="@+id/btn_speaker_earphone" + android:src="@drawable/btn_earphone" + android:layout_alignParentRight="true" + android:layout_marginRight="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/layout_on_off"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + </RelativeLayout> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="175dp" + android:layout_marginBottom="50dp" + android:layout_below="@+id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="50dp" + android:fillViewport="true" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_weight="1" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> diff --git a/fmapp2/res/layout-land-finger-800x480/fmtransmitter.xml b/fmapp2/res/layout-land-finger-800x480/fmtransmitter.xml new file mode 100644 index 0000000..d32ffbc --- /dev/null +++ b/fmapp2/res/layout-land-finger-800x480/fmtransmitter.xml @@ -0,0 +1,222 @@ +<?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/bg_landscape" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <RelativeLayout android:id="@+id/on_off_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:layout_marginBottom="2dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <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/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@id/on_off_layout" + android:layout_marginTop="20dp" > + + <RelativeLayout android:id="@+id/frequency_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="15dp" + 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="25dp" + 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="15dp" + 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:layout_marginTop="20dp" + android:layout_marginBottom="10dp" + android:layout_centerInParent="true" + android:text="@string/radio_text_string"/> + + </RelativeLayout> + + </RelativeLayout> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="167dp" + android:layout_marginBottom="55dp" + android:layout_below="@id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="50dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> + diff --git a/fmapp2/res/layout-land-finger-800x480/navigate.xml b/fmapp2/res/layout-land-finger-800x480/navigate.xml new file mode 100644 index 0000000..269793b --- /dev/null +++ b/fmapp2/res/layout-land-finger-800x480/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/fmapp2/res/layout-land-finger-800x480/station_info.xml b/fmapp2/res/layout-land-finger-800x480/station_info.xml new file mode 100644 index 0000000..3f44573 --- /dev/null +++ b/fmapp2/res/layout-land-finger-800x480/station_info.xml @@ -0,0 +1,174 @@ +<?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:orientation="vertical"> + + <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="5dp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:visibility="gone" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/count_down" + android:textSize="14sp" + android:layout_marginLeft="25dp" + android:textStyle="bold" + android:visibility="invisible" + android:text="@string/default_sleep" /> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_toLeftOf="@+id/sleep_msg_tv" + android:layout_toRightOf="@+id/stereo_text_tv" + android:layout_centerInParent="true" + android:text="@string/eradio_text_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="16dp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="13sp" + android:text="@string/default_audio"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="1dp" + android:paddingBottom="1dp" + android:layout_below="@+id/top_most" + android:layout_alignWithParentIfMissing="true"> + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:paddingLeft="16dp" + android:text="@string/station_id_string" /> + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:textStyle="bold" + android:layout_centerInParent="true" + android:text="@string/radio_text_string"/> + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:paddingRight="16dp" + android:text="@string/pty_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="1dp" + android:layout_marginBottom="1dp" + android:layout_marginLeft="2dp" + android:layout_below="@+id/station_frequency_row" + android:layout_marginRight="2dp"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25dp" + android:gravity="center" + android:clickable="true" + android:focusable="true" + android:layout_toLeftOf="@+id/btn_forward" + android:layout_toRightOf="@+id/btn_back" + 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="wrap_content" + android:paddingLeft="15dp" + 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="wrap_content" + android:paddingRight="15dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + </RelativeLayout> + +</merge> diff --git a/fmapp2/res/layout-land-finger/fmradio.xml b/fmapp2/res/layout-land-finger/fmradio.xml new file mode 100644 index 0000000..23d74fb --- /dev/null +++ b/fmapp2/res/layout-land-finger/fmradio.xml @@ -0,0 +1,190 @@ +<?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/bg_landscape" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentTop="true"> + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:layout_width="wrap_content" + 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:drawableLeft="@drawable/recorder_start" + android:layout_marginLeft="2dp" + android:layout_toRightOf="@+id/btn_onoff" + android:textSize="14sp" + android:textStyle="bold" + android:clickable="true" + android:focusable="true" + android:visibility="invisible" + android:text="@string/default_record"/> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_toRightOf="@+id/record_msg_tv" + android:layout_toLeftOf="@+id/btn_silent" + android:textSize="16sp" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <ImageView android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_toLeftOf="@+id/btn_speaker_earphone" + android:layout_width="wrap_content" + android:layout_marginRight="10dp" + android:clickable="true" + android:focusable="true" + android:layout_height="wrap_content" /> + + <ImageView android:id="@+id/btn_speaker_earphone" + android:src="@drawable/btn_earphone" + android:layout_alignParentRight="true" + android:layout_marginRight="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="1dp" + android:layout_below="@+id/layout_on_off"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + </RelativeLayout> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="175dp" + android:layout_marginBottom="50dp" + android:layout_below="@+id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="50dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> diff --git a/fmapp2/res/layout-land-finger/fmtransmitter.xml b/fmapp2/res/layout-land-finger/fmtransmitter.xml new file mode 100644 index 0000000..1e350b0 --- /dev/null +++ b/fmapp2/res/layout-land-finger/fmtransmitter.xml @@ -0,0 +1,223 @@ +<?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/bg_landscape" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <RelativeLayout android:id="@+id/on_off_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:layout_marginBottom="2dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <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/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@id/on_off_layout" + android:layout_marginTop="20dp" > + + <RelativeLayout android:id="@+id/frequency_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="15dp" + 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="25dp" + 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="15dp" + 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> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="175dp" + android:layout_marginBottom="50dp" + android:layout_below="@id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="12sp" + android:singleLine="true" + android:layout_width="85dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> + diff --git a/fmapp2/res/layout-land-finger/navigate.xml b/fmapp2/res/layout-land-finger/navigate.xml new file mode 100644 index 0000000..269793b --- /dev/null +++ b/fmapp2/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/fmapp2/res/layout-land-finger/station_info.xml b/fmapp2/res/layout-land-finger/station_info.xml new file mode 100644 index 0000000..9383894 --- /dev/null +++ b/fmapp2/res/layout-land-finger/station_info.xml @@ -0,0 +1,172 @@ +<?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:orientation="vertical"> + + <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="5dp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:visibility="gone" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/count_down" + android:textSize="14sp" + android:layout_marginLeft="25dp" + android:textStyle="bold" + android:visibility="invisible" + android:text="@string/default_sleep" /> + + <TextView android:id="@+id/eradio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="5dp" + android:paddingRight="5dp" + android:layout_toRightOf="@+id/sleep_msg_tv" + android:layout_toLeftOf="@+id/stereo_text_tv" + android:layout_centerInParent="true" + android:text="@string/eradio_text_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="16dp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="13sp" + android:text="@string/default_audio"/> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/station_frequency_row" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingTop="1dp" + android:paddingBottom="1dp" + android:layout_below="@+id/top_most" + android:layout_alignWithParentIfMissing="true"> + + <TextView android:id="@+id/call_sign_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:paddingLeft="16dp" + android:text="@string/station_id_string" /> + + <TextView android:id="@+id/radio_text_tv" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:textStyle="bold" + android:layout_centerInParent="true" + android:text="@string/radio_text_string"/> + + <TextView android:id="@+id/pty_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14sp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:paddingRight="16dp" + android:text="@string/pty_string" /> + + </RelativeLayout> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="1dp" + android:layout_marginBottom="1dp" + android:layout_marginLeft="2dp" + android:layout_below="@+id/station_frequency_row" + android:layout_marginRight="2dp"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25dp" + android:gravity="center" + android:layout_toLeftOf="@+id/btn_forward" + android:layout_toRightOf="@+id/btn_back" + 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="wrap_content" + android:paddingLeft="15dp" + 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="wrap_content" + android:paddingRight="15dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + </RelativeLayout> + +</merge> diff --git a/fmapp2/res/layout/alert_dialog_list.xml b/fmapp2/res/layout/alert_dialog_list.xml new file mode 100644 index 0000000..8b9ae52 --- /dev/null +++ b/fmapp2/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/fmapp2/res/layout/alert_dialog_text_entry.xml b/fmapp2/res/layout/alert_dialog_text_entry.xml new file mode 100644 index 0000000..a54fc00 --- /dev/null +++ b/fmapp2/res/layout/alert_dialog_text_entry.xml @@ -0,0 +1,50 @@ +<?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: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/fmapp2/res/layout/delete_dialog.xml b/fmapp2/res/layout/delete_dialog.xml new file mode 100644 index 0000000..44df762 --- /dev/null +++ b/fmapp2/res/layout/delete_dialog.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 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, Inc. 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. + */ + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView android:id="@+id/prompt" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp" + android:drawableLeft="@android:drawable/ic_dialog_alert" + android:drawablePadding="8dp"> + </TextView> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="6dp"> + + <Button android:id="@+id/delete" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:text="@string/button_delete" + android:layout_gravity="center_horizontal" + android:layout_alignParentLeft="true" /> + + <Button android:id="@+id/cancel" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:text="@string/button_cancel" + android:layout_alignParentRight="true" /> + + </RelativeLayout> + +</LinearLayout> diff --git a/fmapp2/res/layout/fmradio.xml b/fmapp2/res/layout/fmradio.xml new file mode 100644 index 0000000..4aef544 --- /dev/null +++ b/fmapp2/res/layout/fmradio.xml @@ -0,0 +1,202 @@ +<?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/bg" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <RelativeLayout android:id="@+id/layout_on_off" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="3dp" + android:layout_alignParentTop="true"> + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + 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:drawableLeft="@drawable/count_down" + android:layout_toRightOf="@+id/btn_onoff" + android:textSize="14sp" + android:layout_marginLeft="5dp" + android:textStyle="bold" + android:visibility="invisible" + android:text="@string/default_sleep" /> + + <TextView android:id="@+id/prog_service_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_marginLeft="2dp" + android:layout_marginRight="2dp" + android:layout_toLeftOf="@+id/btn_silent" + android:layout_toRightOf="@+id/sleep_msg_tv" + android:textSize="16sp" + android:singleLine="true" + android:textStyle="bold" + android:text="@string/prog_service_string"/> + + <ImageView android:id="@+id/btn_silent" + android:src="@drawable/ic_silent_mode_off" + android:layout_toLeftOf="@+id/btn_speaker_earphone" + android:layout_width="wrap_content" + android:layout_marginRight="10dp" + android:clickable="true" + android:focusable="true" + android:layout_height="wrap_content" /> + + <ImageView android:id="@+id/btn_speaker_earphone" + android:src="@drawable/btn_earphone" + android:layout_alignParentRight="true" + android:layout_marginRight="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + </RelativeLayout> + + <LinearLayout android:id="@+id/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dp" + android:layout_below="@+id/layout_on_off"> + + <!-- This included layout contains Station Information to display --> + <include layout="@layout/station_info" /> + + </LinearLayout> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="200dp" + android:layout_marginBottom="50dp" + android:layout_below="@+id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> diff --git a/fmapp2/res/layout/fmstats.xml b/fmapp2/res/layout/fmstats.xml new file mode 100644 index 0000000..d9306d5 --- /dev/null +++ b/fmapp2/res/layout/fmstats.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * 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. +--> + +<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"/> + + </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/fmapp2/res/layout/fmtags.xml b/fmapp2/res/layout/fmtags.xml new file mode 100644 index 0000000..352320f --- /dev/null +++ b/fmapp2/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/fmapp2/res/layout/fmtransmitter.xml b/fmapp2/res/layout/fmtransmitter.xml new file mode 100644 index 0000000..b2ba8e5 --- /dev/null +++ b/fmapp2/res/layout/fmtransmitter.xml @@ -0,0 +1,223 @@ +<?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/bg" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <RelativeLayout android:id="@+id/on_off_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:layout_marginBottom="2dp" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" > + + <ImageView android:id="@+id/btn_onoff" + android:src="@drawable/btn_onoff" + android:layout_alignParentLeft="true" + android:layout_marginLeft="15dp" + android:clickable="true" + android:focusable="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <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/stationinfo_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@id/on_off_layout" + android:layout_marginTop="150dp" > + + <RelativeLayout android:id="@+id/frequency_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="15dp" + 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="25dp" + 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="15dp" + 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> + + <com.caf.fmradio.HorizontalNumberPicker + android:id="@+id/fm_picker" + android:layout_width="fill_parent" + android:layout_height="200dp" + android:layout_marginBottom="50dp" + android:layout_below="@id/stationinfo_layout"/> + + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="60dp" + android:layout_alignParentBottom="true" + android:scrollbars="none"> + + <LinearLayout android:id="@+id/favorite_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="horizontal"> + + <Button android:id="@+id/presets_button_1" + style="@style/ButtonStripLeft" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_2" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_3" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_4" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_5" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_6" + style="@style/ButtonStripMiddle" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + <Button android:id="@+id/presets_button_7" + style="@style/ButtonStripRight" + android:text="@string/add_station" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="15sp" + android:singleLine="true" + android:layout_width="80dp" + android:layout_weight="1" + android:layout_height="wrap_content"/> + + </LinearLayout> + + </HorizontalScrollView> + +</RelativeLayout> + diff --git a/fmapp2/res/layout/frequency_picker.xml b/fmapp2/res/layout/frequency_picker.xml new file mode 100644 index 0000000..cef47f9 --- /dev/null +++ b/fmapp2/res/layout/frequency_picker.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** Copyright (c) 2013, The Linux Foundation. All rights reserved. +** Not a Contribution. +** +** Copyright 2007, 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. +*/ +--> +<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/fmapp2/res/layout/frequency_picker_dialog.xml b/fmapp2/res/layout/frequency_picker_dialog.xml new file mode 100644 index 0000000..3d95123 --- /dev/null +++ b/fmapp2/res/layout/frequency_picker_dialog.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** Copyright (c) 2013, The Linux Foundation. All rights reserved. +** Not a Contribution. +** +** Copyright 2007, 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. +*/ +--> + +<com.caf.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/fmapp2/res/layout/navigate.xml b/fmapp2/res/layout/navigate.xml new file mode 100644 index 0000000..5e6b665 --- /dev/null +++ b/fmapp2/res/layout/navigate.xml @@ -0,0 +1,158 @@ +<?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="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/fmapp2/res/layout/rename_dialog.xml b/fmapp2/res/layout/rename_dialog.xml new file mode 100644 index 0000000..2374c9c --- /dev/null +++ b/fmapp2/res/layout/rename_dialog.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 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, Inc. 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. + */ + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/rename" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:padding="10dp"> + + <EditText android:id="@+id/name" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:inputType="text" + android:textColor="#FFF" + android:singleLine="true" + android:maxLength="16"> + <requestFocus /> + </EditText> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="6dp" > + + <Button android:id="@+id/save" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:text="@string/button_ok" + android:layout_alignParentLeft="true" /> + + <Button android:id="@+id/cancel" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:text="@string/button_cancel" + android:layout_alignParentRight="true" /> + + </RelativeLayout> + +</LinearLayout> diff --git a/fmapp2/res/layout/station_info.xml b/fmapp2/res/layout/station_info.xml new file mode 100644 index 0000000..fc7d62e --- /dev/null +++ b/fmapp2/res/layout/station_info.xml @@ -0,0 +1,192 @@ +<?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/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="5dp" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:visibility="gone" /> + + <TextView android:id="@+id/sleep_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/count_down" + android:textSize="14sp" + android:layout_marginLeft="25dp" + android:textStyle="bold" + android:visibility="invisible" + android:text="@string/default_sleep" /> + + <TextView android:id="@+id/record_msg_tv" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:drawableLeft="@drawable/recorder_start" + android:layout_marginLeft="20dp" + android:textSize="14sp" + android:textStyle="bold" + android:clickable="true" + android:visibility="invisible" + android:text="@string/default_record"/> + + <TextView android:id="@+id/stereo_text_tv" + android:textAppearance="?android:attr/textAppearanceSmall" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingRight="16dp" + android:layout_alignParentTop="true" + android:layout_alignParentRight="true" + android:layout_alignBaseline="@id/signal_level" + android:textSize="13sp" + 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="5dp" + android:paddingBottom="5dp" + 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="14sp" + android:paddingTop="2dp" + android:paddingRight="2dp" + android:paddingBottom="2dp" + android:paddingLeft="2dp" + 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:textSize="14sp" + android:paddingTop="2dp" + android:paddingRight="2dp" + android:paddingBottom="2dp" + android:paddingLeft="2dp" + android:text="@string/pty_string" /> + + </LinearLayout> + + <RelativeLayout android:id="@+id/second_layout" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="2dp" + android:layout_marginBottom="2dp" + android:layout_marginLeft="2dp" + android:layout_marginRight="2dp"> + + <TextView android:id="@+id/prog_frequency_tv" + android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25dp" + android:gravity="center" + android:layout_toLeftOf="@+id/btn_forward" + android:layout_toRightOf="@+id/btn_back" + 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="wrap_content" + android:paddingLeft="15dp" + 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="wrap_content" + android:paddingRight="15dp" + android:layout_alignParentRight="true" + android:visibility="visible" + android:layout_alignWithParentIfMissing="true" + android:src="@drawable/btn_arrow_right"/> + + </RelativeLayout> + + <!-- 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="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dp" + android:paddingRight="2dp" + android:paddingBottom="5dp" + android:paddingTop="20dp" + 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="15sp" + android:singleLine="true" + android:textStyle="bold" + android:paddingLeft="2dp" + android:paddingRight="2dp" + android:paddingBottom="5dp" + android:gravity="center_horizontal" + android:text="@string/eradio_text_string"/> + + </LinearLayout> + +</merge> diff --git a/fmapp2/res/layout/station_list.xml b/fmapp2/res/layout/station_list.xml new file mode 100644 index 0000000..18d31f0 --- /dev/null +++ b/fmapp2/res/layout/station_list.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 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, Inc. 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. + */ + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <!--Change layout_height value "wrap_content" to "fill_parent" can dispaly last item dividing line --> + <ListView android:id="@+id/station_list" + android:layout_width="fill_parent" + android:layout_height="fill_parent" > + </ListView> + +</LinearLayout> diff --git a/fmapp2/res/layout/station_list_item.xml b/fmapp2/res/layout/station_list_item.xml new file mode 100644 index 0000000..40c05d5 --- /dev/null +++ b/fmapp2/res/layout/station_list_item.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 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, Inc. 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. + */ + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/item" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical"> + + <TableLayout + android:id="@+id/TableLayout1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stretchColumns="1"> + + <TableRow> + <TextView + android:id="@+id/fixName" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="30dp" + android:paddingTop="15dp" + android:text="@string/station_name" /> + + <TextView + android:id="@+id/name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="marquee" + android:marqueeRepeatLimit="marquee_forever" /> + + </TableRow> + <TableRow > + + <TextView + android:id="@+id/freq" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingLeft="30dp" + android:paddingBottom="15dp" + android:textSize="20sp" + android:layout_span="2" /> + + </TableRow> + + </TableLayout> + +</LinearLayout> diff --git a/fmapp2/res/layout/statusbar.xml b/fmapp2/res/layout/statusbar.xml new file mode 100644 index 0000000..677ed74 --- /dev/null +++ b/fmapp2/res/layout/statusbar.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (C) 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, Inc. 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. + */ + --> + +<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:layout_gravity="center_vertical" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + </ImageView> + + <TextView android:id="@+id/frequency" + android:textAppearance="?android:attr/textAppearanceMediumInverse" + android:textColor="#FFFFFFFF" + android:focusable="true" + android:ellipsize="marquee" + android:singleLine="true" + android:layout_gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + +</LinearLayout> diff --git a/fmapp2/res/values-zh-rCN/arrays.xml b/fmapp2/res/values-zh-rCN/arrays.xml new file mode 100644 index 0000000..d0a95a0 --- /dev/null +++ b/fmapp2/res/values-zh-rCN/arrays.xml @@ -0,0 +1,203 @@ +<?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 "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>立体声</item> + <item>单声道</item> + </string-array> + + <string-array name="record_durations_entries"> + <item>5 分钟</item> + <item>15 分钟</item> + <item>30 分钟</item> + <item>直至停止</item> + </string-array> + + <string-array name="sleep_duration_values"> + <item>15 分钟之后</item> + <item>30 分钟之后</item> + <item>45 分钟之后</item> + <item>1 小时之后</item> + </string-array> + + <string-array name="search_category_rbds_entries"> + <item>全部电台</item> + <item>成人点击</item> + <item>古典</item> + <item>经典摇滚</item> + <item>校园</item> + <item>乡村</item> + <item>紧急</item> + <item>紧急 测试</item> + <item>外语频道</item> + <item>信息频道</item> + <item>爵士</item> + <item>新闻</item> + <item>怀旧</item> + <item>老歌</item> + <item>个性</item> + <item>公共频道</item> + <item>宗教音乐</item> + <item>宗教交流</item> + <item>节奏布鲁斯</item> + <item>摇滚</item> + <item>轻音乐</item> + <item>轻摇滚</item> + <item>轻节奏布鲁斯</item> + <item>体育</item> + <item>交流</item> + <item>前40</item> + <item>天气</item> + </string-array> + + <string-array name="search_category_rds_entries"> + <item>全部电台</item> + <item>文化</item> + <item>新闻时事</item> + <item>儿童节目</item> + <item>乡村音乐</item> + <item>记录</item> + <item>戏剧</item> + <item>轻音乐</item> + <item>教育</item> + <item>紧急</item> + <item>紧急测试</item> + <item>经济</item> + <item>民族音乐</item> + <item>信息</item> + <item>爵士音乐</item> + <item>轻古典音乐</item> + <item>休闲</item> + <item>新闻</item> + <item>民族音乐</item> + <item>其他音乐</item> + <item>怀旧</item> + <item>听众来电</item> + <item>流行音乐</item> + <item>宗教</item> + <item>摇滚音乐</item> + <item>科学</item> + <item>古典</item> + <item>社会事务</item> + <item>体育</item> + <item>旅行</item> + <item>变奏</item> + <item>天气</item> + </string-array> + + + <string-array name="presetlist_edit_category"> + <item>重命名</item> + <item>自动选择</item> + <item>删除</item> + </string-array> + + + <string-array name="regional_band_entries"> + <item>北美</item> + <item>欧洲</item> + <item>日本</item> + <item>日本 (宽频)</item> + <item>澳大利亚</item> + <item>奥地利</item> + <item>比利时</item> + <item>巴西</item> + <item>中国</item> + <item>捷克</item> + <item>丹麦</item> + <item>芬兰</item> + <item>法国</item> + <item>德国</item> + <item>希腊</item> + <item>香港</item> + <item>印度</item> + <item>爱尔兰</item> + <item>意大利</item> + <item>韩国</item> + <item>墨西哥</item> + <item>荷兰</item> + <item>新西兰</item> + <item>挪威</item> + <item>波兰</item> + <item>葡萄牙</item> + <item>俄罗斯</item> + <item>新加坡</item> + <item>斯洛伐克</item> + <item>西班牙</item> + <item>瑞士</item> + <item>瑞典</item> + <item>台湾</item> + <item>土耳其</item> + <item>英国</item> + <item>美国</item> + </string-array> + + <string-array name="regional_band_summary"> + <item>北美 (87.5MHz - 108.0MHz ,步长 200 Khz)</item> + <item>欧洲 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>日本 (76.0MHz - 90.0MHz ,步长 100 Khz)</item> + <item>日本 (宽频) (90.0MHz - 108.0MHz ,步长 50 Khz)</item> + <item>澳大利亚 (87.7MHz - 108.0MHz , 步长 100 Khz)</item> + <item>奥地利 (87.5MHz - 108.0MHz , 步长 50 Khz)</item> + <item>比利时 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>巴西 (87.8MHz - 108.0MHz ,步长 200 Khz)</item> + <item>中国 (87.0MHz - 108.0MHz ,步长 100 Khz)</item> + <item>捷克 (87.5MHz - 108.0MHz , 步长 100 Khz)</item> + <item>丹麦 (87.5MHz - 108.0MHz ,步长 50 Khz)</item> + <item>芬兰 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>法国 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>德国 (87.5MHz - 108.0MHz ,步长 50 Khz)</item> + <item>希腊 (87.5MHz - 108.0MHz ,步长 50 Khz)</item> + <item>香港 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>印度 (91.0MHz - 106.4MHz ,步长 100 Khz)</item> + <item>爱尔兰 (87.5MHz - 108.0MHz ,步长 50 Khz)</item> + <item>意大利 (87.5MHz - 108.0MHz , 步长 50 Khz)</item> + <item>韩国 (87.5MHz - 108.0MHz ,步长 200 Khz)</item> + <item>墨西哥 (88.1MHz - 107.9MHz ,步长 200 Khz)</item> + <item>荷兰 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>新西兰 (88.0MHz - 107.0MHz ,步长 100 Khz)</item> + <item>挪威 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>波兰 (88.0MHz - 108.0MHz ,步长 50 Khz)</item> + <item>葡萄牙 (87.5MHz - 108.0MHz ,步长 50 Khz)</item> + <item>俄国 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>新加坡 (88.0MHz - 108.0MHz ,步长 100 Khz)</item> + <item>斯洛伐克 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>西班牙 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>瑞士 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>瑞典 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>台湾 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>土耳其 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>英国 (87.5MHz - 108.0MHz ,步长 100 Khz)</item> + <item>美国 (88.1MHz - 107.9MHz ,步长 200 Khz)</item> + </string-array> +</resources> diff --git a/fmapp2/res/values-zh-rCN/strings.xml b/fmapp2/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000..cbea52b --- /dev/null +++ b/fmapp2/res/values-zh-rCN/strings.xml @@ -0,0 +1,119 @@ +<?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 "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 收音机</string> + <string name="menu_settings">设置</string> + <string name="menu_record_start">开始录音</string> + <string name="menu_record_stop">停止录音</string> + <string name="menu_all_channels">全部频道</string> + <string name="app_label_all_channels">全部频道</string> + <string name="button_ok">确定</string> + <string name="button_cancel">取消</string> + <string name="button_delete">删除</string> + <string name="error_sdcard_access">不能访问SD卡</string> + <string name="fm_service_started">FM 服务</string> + <string name="menu_sleep">自动关闭</string> + <string name="menu_sleep_cancel">取消自动关闭</string> + <string name="menu_scan_start">搜索</string> + <string name="button_text_stop">停止</string> + <string name="menu_scan_stop">停止搜索</string> + <string name="msg_seeking">搜索 FM 电台</string> + <string name="msg_scanning">搜索所有 FM 电台</string> + <string name="msg_scanning_pty">搜索 "<xliff:g id="pty_name">%1$s</xliff:g>"</string> + <string name="msg_noantenna">请插入耳机</string> + <!-- The messsage shown when FM radio was turned on, headset was plugged out --> + <string name="msg_headset_plug_out">收音机播放中拔出了耳机,收音机已经自动关闭。</string> + <string name="dialog_sleep_title">收音机自动关闭</string> + <string name="preset_name_dialog_title">输入名称 </string> + <string name="preset_name_title">重命名</string> + <string name="preset_search_title">搜索</string> + <string name="preset_search_name">搜索 "<xliff:g id="preset_name">%1$s</xliff:g>"</string> + <string name="preset_add">添加</string> + <string name="preset_replace">替换</string> + <string name="preset_tune">设定频率</string> + <string name="preset_rename">重命名</string> + <string name="preset_delete">删除</string> + <string name="sel_band_menu">选择地区</string> + <string name="settings_menu">设置</string> + <string name="regional_band">地区</string> + <string name="aud_output_mode">音频输出模式</string> + <string name="sel_audio_output">选择音频输出</string> + <string name="record_dur">录音时长</string> + <string name="sel_rec_dur">选择录音时长</string> + <string name="auto_select_af">备用频率</string> + <string name="auto_select_af_enabled">自动选择开启</string> + <string name="auto_select_af_disabled">自动选择关闭</string> + <string name="settings_revert_confirm_title">确定清除设置</string> + <string name="settings_revert_confirm_msg">将删除所有设置</string> + <string name="settings_back_summary">Return to previous view</string> + <string name="settings_revert_defaults_title">恢复出厂设置</string> + <string name="settings_revert_defaults_summary">清除所有设置</string> + <string name="audio_type_stereo">立体声</string> + <string name="audio_type_mono">单声道</string> + <string name="spaceIsLow_content">"USB 存储设备空间不足,请更改录音时长设置,或删除某些录音或者其他文件。</string> + <string name="alert_dialog_ok">确定</string> + <string name="alert_dialog_cancel">取消</string> + <string name="search_dialog_title">搜索电台</string> + + <string name="fm_command_timeout_title">超时</string> + <string name="fm_tune_timeout_msg">打开FM电台无响应。 + 如果问题持续, 请重新启动FM收音机。</string> + <string name="fm_command_failed_title">FM 操作失败</string> + <string name="fm_cmd_failed_msg">FM 操作失败。如果问题持续, 请重新启动FM收音机。 + </string> + <string name="fm_cmd_failed_msg_hdmi">FM 操作失败。 不支持 FM 和 HDMI 并发 。 + </string> + <string name="fm_cmd_failed_call_on">FM 操作失败。通话期间禁止FM音频 </string> + + <string name="station_name">电台:</string> + <string name="station_exists">此频率已存在!</string> + <string name="stat_notif_frequency">FM 收音机 "<xliff:g id="frequency">%1$s</xliff:g>" MHz</string> + <string name="cannot_startup_during_call">通话中不能启动FM!</string> + <string name="cannot_startup_during_airplane">"飞行模式中不能启动FM!"</string> + <string name="station_list_delete_station">删除:<xliff:g id="name">%s</xliff:g></string> + <string name="station_list_delete_station_prompt">要删除 <xliff:g id="name">%s</xliff:g> 吗?</string> + <string name="sdcard_no_space_cannot_recording">SD卡空间不足,不能录音</string> + <string name="recording_stop_no_space">SD卡空间不足,录音停止</string> + <string name="station_name_empty">电台名字不能为空,请新重输入</string> + <string name="station_name_exist">已存在名为\"<xliff:g id="name">%s</xliff:g>\"的电台,请输入其他名称。</string> + <!-- the title of rename dialog lack of translation --> + <string name="dialog_presetlist_rename_title">"请输入名字"</string> + + <!-- prompt message for stopping record --> + <string name="save_record_file">"录音已保存至 "<xliff:g id="record_file">%1$s</xliff:g>"</string> + <!-- all recordings will show up in the media database with this 'artist' name --> + <string name="audio_db_artist_name">我的 FM 录音</string> + <!-- all recordings will show up in the media database with this 'album' name --> + <string name="audio_db_album_name">FM 录音</string> + <!-- all recordings will show up in the media database in a playlist with this name --> + <string name="audio_db_playlist_name">FM 录音</string> +</resources> diff --git a/fmapp2/res/values-zh-rTW/strings.xml b/fmapp2/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000..cf3d605 --- /dev/null +++ b/fmapp2/res/values-zh-rTW/strings.xml @@ -0,0 +1,101 @@ +<!-- +Copyright (c) 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 "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 收音機</string> + <string name="menu_settings">設定</string> + <string name="menu_record_start">開始錄音</string> + <string name="menu_record_stop">停止錄音</string> + <string name="menu_all_channels">全部頻道</string> + <string name="app_label_all_channels">全部頻道</string> + <string name="button_ok">確定</string> + <string name="button_cancel">取消</string> + <string name="button_delete">取消</string> + <string name="error_sdcard_access">不能訪問SD卡</string> + <string name="fm_service_started">FM 服務</string> + <string name="menu_sleep">自動關閉</string> + <string name="menu_sleep_cancel">取消自動關閉</string> + <string name="menu_scan_start">搜索</string> + <string name="button_text_stop">停止</string> + <string name="menu_scan_stop">停止搜索</string> + <string name="msg_seeking">搜索 FM 電台</string> + <string name="msg_scanning">搜索所有 FM 電台</string> + <string name="msg_scanning_pty">搜索 "<xliff:g id="pty_name">%1$s</xliff:g>"</string> + <string name="msg_noantenna">請插入耳機</string> + <string name="dialog_sleep_title">收音機自動關閉</string> + <string name="preset_name_dialog_title">輸入名稱 </string> + <string name="preset_name_title">重命名</string> + <string name="preset_search_title">搜索</string> + <string name="preset_search_name">搜索 "<xliff:g id="preset_name">%1$s</xliff:g>"</string> + <string name="preset_add">添加</string> + <string name="preset_replace">替換</string> + <string name="preset_tune">設定頻率</string> + <string name="preset_rename">重命名</string> + <string name="preset_delete">刪除</string> + <string name="sel_band_menu">選擇地區</string> + <string name="settings_menu">設定</string> + <string name="regional_band">地區</string> + <string name="aud_output_mode">音頻輸出模式</string> + <string name="sel_audio_output">選擇音頻輸出</string> + <string name="record_dur">錄音時長</string> + <string name="sel_rec_dur">選擇錄音時長</string> + <string name="auto_select_af">備用頻率</string> + <string name="auto_select_af_enabled">自動選擇開啟</string> + <string name="auto_select_af_disabled">自動選擇關閉</string> + <string name="settings_revert_confirm_title">確定清除設定</string> + <string name="settings_revert_confirm_msg">將刪除所有設定</string> + <string name="settings_revert_defaults_title">恢復出廠設定</string> + <string name="settings_revert_defaults_summary">清除所有設定</string> + <string name="audio_type_stereo">立體聲</string> + <string name="audio_type_mono">單聲道</string> + <string name="spaceIsLow_content">USB 存儲設備空間不足,請更改錄音時長設置,或刪除某些錄音或者其他檔案。</string> + <string name="alert_dialog_ok">確定</string> + <string name="alert_dialog_cancel">取消</string> + <string name="search_dialog_title">搜索電台</string> + <string name="fm_command_timeout_title">超時</string> + <string name="fm_tune_timeout_msg">打開FM 電台無響應。如果問題持續,請重新啟動FM 收音機。</string> + <string name="fm_command_failed_title">FM 操作失敗</string> + <string name="fm_cmd_failed_msg">FM 操作失敗。如果問題持續,請重新啟動FM 收音機。</string> + <string name="fm_cmd_failed_msg_hdmi">FM 操作失敗。不支持FM和 HDMI 並發。 </string> + <string name="fm_cmd_failed_call_on">FM 操作失敗。通話期間禁止FM音頻 </string> + <string name="station_name">電台:</string> + <string name="station_exists">此頻率已存在!</string> + <string name="stat_notif_frequency">FM 收音機 "<xliff:g id="frequency">%1$s</xliff:g>" MHz</string> + <string name="cannot_startup_during_call">通话中不能启动FM!</string> + <string name="cannot_startup_during_airplane">"通話中不能啟動FM!"</string> + <string name="station_list_delete_station">删除:<xliff:g id="name">%s</xliff:g></string> + <string name="station_list_delete_station_prompt">要删除 <xliff:g id="name">%s</xliff:g> 吗?</string> + <string name="sdcard_no_space_cannot_recording">SD卡空間不足,不能錄音</string> + <string name="recording_stop_no_space">SD卡空間不足,錄音停止</string> + <string name="station_name_empty">電台名字不能為空,請新重輸入</string> + <string name="station_name_exist">已存在名為\"<xliff:g id="name">%s</xliff:g>\"的電台,請輸入其他名稱。</string> + <string name="dialog_presetlist_rename_title">請輸入名字</string> + + <!-- prompt message for stopping record --> + <string name="save_record_file">錄音已保存至 "<xliff:g id="record_file">%1$s</xliff:g>"</string> +</resources> diff --git a/fmapp2/res/values/arrays.xml b/fmapp2/res/values/arrays.xml new file mode 100644 index 0000000..cb673c8 --- /dev/null +++ b/fmapp2/res/values/arrays.xml @@ -0,0 +1,354 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (c) 2009, 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. +--> +<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 FM Search 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> RF Statistics</item> + </string-array> + + <string-array name="stats_options"> + <item> Select the option</item> + <item> Configure FM Search parameters</item> + <item> RF Statistics</item> + </string-array> + +</resources> diff --git a/fmapp2/res/values/strings.xml b/fmapp2/res/values/strings.xml new file mode 100644 index 0000000..0c46991 --- /dev/null +++ b/fmapp2/res/values/strings.xml @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">FM Radio New</string> + <string name="tx_app_name">FM Tx New</string> + + <string name="default_sleep"></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="menu_all_channels">All Channels</string> + <string name="app_label_all_channels">All Channels</string> + <string name="button_ok">OK</string> + <string name="button_cancel">Cancel</string> + <string name="button_delete">Delete</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>" MHz</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="add_station">+</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="station_name">Station:</string> + <string name="station_exists">This frequency already exists!</string> + <string name="station_list_delete_station">Delete:<xliff:g id="name">%s</xliff:g></string> + <string name="station_list_delete_station_prompt">Are you sure to delete <xliff:g id="name">%s</xliff:g> .</string> + <string name="station_name_empty">Station name is empty, please input again.</string> + <string name="station_name_exist">There is already a station named \"<xliff:g id="name">%s</xliff:g>\". Please input a different name.</string> + + <string name="rt_plus_tags">Tags</string> + +</resources> diff --git a/fmapp2/res/values/styles.xml b/fmapp2/res/values/styles.xml new file mode 100644 index 0000000..e9c9bcf --- /dev/null +++ b/fmapp2/res/values/styles.xml @@ -0,0 +1,61 @@ +<?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. +--> +<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> + + <style name="ButtonStripLeft"> + <item name="android:gravity">center</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">@drawable/btn_strip_trans_left</item> + </style> + + <style name="ButtonStripMiddle"> + <item name="android:gravity">center</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">@drawable/btn_strip_trans_middle</item> + </style> + + <style name="ButtonStripRight"> + <item name="android:gravity">center</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:background">@drawable/btn_strip_trans_right</item> + </style> + +</resources> diff --git a/fmapp2/src/com/caf/fmradio/FMAdapterApp.java b/fmapp2/src/com/caf/fmradio/FMAdapterApp.java new file mode 100644 index 0000000..dbc17c5 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/fmradio/FMMediaButtonIntentReceiver.java b/fmapp2/src/com/caf/fmradio/FMMediaButtonIntentReceiver.java new file mode 100644 index 0000000..9bc4d77 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMMediaButtonIntentReceiver.java @@ -0,0 +1,71 @@ +/* + * 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 "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.caf.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.caf.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/fmapp2/src/com/caf/fmradio/FMRadio.java b/fmapp2/src/com/caf/fmradio/FMRadio.java new file mode 100644 index 0000000..caa9bb2 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMRadio.java @@ -0,0 +1,3037 @@ +/* + * 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.caf.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.DisplayMetrics; +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.caf.utils.FrequencyPicker; +import com.caf.utils.FrequencyPickerDialog; +import android.content.ServiceConnection; +import android.media.MediaRecorder; + +import qcom.fmradio.FmConfig; +import android.os.ServiceManager; + +import com.caf.fmradio.HorizontalNumberPicker.OnScrollFinishListener; +import com.caf.fmradio.HorizontalNumberPicker.OnValueChangeListener; +import com.caf.fmradio.HorizontalNumberPicker.Scale; + +import android.content.SharedPreferences; + +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; + private static final int MENU_STATION_LIST = Menu.FIRST + 12; + /* 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 = 7; + + + /* 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; + + public static final String SCAN_STATION_PREFS_NAME = "scan_station_list"; + public static final String NUM_OF_STATIONS= "number_of_stations"; + public static final String STATION_NAME = "name_of_station"; + public static final String STATION_FREQUENCY = "frequency_of_station"; + + private static IFMRadioService mService = null; + private static FmSharedPreferences mPrefs; + + /* Button Resources */ + private ImageView mOnOffButton; + private ImageView mMuteButton; + private ImageView mSpeakerButton; + /* Button to navigate Preset pages */ + private ImageButton mPresetPageButton; + /* 6 Preset Buttons */ + private Button[] mPresetButtons = {null, null, 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 ScrollerText mERadioTextScroller = 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 HorizontalNumberPicker mPicker; + private int mFrequency; + + /** Index of arrays.xml key word "search_category_rbds_entries or search_category_rds_entries resources*/ + private int mItemsIndex = -1; + private static int mDisplayWidth; + private static final int TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER = 20; + private static final int FREQUENCY_STEP_SMALL = 50; + private static final int FREQUENCY_STEP_MEDIUM = 100; + private static final int FREQUENCY_STEP_LARGE = 200; + public static boolean mUpdatePickerValue = false; + + private LoadedDataAndState SavedDataAndState = null; + + /** fm stats property string */ + public static final String FM_STATS_PROP = "persist.fm.stats"; + + /** 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()); + + mDisplayWidth = getWindowManager().getDefaultDisplay().getWidth(); + DisplayMetrics outMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(outMetrics ); + + setContentView(R.layout.fmradio); + SavedDataAndState = (LoadedDataAndState)getLastNonConfigurationInstance(); + + mPicker = (HorizontalNumberPicker)findViewById(R.id.fm_picker); + if (mPicker != null) { + mPicker.setTextSize(mDisplayWidth / TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER); + mPicker.setDensity(outMetrics.densityDpi); + mPicker.setOnValueChangedListener(new OnValueChangeListener(){ + @Override + public void onValueChange(HorizontalNumberPicker picker, + int oldVal, int newVal) { + // TODO Auto-generated method stub + valueToFrequency(newVal); + mHandler.post(mRadioChangeFrequency); + } + }); + } + + mAnimation = AnimationUtils.loadAnimation(this, + R.anim.preset_select); + + mMuteButton = (ImageView)findViewById(R.id.btn_silent); + if (mMuteButton != null) { + mMuteButton.setOnClickListener(mMuteModeClickListener); + } + + mSpeakerButton = (ImageView)findViewById(R.id.btn_speaker_earphone); + if (mSpeakerButton != null) { + mSpeakerButton.setOnClickListener(mSpeakerClickListener); + } + + mOnOffButton = (ImageView)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); + } + + /* 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); + mPresetButtons[5] = (Button)findViewById(R.id.presets_button_6); + mPresetButtons[6] = (Button)findViewById(R.id.presets_button_7); + + 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); + if (mRecordingMsgTV != null) { + mRecordingMsgTV.setOnClickListener(mRecordButtonListener); + } + /* 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); + } + + if ((mERadioTextScroller == null) && (mERadioTextTV != null)) { + mERadioTextScroller = new ScrollerText(mERadioTextTV); + } + + + //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"); + } + } + } + + protected void setDisplayvalue(){ + int max = mPrefs.getUpperLimit(); + int min = mPrefs.getLowerLimit(); + int step = mPrefs.getFrequencyStepSize(); + switch(step) { + case FREQUENCY_STEP_SMALL: + mPicker.setScale(Scale.SCALE_SMALL); + break; + case FREQUENCY_STEP_MEDIUM: + mPicker.setScale(Scale.SCALE_MEDIUM); + break; + case FREQUENCY_STEP_LARGE: + mPicker.setScale(Scale.SCALE_LARGE); + } + + int channels = (int)((max - min) / step); + String [] displayValues = new String[channels + 1]; + for(int i = 0; i < displayValues.length; i++) { + displayValues[i] = String.valueOf((min + i * step) / 1000.0f); + } + mPicker.setDisplayedValues(displayValues, true); + mPicker.setWrapSelectorWheel(true); + mPicker.invalidate(); + } + protected int valueToFrequency(int value) { + mFrequency = mPrefs.getLowerLimit() + value * + mPrefs.getFrequencyStepSize(); + return mFrequency; + } + + @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(); + mERadioTextScroller.stopScroll(); + FmSharedPreferences.setTunedFrequency(mTunedStation.getFrequency()); + mPrefs.Save(); + } + + @Override + public void onResume() { + super.onResume(); + Log.d(LOGTAG, "FMRadio: onResume"); + mPrefs.Load(); + if (mPicker != null) { + setDisplayvalue(); + } + PresetStation station = new PresetStation("", + FmSharedPreferences.getTunedFrequency()); + if (station != null) { + mTunedStation.Copy(station); + } + mHandler.post(mUpdateProgramService); + mHandler.post(mUpdateRadioText); + mHandler.post(mOnStereo); + mUpdatePickerValue = true; + 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(FM_STATS_PROP, false); + if(mFMStats) { + item = menu.add(0, MENU_STAT_TEST, 0,R.string.menu_stats). + setIcon(android.R.drawable.ic_menu_info_details); + } + menu.add(0, MENU_STATION_LIST, 0, R.string.menu_all_channels); + 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); + } + 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); + 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_STATION_LIST: + Intent stationListIntent = new Intent().setClass(this, + StationListActivity.class); + startActivity(stationListIntent); + 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"); + mSpeakerButton.setImageResource(R.drawable.btn_earphone); + }else { // as Speaker is off turn it on. + mService.enableSpeaker(true); + Log.d(LOGTAG, "Speaker phone is turned on"); + mSpeakerButton.setImageResource(R.drawable.btn_speaker); + } + invalidateOptionsMenu(); + }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_SEARCH: { + return createSearchDlg(id, dlgBuilder); + } + case DIALOG_SLEEP: { + return createSleepDlg(id, dlgBuilder); + } + 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); + } + case DIALOG_PICK_FREQUENCY: { + FmConfig fmConfig = FmSharedPreferences.getFMConfiguration(); + return new FrequencyPickerDialog(this, fmConfig, mTunedStation.getFrequency(), mFrequencyChangeListener); + } + 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_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; + } + 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; + } + 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(FmSharedPreferences.DEFAULT_NO_FREQUENCY); + } + } + } + } //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; + + mItemsIndex = item ; + /* 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]); + clearStationList(); + 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 + if(!stationExists(mTunedStation)) { + 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); + if (mPresetButtonStation.getFrequency() == mTunedStation.getFrequency()) { + // Restore current tuned station's name as its frequency + mTunedStation.setName(""); + } + 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.setCanceledOnTouchOutside(false); + 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 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 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) { + SeekNextStation(); + return true; + } + }; + + private View.OnLongClickListener mBackLongClickListener = + new View.OnLongClickListener() { + public boolean onLongClick(View view) { + SeekPreviousStation(); + 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() + ")"); + 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 mSpeakerClickListener = + new View.OnClickListener() { + @Override + public void onClick(View v) { + // TODO Auto-generated method stub + enableSpeaker(); + } + }; + + 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 View.OnClickListener mRecordButtonListener = + new View.OnClickListener() { + public void onClick(View v) { + if (isRecording()) { + stopRecording(); + }else if(!isAnalogModeEnabled()) { + startRecording(); + } + invalidateOptionsMenu(); + } + }; + 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.setEnabled(true); + }else { + mOnOffButton.setEnabled(false); + } + } + + 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"); + } + }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 void clearStationList() { + SharedPreferences sp = getSharedPreferences(SCAN_STATION_PREFS_NAME, 0); + SharedPreferences.Editor editor = sp.edit(); + editor.clear(); + editor.commit(); + } + 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"); + }else { + } + }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"); + } + }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"); + } + }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) { + mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds + (R.drawable.recorder_stop, 0, 0, 0); + 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.setText(""); + mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds + (R.drawable.recorder_start, 0, 0, 0); + } + 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 boolean stationExists(PresetStation station ){ + boolean exists = FmSharedPreferences.sameStationExists(station); + if(exists){ + Toast t = Toast.makeText(this, getString(R.string.station_exists), Toast.LENGTH_SHORT); + t.show(); + } + return exists; + } + + private void addToPresets() { + int currentList = FmSharedPreferences.getCurrentListIndex(); + PresetStation selectedStation = getCurrentTunedStation(); + if(!stationExists(selectedStation)) { + 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 (mRadioTextScroller != null) { + mRadioTextScroller.startScroll(); + } + if (mERadioTextScroller != null) { + mERadioTextScroller.startScroll(); + } + if (mTuneStationFrequencyTV != null) { + mTuneStationFrequencyTV.setOnLongClickListener(mFrequencyViewClickListener); + } + invalidateOptionsMenu(); + if ((mRecordingMsgTV != null) && !isRecording()) { + mRecordingMsgTV.setText(""); + } + }else { + if (mRadioTextScroller != null) { + mRadioTextScroller.stopScroll(); + } + if (mERadioTextScroller != null) { + mERadioTextScroller.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 (mPicker != null) { + mPicker.setVisibility( + bEnable ? 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 && isSleepTimerActive()) ? 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(""); + mERadioTextScroller.mOriginalString = ""; + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(false); + } + }else { + if (mRadioTextTV != null) { + mRadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + } + if (mERadioTextTV != null) { + mERadioTextTV.setText(""); + mERadioTextScroller.mOriginalString = ""; + } + if (mOnOffButton != null) { + mOnOffButton.setEnabled(true); + } + } + + 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)); + } + if(mSpeakerButton != null) { + mSpeakerButton.setEnabled(bEnable); + if (bEnable) { + if(isSpeakerEnabled()) { + mSpeakerButton.setImageResource(R.drawable.btn_speaker); + }else { + mSpeakerButton.setImageResource(R.drawable.btn_earphone); + } + }else{ + mSpeakerButton.setImageResource(R.drawable.btn_earphone); + } + } + } + + 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(R.string.add_station); + mPresetButtons[buttonIndex].setTag(station); + } + } + } + } + + private void updateStationInfoToUI() { + double frequency = mTunedStation.getFrequency() / 1000.0; + mTuneStationFrequencyTV.setText("" + frequency + "MHz"); + if ((mPicker != null) && mUpdatePickerValue) { + mPicker.setValue(((mTunedStation.getFrequency() - mPrefs.getLowerLimit()) + / mPrefs.getFrequencyStepSize())); + } + mStationCallSignTV.setText(mTunedStation.getPIString()); + mProgramTypeTV.setText(mTunedStation.getPtyString()); + mRadioTextTV.setText(""); + mERadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + mRadioTextScroller.mStringlength = 0; + mRadioTextScroller.mIteration = 0; + mERadioTextScroller.mOriginalString = ""; + mERadioTextScroller.mStringlength = 0; + mERadioTextScroller.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(); + invalidateOptionsMenu(); + } + + /** 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) { + 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) { + 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); + } + } + + private boolean hasSleepTimerExpired() { + boolean expired = true; + if (isSleepTimerActive()) { + long timeNow = ((SystemClock.elapsedRealtime())); + 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); + 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; + DebugToasts("Turning Off FM Radio", Toast.LENGTH_SHORT); + disableRadio(); + return; + } + case 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(); + invalidateOptionsMenu(); + } + 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.setPty(0); + mRadioTextTV.setText(""); + mERadioTextTV.setText(""); + mRadioTextScroller.mOriginalString = ""; + mProgramServiceTV.setText(""); + mRadioTextScroller.stopScroll(); + mERadioTextScroller.mOriginalString = ""; + mERadioTextScroller.stopScroll(); + mUpdatePickerValue = true; + updateStationInfoToUI(); + } + + final Runnable mRadioEnabled = new Runnable() { + public void run() { + /* Update UI to FM On State */ + enableRadioOnOffUI(true); + /* Tune to the last tuned frequency */ + mUpdatePickerValue = true; + 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(); + invalidateOptionsMenu(); + } + }; + + 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; + }else if(TextUtils.isEmpty(str)) { /* Rest the string to empty*/ + 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 mRadioChangeFrequency = new Runnable(){ + public void run() { + mUpdatePickerValue = false; + tuneRadio(mFrequency); + } + }; + + 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(""); + mERadioTextScroller.mOriginalString = ""; + }else { + mERadioTextTV.setText(str); + mERadioTextScroller.mOriginalString = str; + } + mERadioTextScroller.startScroll(); + }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); + }else if (TextUtils.isEmpty(str)) { /* Rest the string to empty*/ + mProgramServiceTV.setText(""); + }else { + /* For non-Empty, non-Printable string, just leave the + existing old string + */ + } + /* 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) { + mStringlength = mOriginalString.length(); + 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); + } + if ((mOriginalString !=null) && (mOriginalString.length() >= mIteration)) + szStr2 = mOriginalString.substring(mIteration); + } + 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()) { + mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds + (R.drawable.recorder_stop, 0, 0, 0); + initiateRecordThread(); + }else if((mRecordDuration > 0) && + (mRecordUpdateHandlerThread != null)) { + mRecordUpdateHandlerThread.interrupt(); + if(mRecordingMsgTV != null) { + mRecordingMsgTV.setText(""); + } + 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: "); + if (mIsScaning) { + Log.d(LOGTAG, "isScanning...................."); + SharedPreferences sp = getSharedPreferences(SCAN_STATION_PREFS_NAME, 0); + SharedPreferences.Editor editor = sp.edit(); + int station_number = sp.getInt(NUM_OF_STATIONS, 0); + station_number++; + editor.putInt(NUM_OF_STATIONS, station_number); + editor.putString(STATION_NAME + station_number, station_number + ""); + editor.putInt(STATION_FREQUENCY + station_number, + FmSharedPreferences.getTunedFrequency()); + editor.commit(); + } + 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 :"); + } + 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/fmapp2/src/com/caf/fmradio/FMRadioService.java b/fmapp2/src/com/caf/fmradio/FMRadioService.java new file mode 100644 index 0000000..5d971cf --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMRadioService.java @@ -0,0 +1,3009 @@ +/* + * 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.caf.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.caf.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.caf.fmradio.SLEEP_EXPIRED"; + private static final String RECORD_EXPIRED_ACTION = "com.caf.fmradio.RECORD_TIMEOUT"; + private static final String SERVICE_DELAYED_STOP_ACTION = "com.caf.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 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 (mSpeakerPhoneOn) { + mSpeakerPhoneOn = false; + if (isAnalogModeSupported()) { + setAudioPath(true); + } + AudioSystem.setForceUse(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE); + } + if(true == isFmRecordingOn()) + stopRecording(); + if(true == mPlaybackInProgress) { + if (mMuted) + unMute(); + stopFM(); + } + 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.caf.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)); + } + } + 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"); + mReceiver.getAFInfo(); + } + 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; + } + + 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(); + } + //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/fmapp2/src/com/caf/fmradio/FMStats.java b/fmapp2/src/com/caf/fmradio/FMStats.java new file mode 100644 index 0000000..2afb301 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMStats.java @@ -0,0 +1,1346 @@ +/* + * 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.caf.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.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; + 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; + + @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 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(""); + } + } + }; + + 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); + 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_SinrSmplsCnt); + tv1.setVisibility(View.VISIBLE); + } + 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 (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 (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 (SetButton != null) { + SetButton.setText(R.string.set_IntfHighTh); + SetButton.setVisibility(View.VISIBLE); + SetButton.setOnClickListener(mOnSetIntfHighThListener); + } + break; + case 4: + 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 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/fmapp2/src/com/caf/fmradio/FMTransmitterActivity.java b/fmapp2/src/com/caf/fmradio/FMTransmitterActivity.java new file mode 100644 index 0000000..e83f61f --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMTransmitterActivity.java @@ -0,0 +1,1563 @@ +/* + * 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.caf.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 android.util.DisplayMetrics; + +import java.lang.ref.WeakReference; //import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.ArrayList; + +import com.caf.utils.FrequencyPicker; +import com.caf.utils.FrequencyPickerDialog; +import android.content.ServiceConnection; + +import qcom.fmradio.FmConfig; +import android.os.ServiceManager; + +import com.caf.fmradio.HorizontalNumberPicker.OnScrollFinishListener; +import com.caf.fmradio.HorizontalNumberPicker.OnValueChangeListener; +import com.caf.fmradio.HorizontalNumberPicker.Scale; + +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 = 7; + private static IFMTransmitterService mService = null; + private static FmSharedPreferences mPrefs; + + /* Button Resources */ + private ImageView mOnOffButton; + private HorizontalNumberPicker mPicker; + /* 6 Preset Buttons */ + private Button[] mPresetButtons = {null, null, null, null, null, null, null}; + private int[] mPresetFrequencies = {0, 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 mFrequency; + 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; + + private static int mDisplayWidth; + private static final int TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER = 20; + private static final int FREQUENCY_STEP_SMALL = 50; + private static final int FREQUENCY_STEP_MEDIUM = 100; + private static final int FREQUENCY_STEP_LARGE = 200; + public static boolean mUpdatePickerValue = false; + + /** 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()); + + mDisplayWidth = getWindowManager().getDefaultDisplay().getWidth(); + DisplayMetrics outMetrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics(outMetrics); + + setContentView(R.layout.fmtransmitter); + SavedDataAndState = (LoadedDataAndState)getLastNonConfigurationInstance(); + mAnimation = AnimationUtils.loadAnimation(this, R.anim.preset_select); + + mPicker = (HorizontalNumberPicker)findViewById(R.id.fm_picker); + if(mPicker != null) { + mPicker.setTextSize(mDisplayWidth / TEXTSIZE_PARAMETER_FOR_NUMBER_PICKER); + mPicker.setDensity(outMetrics.densityDpi); + mPicker.setOnValueChangedListener(new OnValueChangeListener() { + @Override + public void onValueChange(HorizontalNumberPicker picker, + int oldVal, int newVal) { + // TODO Auto-generated method stub + valueToFrequency(newVal); + mHandler.post(mRadioChangeFrequency); + } + }); + } + mOnOffButton = (ImageView)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); + mPresetButtons[6] = (Button)findViewById(R.id.presets_button_7); + 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); + } + } + + protected void setDisplayvalue() { + int max = mPrefs.getUpperLimit(); + int min = mPrefs.getLowerLimit(); + int step = mPrefs.getFrequencyStepSize(); + + if(mPicker == null) + return; + + switch(step) { + case FREQUENCY_STEP_SMALL: + mPicker.setScale(Scale.SCALE_SMALL); + break; + case FREQUENCY_STEP_MEDIUM: + mPicker.setScale(Scale.SCALE_MEDIUM); + break; + case FREQUENCY_STEP_LARGE: + mPicker.setScale(Scale.SCALE_LARGE); + break; + } + int channels = (int)((max - min) / step); + String [] displayValues = new String[channels + 1]; + for(int i = 0; i < displayValues.length; i++) { + displayValues[i] = String.valueOf((min + i * step) / 1000.0f); + } + if(mPicker != null) { + mPicker.setDisplayedValues(displayValues, true); + mPicker.setWrapSelectorWheel(true); + mPicker.invalidate(); + } + } + @Override + public void onResume() { + super.onResume(); + LoadPreferences(); + if(mPicker != null) { + setDisplayvalue(); + } + mUpdatePickerValue = true; + 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); + + 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: { + FmConfig fmConfig = FmSharedPreferences.getFMConfiguration(); + ((FrequencyPickerDialog) dialog).updateSteps(fmConfig.getChSpacing()); + ((FrequencyPickerDialog) dialog).updateMinFreq(fmConfig.getLowerLimit()); + ((FrequencyPickerDialog) dialog).updateMaxFreq(fmConfig.getUpperLimit()); + ((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); + if(!stationExists(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.setCanceledOnTouchOutside(false); + 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] = 0; + } + 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 boolean stationExists(int frequency) { + boolean exists = false; + for(int i = 0; i < MAX_PRESETS; i++) { + if(mPresetFrequencies[i] == frequency) { + exists = true; + break; + } + } + if(exists){ + Toast t = Toast.makeText(this, getString(R.string.station_exists), Toast.LENGTH_SHORT); + t.show(); + } + return exists; + } + 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 if(!stationExists(mTunedFrequency)) { + 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)); + } + if(mPicker != null) { + mPicker.setVisibility( + bEnable ? 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(int buttonIndex = 0; (buttonIndex < MAX_PRESETS); buttonIndex++) { + if(mPresetButtons[buttonIndex] != null) { + String display = ""; + int frequency = mPresetFrequencies[buttonIndex]; + if(frequency != 0) { + display = PresetStation.getFrequencyString(frequency); + }else { + display = this.getString(R.string.add_station); + } + mPresetButtons[buttonIndex].setText(display); + mPresetButtons[buttonIndex].setTag(new Integer(buttonIndex)); + } + } + } + + private void updateStationInfoToUI() { + mTuneStationFrequencyTV.setText(PresetStation.getFrequencyString(mTunedFrequency)); + if((mPicker != null) && mUpdatePickerValue) { + mPicker.setValue(((mTunedFrequency - mPrefs.getLowerLimit()) + / mPrefs.getFrequencyStepSize())); + } + 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) { + 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) { + 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 { + mService.tune(frequency); + updateStationInfoToUI(); + }catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private void resetFMStationInfoUI() { + mRadioTextTV.setText(""); + mRadioTextScroller.stopScroll(); + mUpdatePickerValue = true; + 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; + + 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]; + } + } + } + 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); + }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(); + } + } + } + }; + + final Runnable mRadioChangeFrequency = new Runnable(){ + public void run() { + mUpdatePickerValue = false; + tuneRadio(mFrequency); + } + }; + + protected int valueToFrequency(int value) { + mFrequency = mPrefs.getLowerLimit() + value * + mPrefs.getFrequencyStepSize(); + return mFrequency; + } + + 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); + } + szStr2 = mOriginalString.substring(mIteration); + } + 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"); + } + // 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 */ + mUpdatePickerValue = true; + 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/fmapp2/src/com/caf/fmradio/FMTransmitterConfigReceiver.java b/fmapp2/src/com/caf/fmradio/FMTransmitterConfigReceiver.java new file mode 100644 index 0000000..4f15188 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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.caf.fmradio", "com.caf.fmradio.FMTransmitterActivity"); + pManager.setComponentEnabledSetting(fmTransmitter, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + } + } + } +} diff --git a/fmapp2/src/com/caf/fmradio/FMTransmitterService.java b/fmapp2/src/com/caf/fmradio/FMTransmitterService.java new file mode 100644 index 0000000..a720868 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FMTransmitterService.java @@ -0,0 +1,1221 @@ +/* + * 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.caf.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.caf.utils.A2dpDeviceStatus; +import android.media.IRemoteControlDisplay; +import android.os.Bundle; +import android.os.RemoteException; +import android.graphics.Bitmap; +import android.media.IAudioService; +import android.media.MediaMetadataRetriever; + + +/** + * 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 = " "; + private IAudioService mAudioService; + private AudioManager mAudioManager; + private Metadata mMetadata; + RdsDisplay mRds; + + 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); + mAudioManager = new AudioManager(getApplicationContext()); + mMetadata = new Metadata(); + registerHeadsetListener(); + mRds = new RdsDisplay(); + mAudioManager.registerRemoteControlDisplay(mRds); + } + + @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); + mAudioManager.unregisterRemoteControlDisplay(mRds); + + /* 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"); + } + + @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); + } + + @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.caf.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"); + } + } + } + } + }; + private class RdsDisplay extends IRemoteControlDisplay.Stub { + RdsDisplay() { + } + + @Override + public void setPlaybackState(int generationId, int state, long stateChangeTimeMs, + long currentPosMs, float speed) { + } + + @Override + public void setMetadata(int generationId, Bundle metadata) { + updateMetadata(metadata); + } + + @Override + public void setTransportControlInfo(int generationId, int flags, int posCapabilities) { + } + + @Override + public void setArtwork(int generationId, Bitmap bitmap) { + } + + @Override + public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) { + } + + @Override + public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent, + boolean clearing) { + } + } + + class Metadata { + private String artist; + private String trackTitle; + private String albumTitle; + + public Metadata() { + artist = null; + trackTitle = null; + albumTitle = null; + } + + public String toString() { + return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + + albumTitle + "]"; + } + } + + private String getMdString(Bundle data, int id) { + return data.getString(Integer.toString(id)); + } + + private void updateMetadata(Bundle data) { + String oldMetadata = mMetadata.toString(); + mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST); + mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE); + mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM); + Log.v(LOGTAG, "mMetadata=" + mMetadata.toString()); + if (mTransmitter != null ){ + RText = mMetadata.albumTitle +":" + mMetadata.trackTitle +":"+ mMetadata.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(); + } + } + 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/fmapp2/src/com/caf/fmradio/FmSharedPreferences.java b/fmapp2/src/com/caf/fmradio/FmSharedPreferences.java new file mode 100644 index 0000000..db215e8 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/FmSharedPreferences.java @@ -0,0 +1,1114 @@ +/* + * 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.caf.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 = "FmSharedPreferences"; + + 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 final String AUDIO_OUTPUT_MODE = "audio_output_mode"; + + 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 = ""; + public 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); + mAudioOutputMode = sp.getBoolean(AUDIO_OUTPUT_MODE, true); + /* Reset the Lists before reading the preferences */ + mListOfPlists.clear(); + + 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 int getFrequencyStepSize() { + return mFrequencyBand_Stepsize; + } + 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 : 88000 TO 108000 IN 50 KHZ STEPS*/ + mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND); + mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_50_KHZ); + mFMConfiguration.setLowerLimit(88000); + 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/fmapp2/src/com/caf/fmradio/FmTags.java b/fmapp2/src/com/caf/fmradio/FmTags.java new file mode 100644 index 0000000..f3bbdbe --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/fmradio/HorizontalNumberPicker.java b/fmapp2/src/com/caf/fmradio/HorizontalNumberPicker.java new file mode 100644 index 0000000..8a89a85 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/HorizontalNumberPicker.java @@ -0,0 +1,2223 @@ +/* + * Copyright (C) 2012-2013, The Linux Foundation. All rights reserved. + * Not a Contribution + * + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caf.fmradio; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.annotation.Widget; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.method.NumberKeyListener; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.LayoutInflater.Filter; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.animation.DecelerateInterpolator; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.Scroller; +import android.widget.TextView; + +import com.android.internal.R; + +/** + * A widget that enables the user to select a number form a predefined range. + * The widget presents an input filed and up and down buttons for selecting the + * current value. Pressing/long pressing the up and down buttons increments and + * decrements the current value respectively. Touching the input filed shows a + * scroll wheel, tapping on which while shown and not moving allows direct edit + * of the current value. Sliding motions up or down hide the buttons and the + * input filed, show the scroll wheel, and rotate the latter. Flinging is also + * supported. The widget enables mapping from positions to strings such that + * instead the position index the corresponding string is displayed. + * <p> + * For an example of using this widget, see {@link android.widget.TimePicker}. + * </p> + */ +@Widget +public class HorizontalNumberPicker extends LinearLayout { + + /** + * The default update interval during long press. + */ + private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300; + + /** + * The index of the middle selector item. + */ +// private static final int SELECTOR_MIDDLE_ITEM_INDEX = 10; + + /** + * The coefficient by which to adjust (divide) the max fling velocity. + */ + private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8; + + /** + * The the duration for adjusting the selector wheel. + */ + private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800; + + /** + * The duration of scrolling to the next/previous value while changing the + * current value by one, i.e. increment or decrement. + */ + private static final int CHANGE_CURRENT_BY_ONE_SCROLL_DURATION = 300; + + /** + * The the delay for showing the input controls after a single tap on the + * input text. + */ + private static final int SHOW_INPUT_CONTROLS_DELAY_MILLIS = ViewConfiguration + .getDoubleTapTimeout(); + + /** + * The strength of fading in the top and bottom while drawing the selector. + */ + private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f; + + /** + * The default unscaled height of the selection divider. + */ + private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2; + + /** + * In this state the selector wheel is not shown. + */ + private static final int SELECTOR_WHEEL_STATE_NONE = 0; + + /** + * In this state the selector wheel is small. + */ + private static final int SELECTOR_WHEEL_STATE_SMALL = 1; + + /** + * In this state the selector wheel is large. + */ + private static final int SELECTOR_WHEEL_STATE_LARGE = 2; + + /** + * The alpha of the selector wheel when it is bright. + */ + private static final int SELECTOR_WHEEL_BRIGHT_ALPHA = 255; + + /** + * The alpha of the selector wheel when it is dimmed. + */ + private static final int SELECTOR_WHEEL_DIM_ALPHA = 60; + + /** + * The alpha for the increment/decrement button when it is transparent. + */ + private static final int BUTTON_ALPHA_TRANSPARENT = 0; + + /** + * The alpha for the increment/decrement button when it is opaque. + */ + private static final int BUTTON_ALPHA_OPAQUE = 1; + + /** + * The property for setting the selector paint. + */ + private static final String PROPERTY_SELECTOR_PAINT_ALPHA = "selectorPaintAlpha"; + + /** + * The property for setting the increment/decrement button alpha. + */ + private static final String PROPERTY_BUTTON_ALPHA = "alpha"; + + /** + * The numbers accepted by the input text's {@link Filter} + */ + private static final char[] DIGIT_CHARACTERS = new char[] { '0', '1', '2', + '3', '4', '5', '6', '7', '8', '9' }; + + /** + * Constant for unspecified size. + */ + private static final int SIZE_UNSPECIFIED = -1; + + /** + * Use a custom NumberPicker formatting callback to use two-digit minutes + * strings like "01". Keeping a static formatter etc. is the most efficient + * way to do this; it avoids creating temporary objects on every call to + * format(). + * + * @hide + */ + public static final HorizontalNumberPicker.Formatter TWO_DIGIT_FORMATTER = new HorizontalNumberPicker.Formatter() { + final StringBuilder mBuilder = new StringBuilder(); + + final java.util.Formatter mFmt = new java.util.Formatter(mBuilder, + java.util.Locale.US); + + final Object[] mArgs = new Object[1]; + + public String format(int value) { + mArgs[0] = value; + mBuilder.delete(0, mBuilder.length()); + mFmt.format("%02d", mArgs); + return mFmt.toString(); + } + }; + + private static final String TAG = "HorizontalNumberPicker"; + + /** + * The increment button. + */ + // private final ImageButton mIncrementButton; + + /** + * The decrement button. + */ + // private final ImageButton mDecrementButton; + + /** + * The text for showing the current value. + */ +// private final EditText mInputText; + + /** + * The min height of this widget. + */ + private final int mMinHeight = 0; + + /** + * The max height of this widget. + */ + private int mMaxHeight; + + /** + * The max width of this widget. + */ + private final int mMinWidth = 0; + + /** + * The max width of this widget. + */ + private int mMaxWidth; + + /** + * Flag whether to compute the max width. + */ + private final boolean mComputeMaxWidth; + + /** + * The height of the text. + */ + private int mTextSize = 20; + /** + * The height of the gap between text elements if the selector wheel. + */ + private int mSelectorTextGapHeight; + + /** + * The width of the gap between text elements if the selector wheel. + */ + private int mSelectorTextGapWidth; + + /** + * The values to be displayed instead the indices. + */ + private String[] mDisplayedValues; + + /** + * Lower value of the range of numbers allowed for the NumberPicker + */ + private int mMinValue; + + /** + * Upper value of the range of numbers allowed for the NumberPicker + */ + private int mMaxValue; + + /** + * Current value of this NumberPicker + */ + private int mValue; + + /** + * Listener to be notified upon current value change. + */ + private OnValueChangeListener mOnValueChangeListener; + + /** + * Listener to be notified upon scroll state change. + */ + private OnScrollListener mOnScrollListener; + + /** + * Formatter for for displaying the current value. + */ + private Formatter mFormatter; + + /** + * The speed for updating the value form long press. + */ + private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL; + + /** + * Cache for the string representation of selector indices. + */ + private final SparseArray<String> mSelectorIndexToStringCache = new SparseArray<String>(); + + /** + * The selector indices whose value are show by the selector. + */ + private final int[] SELECTOR_INDICES_MEDIUM = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE + }; + private final int[] SELECTOR_INDICES_SMALL = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, + }; + private final int[] SELECTOR_INDICES_LARGE = new int[] { + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, + Integer.MIN_VALUE, Integer.MIN_VALUE, + }; + private int[] mSelectorIndices = SELECTOR_INDICES_MEDIUM; + private int mSelectorMiddleItemIndex = mSelectorIndices.length / 2; + /** + * The offset to middle of selector. + */ + private static final int SELECTOR_OFFSET_ZERO = 0; + /** + * The colors alpha of selector text. + */ + private static final int SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE = 255; + + private static float mDensity = 1.0f; + private static final float LDPI = 0.75f; + private static final float MDPI = 1.0f; + private static final float HDPI = 1.5f; + private static final float XHDPI = 2.0f; + + private float mScaleWidth = 2; + private float mScaleLengthShort = 10; + private float mScaleLengthLong = 20; + private float mGapBetweenNumAndScale = 18; + private float mHdpiPositionAdjust = 18; + + public enum Scale { + SCALE_SMALL, + SCALE_MEDIUM, + SCALE_LARGE + }; + /** + * The {@link Paint} for drawing the selector. + */ + private final Paint mSelectorWheelPaint; + + /** + * The height of a selector element (text + gap). + */ + private int mSelectorElementHeight; + + /** + * The width of a selector element (text + gap). + */ + private int mSelectorElementWidth; + + /** + * The initial offset of the scroll selector. + */ + private int mInitialScrollOffset = Integer.MIN_VALUE; + + /** + * The initial offset for horizontal scroll selector . + */ + private static final int INIT_SCROLL_OFFSET_HORIZONTAL = 0; + + /** + * The initial offset for vertical scroll selector . + */ + private static final int INIT_SCROLL_OFFSET_VERTICAL = 0; + /** + * The current offset of the scroll selector. + */ + private int mCurrentScrollOffset; + + /** + * The {@link Scroller} responsible for flinging the selector. + */ + private final Scroller mFlingScroller; + + /** + * The {@link Scroller} responsible for adjusting the selector. + */ + private final Scroller mAdjustScroller; + + /** + * The previous Y coordinate while scrolling the selector. + */ + private int mPreviousScrollerY; + + /** + * The previous Y coordinate while scrolling the selector. + */ + private int mPreviousScrollerX; + + /** + * Handle to the reusable command for setting the input text selection. + */ + private SetSelectionCommand mSetSelectionCommand; + + /** + * Handle to the reusable command for adjusting the scroller. + */ + private AdjustScrollerCommand mAdjustScrollerCommand; + + /** + * Handle to the reusable command for changing the current value from long + * press by one. + */ + private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand; + + /** + * {@link Animator} for showing the up/down arrows. + */ +// private final AnimatorSet mShowInputControlsAnimator; + + /** + * {@link Animator} for dimming the selector wheel. + */ +// private final Animator mDimSelectorWheelAnimator; + + /** + * The Y position of the last down event. + */ + private float mLastDownEventY; + /** + * The X position of the last down event. + */ + private float mLastDownEventX; + + /** + * The Y position of the last motion event. + */ + private float mLastMotionEventY; + + /** + * The X position of the last motion event. + */ + private float mLastMotionEventX; + + /** + * Flag if to begin edit on next up event. + */ + private boolean mBeginEditOnUpEvent; + + /** + * Flag if to adjust the selector wheel on next up event. + */ + private boolean mAdjustScrollerOnUpEvent; + + /** + * The state of the selector wheel. + */ + private int mSelectorWheelState; + + /** + * Determines speed during touch scrolling. + */ + private VelocityTracker mVelocityTracker; + + /** + * @see ViewConfiguration#getScaledTouchSlop() + */ + private int mTouchSlop; + + /** + * @see ViewConfiguration#getScaledMinimumFlingVelocity() + */ + private int mMinimumFlingVelocity; + + /** + * @see ViewConfiguration#getScaledMaximumFlingVelocity() + */ + private int mMaximumFlingVelocity; + + /** + * Flag whether the selector should wrap around. + */ + private boolean mWrapSelectorWheel; + + /** + * The back ground color used to optimize scroller fading. + */ + private final int mSolidColor; + + /** + * Flag indicating if this widget supports flinging. + */ + private final boolean mFlingable; + + /** + * Divider for showing item to be selected while scrolling + */ +// private final Drawable mSelectionDivider; + + /** + * The height of the selection divider. + */ +// private final int mSelectionDividerHeight; + + /** + * Reusable {@link Rect} instance. + */ + private final Rect mTempRect = new Rect(); + + /** + * The current scroll state of the number picker. + */ + private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE; + + /** + * The duration of the animation for showing the input controls. + */ + private final long mShowInputControlsAnimimationDuration; + + /** + * Flag whether the scoll wheel and the fading edges have been initialized. + */ + private boolean mScrollWheelAndFadingEdgesInitialized; + + private boolean mHorizontal = true; + + /** + * Interface to listen for changes of the current value. + */ + public interface OnValueChangeListener { + + /** + * Called upon a change of the current value. + * + * @param picker + * The NumberPicker associated with this listener. + * @param oldVal + * The previous value. + * @param newVal + * The new value. + */ + void onValueChange(HorizontalNumberPicker picker, int oldVal, int newVal); + } + + + private OnScrollFinishListener mOnScrollFinishListener; + + public interface OnScrollFinishListener{ + public void onScrollFinish(int value); + } + /** + * Interface to listen for the picker scroll state. + */ + public interface OnScrollListener { + + /** + * The view is not scrolling. + */ + public static int SCROLL_STATE_IDLE = 0; + + /** + * The user is scrolling using touch, and their finger is still on the + * screen. + */ + public static int SCROLL_STATE_TOUCH_SCROLL = 1; + + /** + * The user had previously been scrolling using touch and performed a + * fling. + */ + public static int SCROLL_STATE_FLING = 2; + + /** + * Callback invoked while the number picker scroll state has changed. + * + * @param view + * The view whose scroll state is being reported. + * @param scrollState + * The current scroll state. One of + * {@link #SCROLL_STATE_IDLE}, + * {@link #SCROLL_STATE_TOUCH_SCROLL} or + * {@link #SCROLL_STATE_IDLE}. + */ + public void onScrollStateChange(HorizontalNumberPicker view, + int scrollState); + } + + /** + * Interface used to format current value into a string for presentation. + */ + public interface Formatter { + + /** + * Formats a string representation of the current value. + * + * @param value + * The currently selected value. + * @return A formatted string representation. + */ + public String format(int value); + } + + /** + * Create a new number picker. + * + * @param context + * The application environment. + */ + public HorizontalNumberPicker(Context context) { + this(context, null); + } + + /** + * Create a new number picker. + * + * @param context + * The application environment. + * @param attrs + * A collection of attributes. + */ + public HorizontalNumberPicker(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.numberPickerStyle); + } + + /** + * Create a new number picker + * + * @param context + * the application environment. + * @param attrs + * a collection of attributes. + * @param defStyle + * The default style to apply to this view. + */ + public HorizontalNumberPicker(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + + // process style attributes + TypedArray attributesArray = context.obtainStyledAttributes(attrs, + R.styleable.NumberPicker, defStyle, 0); + mSolidColor = attributesArray.getColor( + R.styleable.NumberPicker_solidColor, 0); + //mFlingable = attributesArray.getBoolean( + // R.styleable.NumberPicker_flingable, true); + mFlingable = true; + + mComputeMaxWidth = (mMaxWidth == Integer.MAX_VALUE); + attributesArray.recycle(); + + mShowInputControlsAnimimationDuration = getResources().getInteger( + R.integer.config_longAnimTime); + + // By default Linearlayout that we extend is not drawn. This is + // its draw() method is not called but dispatchDraw() is called + // directly (see ViewGroup.drawChild()). However, this class uses + // the fading edge effect implemented by View and we need our + // draw() method to be called. Therefore, we declare we will draw. + setWillNotDraw(false); + setSelectorWheelState(SELECTOR_WHEEL_STATE_NONE); + + + // initialize constants + mTouchSlop = ViewConfiguration.getTapTimeout(); + ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() + / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT; +// mTextSize = (int) mInputText.getTextSize(); + // create the selector wheel paint + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Align.CENTER); + paint.setTextSize(mTextSize); + paint.setColor(Color.WHITE); + mSelectorWheelPaint = paint; + + + // create the fling and adjust scrollers + mFlingScroller = new Scroller(getContext(), null, true); + mAdjustScroller = new Scroller(getContext(), + new DecelerateInterpolator(2.5f)); +// updateInputTextView(); + + updateIncrementAndDecrementButtonsVisibilityState(); + + if (mFlingable) { + if (isInEditMode()) { + setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL); + } else { + // Start with shown selector wheel and hidden controls. When + // made + // visible hide the selector and fade-in the controls to suggest + // fling interaction. + setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE); + } + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + final int msrdWdth = getMeasuredWidth(); + final int msrdHght = getMeasuredHeight(); + + if (!mScrollWheelAndFadingEdgesInitialized) { + mScrollWheelAndFadingEdgesInitialized = true; + // need to do all this when we know our size + initializeSelectorWheel(); + initializeFadingEdges(); + } + setVerticalFadingEdgeEnabled(false); + } + public void setTextSize(int textSize){ + if(textSize > 0 ){ + mTextSize = textSize; + mSelectorWheelPaint.setTextSize(textSize); + } + } + public void setDensity(int density){ + switch(density){ + case DisplayMetrics.DENSITY_LOW : + mDensity = LDPI; + break; + case DisplayMetrics.DENSITY_MEDIUM: + mDensity = MDPI; + break; + case DisplayMetrics.DENSITY_HIGH: + mDensity = HDPI; + break; + case DisplayMetrics.DENSITY_XHIGH: + mDensity = XHDPI; + break; + default: + mDensity = MDPI; + break; + } + } + + public void setScale(Scale scale){ + switch(scale){ + case SCALE_SMALL: + mSelectorIndices = SELECTOR_INDICES_SMALL; + break; + case SCALE_MEDIUM: + mSelectorIndices = SELECTOR_INDICES_MEDIUM; + break; + case SCALE_LARGE: + mSelectorIndices = SELECTOR_INDICES_LARGE; + break; + default : + break; + } + + mSelectorMiddleItemIndex = mSelectorIndices.length / 2; + initializeSelectorWheel(); +// invalidate(); + } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + + final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, + mMaxWidth); + final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, + mMaxHeight); + super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); + // Flag if we are measured with width or height less than the respective + // min. + final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, + getMeasuredWidth(), widthMeasureSpec); + final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, + getMeasuredHeight(), heightMeasureSpec); + setMeasuredDimension(widthSize, heightSize); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (!isEnabled() || !mFlingable) { + return false; + } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mLastMotionEventY = mLastDownEventY = event.getY(); + mLastMotionEventX = mLastDownEventX = event.getX(); + removeAllCallbacks(); + mBeginEditOnUpEvent = false; + mAdjustScrollerOnUpEvent = true; + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); + boolean scrollersFinished = mFlingScroller.isFinished() + && mAdjustScroller.isFinished(); + if (!scrollersFinished) { + mFlingScroller.forceFinished(true); + mAdjustScroller.forceFinished(true); + onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + } + mBeginEditOnUpEvent = scrollersFinished; + mAdjustScrollerOnUpEvent = true; + return true; + } + + mAdjustScrollerOnUpEvent = false; + setSelectorWheelState(SELECTOR_WHEEL_STATE_LARGE); +// hideInputControls(); + return true; + case MotionEvent.ACTION_MOVE: + float currentMoveY = event.getY(); + float currentMoveX = event.getX(); + int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY); + int deltaDownX = (int) Math.abs(currentMoveX - mLastDownEventX); + if(mHorizontal){ + if (mLastDownEventX > mTouchSlop) { + mBeginEditOnUpEvent = false; + onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + return true; + } + }else{ + if (deltaDownY > mTouchSlop) { + mBeginEditOnUpEvent = false; + onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + return true; + } + } + break; + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + return false; + } + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(ev); + int action = ev.getActionMasked(); + + switch (action) { + case MotionEvent.ACTION_MOVE: + if (!mHorizontal) { + float currentMoveY = ev.getY(); + if (mBeginEditOnUpEvent + || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + int deltaDownY = (int) Math.abs(currentMoveY + - mLastDownEventY); + if (deltaDownY > mTouchSlop) { + mBeginEditOnUpEvent = false; + onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + } + int deltaMoveY = (int) (currentMoveY - mLastMotionEventY); + scrollBy(0, deltaMoveY); + invalidate(); + mLastMotionEventY = currentMoveY; + } else { + float currentMoveX = ev.getX(); + if (mBeginEditOnUpEvent + || mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + int deltaDownX = (int) Math.abs(currentMoveX + - mLastDownEventX); + if (deltaDownX > mTouchSlop) { + mBeginEditOnUpEvent = false; + onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + } + int deltaMoveX = (int) (currentMoveX - mLastMotionEventX); + scrollBy(deltaMoveX, 0); + invalidate(); + mLastMotionEventX = currentMoveX; + } + break; + case MotionEvent.ACTION_UP: + if (mBeginEditOnUpEvent) { + setSelectorWheelState(SELECTOR_WHEEL_STATE_SMALL); + return true; + } +// VelocityTracker velocityTracker = mVelocityTracker; +// velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); +// int initialVelocity=0; +// if(!mHorizontal){ +// initialVelocity = (int) velocityTracker.getYVelocity(); +// }else{ +// initialVelocity = (int)velocityTracker.getXVelocity(); +// } +// if (Math.abs(initialVelocity) > mMinimumFlingVelocity) { +// // fling after up +// fling(initialVelocity); +// onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); +// } else { + if (mAdjustScrollerOnUpEvent) { + if (mFlingScroller.isFinished() + && mAdjustScroller.isFinished()) { + postAdjustScrollerCommand(0); + } + } else { + postAdjustScrollerCommand(SHOW_INPUT_CONTROLS_DELAY_MILLIS); + } +// } + mVelocityTracker.recycle(); + mVelocityTracker = null; + break; + } + return true; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_MOVE: + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + removeAllCallbacks(); + forceCompleteChangeCurrentByOneViaScroll(); + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + removeAllCallbacks(); + break; + } + return super.dispatchTouchEvent(event); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER + || keyCode == KeyEvent.KEYCODE_ENTER) { + removeAllCallbacks(); + } + return super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_CANCEL + || action == MotionEvent.ACTION_UP) { + removeAllCallbacks(); + } + return super.dispatchTrackballEvent(event); + } + + @Override + public void computeScroll() { + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { + return; + } + Scroller scroller = mFlingScroller; + if (scroller.isFinished()) { + scroller = mAdjustScroller; + if (scroller.isFinished()) { + return; + } + } + scroller.computeScrollOffset(); + + if (mHorizontal) { + int currentScrollerX = scroller.getCurrX(); + if (mPreviousScrollerX == 0) { + mPreviousScrollerX = scroller.getStartX(); + + } + + scrollBy(currentScrollerX - mPreviousScrollerX, 0); + mPreviousScrollerX = currentScrollerX; + + } else { + int currentScrollerY = scroller.getCurrY(); + if (mPreviousScrollerY == 0) { + mPreviousScrollerY = scroller.getStartY(); + } + scrollBy(0, currentScrollerY - mPreviousScrollerY); + mPreviousScrollerY = currentScrollerY; + } + + if (scroller.isFinished()) { + onScrollerFinished(scroller); + } else { + invalidate(); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + } + + @Override + public void scrollBy(int x, int y) { + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { + return; + } + int[] selectorIndices = mSelectorIndices; + if (!mHorizontal) { + if (!mWrapSelectorWheel && y > 0 + && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + if (!mWrapSelectorWheel && y < 0 + && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + mCurrentScrollOffset += y; + while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) { + mCurrentScrollOffset -= mSelectorElementHeight; + decrementSelectorIndices(selectorIndices); + changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); + if (!mWrapSelectorWheel + && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) { + mCurrentScrollOffset += mSelectorElementHeight; + incrementSelectorIndices(selectorIndices); + changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); + if (!mWrapSelectorWheel + && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + } else { + if (!mWrapSelectorWheel && x > 0 + && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + if (!mWrapSelectorWheel && x < 0 + && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + mCurrentScrollOffset += x; + while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapWidth) { + mCurrentScrollOffset -= mSelectorElementWidth; + decrementSelectorIndices(selectorIndices); + changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); + if (!mWrapSelectorWheel + && selectorIndices[mSelectorMiddleItemIndex] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapWidth) { + mCurrentScrollOffset += mSelectorElementWidth; + incrementSelectorIndices(selectorIndices); + changeCurrent(selectorIndices[mSelectorMiddleItemIndex]); + if (!mWrapSelectorWheel + && selectorIndices[mSelectorMiddleItemIndex] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + } + + } + + @Override + public int getSolidColor() { + return mSolidColor; + } + + /** + * Sets the listener to be notified on change of the current value. + * + * @param onValueChangedListener + * The listener. + */ + public void setOnValueChangedListener( + OnValueChangeListener onValueChangedListener) { + mOnValueChangeListener = onValueChangedListener; + } + + /** + * Set listener to be notified for scroll state changes. + * + * @param onScrollListener + * The listener. + */ + public void setOnScrollListener(OnScrollListener onScrollListener) { + mOnScrollListener = onScrollListener; + } + public void setOnScrollFinishedListener(OnScrollFinishListener onScrollFinishListener){ + mOnScrollFinishListener = onScrollFinishListener; + } + /** + * Set the formatter to be used for formatting the current value. + * <p> + * Note: If you have provided alternative values for the values this + * formatter is never invoked. + * </p> + * + * @param formatter + * The formatter object. If formatter is <code>null</code>, + * {@link String#valueOf(int)} will be used. + * + * @see #setDisplayedValues(String[]) + */ + public void setFormatter(Formatter formatter) { + if (formatter == mFormatter) { + return; + } + mFormatter = formatter; + initializeSelectorWheelIndices(); +// updateInputTextView(); + } + + /** + * Set the current value for the number picker. + * <p> + * If the argument is less than the {@link NumberPicker#getMinValue()} and + * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the + * current value is set to the {@link NumberPicker#getMinValue()} value. + * </p> + * <p> + * If the argument is less than the {@link NumberPicker#getMinValue()} and + * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the + * current value is set to the {@link NumberPicker#getMaxValue()} value. + * </p> + * <p> + * If the argument is less than the {@link NumberPicker#getMaxValue()} and + * {@link NumberPicker#getWrapSelectorWheel()} is <code>false</code> the + * current value is set to the {@link NumberPicker#getMaxValue()} value. + * </p> + * <p> + * If the argument is less than the {@link NumberPicker#getMaxValue()} and + * {@link NumberPicker#getWrapSelectorWheel()} is <code>true</code> the + * current value is set to the {@link NumberPicker#getMinValue()} value. + * </p> + * + * @param value + * The current value. + * @see #setWrapSelectorWheel(boolean) + * @see #setMinValue(int) + * @see #setMaxValue(int) + */ + public void setValue(int value) { + if (mValue == value) { + return; + } + if (value < mMinValue) { + value = mWrapSelectorWheel ? mMaxValue : mMinValue; + } + if (value > mMaxValue) { + value = mWrapSelectorWheel ? mMinValue : mMaxValue; + } + mValue = value; + initializeSelectorWheelIndices(); + updateInputTextView(); + updateIncrementAndDecrementButtonsVisibilityState(); + invalidate(); + } + + /** + * Computes the max width if no such specified as an attribute. + */ + private void tryComputeMaxWidth() { + if (!mComputeMaxWidth) { + return; + } + int maxTextWidth = 0; + if (mDisplayedValues == null) { + float maxDigitWidth = 0; + for (int i = 0; i <= 9; i++) { + final float digitWidth = mSelectorWheelPaint.measureText(String + .valueOf(i)); + if (digitWidth > maxDigitWidth) { + maxDigitWidth = digitWidth; + } + } + int numberOfDigits = 0; + int current = mMaxValue; + while (current > 0) { + numberOfDigits++; + current = current / 10; + } + maxTextWidth = (int) (numberOfDigits * maxDigitWidth); + } else { + final int valueCount = mDisplayedValues.length; + for (int i = 0; i < valueCount; i++) { + final float textWidth = mSelectorWheelPaint + .measureText(mDisplayedValues[i]); + if (textWidth > maxTextWidth) { + maxTextWidth = (int) textWidth; + } + } + } +// maxTextWidth += mInputText.getPaddingLeft() +// + mInputText.getPaddingRight(); + if (mMaxWidth != maxTextWidth) { + if (maxTextWidth > mMinWidth) { + mMaxWidth = maxTextWidth; + } else { + mMaxWidth = mMinWidth; + } + invalidate(); + } + } + + /** + * Gets whether the selector wheel wraps when reaching the min/max value. + * + * @return True if the selector wheel wraps. + * + * @see #getMinValue() + * @see #getMaxValue() + */ + public boolean getWrapSelectorWheel() { + return mWrapSelectorWheel; + } + + /** + * Sets whether the selector wheel shown during flinging/scrolling should + * wrap around the {@link NumberPicker#getMinValue()} and + * {@link NumberPicker#getMaxValue()} values. + * <p> + * By default if the range (max - min) is more than five (the number of + * items shown on the selector wheel) the selector wheel wrapping is + * enabled. + * </p> + * + * @param wrapSelectorWheel + * Whether to wrap. + */ + public void setWrapSelectorWheel(boolean wrapSelectorWheel) { + if (wrapSelectorWheel + && (mMaxValue - mMinValue) < mSelectorIndices.length) { + throw new IllegalStateException( + "Range less than selector items count."); + } + if (wrapSelectorWheel != mWrapSelectorWheel) { + mWrapSelectorWheel = wrapSelectorWheel; + updateIncrementAndDecrementButtonsVisibilityState(); + } + } + + /** + * Sets the speed at which the numbers be incremented and decremented when + * the up and down buttons are long pressed respectively. + * <p> + * The default value is 300 ms. + * </p> + * + * @param intervalMillis + * The speed (in milliseconds) at which the numbers will be + * incremented and decremented. + */ + public void setOnLongPressUpdateInterval(long intervalMillis) { + mLongPressUpdateInterval = intervalMillis; + } + + /** + * Returns the value of the picker. + * + * @return The value. + */ + public int getValue() { + return mValue; + } + + /** + * Returns the min value of the picker. + * + * @return The min value + */ + public int getMinValue() { + return mMinValue; + } + + /** + * Sets the min value of the picker. + * + * @param minValue + * The min value. + */ + public void setMinValue(int minValue) { + if (mMinValue == minValue) { + return; + } + if (minValue < 0) { + throw new IllegalArgumentException("minValue must be >= 0"); + } + mMinValue = minValue; + if (mMinValue > mValue) { + mValue = mMinValue; + } + boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; + setWrapSelectorWheel(wrapSelectorWheel); + initializeSelectorWheelIndices(); + updateInputTextView(); + tryComputeMaxWidth(); + } + + /** + * Returns the max value of the picker. + * + * @return The max value. + */ + public int getMaxValue() { + return mMaxValue; + } + + /** + * Sets the max value of the picker. + * + * @param maxValue + * The max value. + */ + public void setMaxValue(int maxValue) { + if (mMaxValue == maxValue) { + return; + } + if (maxValue < 0) { + throw new IllegalArgumentException("maxValue must be >= 0"); + } + mMaxValue = maxValue; + if (mMaxValue < mValue) { + mValue = mMaxValue; + } + boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; + setWrapSelectorWheel(wrapSelectorWheel); + initializeSelectorWheelIndices(); + tryComputeMaxWidth(); + } + + /** + * Gets the values to be displayed instead of string values. + * + * @return The displayed values. + */ + public String[] getDisplayedValues() { + return mDisplayedValues; + } + + /** + * Sets the values to be displayed. + * + * @param displayedValues + * The displayed values. + */ + public void setDisplayedValues(String[] displayedValues) { + if (mDisplayedValues == displayedValues) { + return; + } + mDisplayedValues = displayedValues; + if (mDisplayedValues != null) { + // Allow text entry rather than strictly numeric entry. +// mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT +// | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + } else { +// mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER); + } +// updateInputTextView(); + initializeSelectorWheelIndices(); + tryComputeMaxWidth(); + } + /** + * Sets the values to be displayed.If autoCalMinMax passed true, will calculate + * and set min value and max value. + * + * @param displayedValues + * The displayed values. + * @param autoCalMinMax + * Whether auto calculate and set the min value and max value. + */ + public void setDisplayedValues(String[] displayeValues , boolean autoCalculateMinMax) { + if(autoCalculateMinMax){ + mMinValue = 0; + mMaxValue = displayeValues.length - 1; + } + setDisplayedValues(displayeValues); + } + + @Override + protected float getTopFadingEdgeStrength() { + return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; + } + + @Override + protected float getBottomFadingEdgeStrength() { + return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + // make sure we show the controls only the very + // first time the user sees this widget + if (mFlingable && !isInEditMode()) { + // animate a bit slower the very first time + showInputControls(mShowInputControlsAnimimationDuration * 2); + } + } + + @Override + protected void onDetachedFromWindow() { + removeAllCallbacks(); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + // There is a good reason for doing this. See comments in draw(). + } + + @Override + public void draw(Canvas canvas) { + // Dispatch draw to our children only if we are not currently running + // the animation for simultaneously dimming the scroll wheel and + // showing in the buttons. This class takes advantage of the View + // implementation of fading edges effect to draw the selector wheel. + // However, in View.draw(), the fading is applied after all the children + // have been drawn and we do not want this fading to be applied to the + // buttons. Therefore, we draw our children after we have completed + // drawing ourselves. + super.draw(canvas); + +// Draw our children if we are not showing the selector wheel of fading +// it out +// if (mShowInputControlsAnimator.isRunning() +// || mSelectorWheelState != SELECTOR_WHEEL_STATE_LARGE) { +// long drawTime = getDrawingTime(); +// for (int i = 0, count = getChildCount(); i < count; i++) { +// View child = getChildAt(i); +// if (!child.isShown()) { +// continue; +// } +// drawChild(canvas, getChildAt(i), drawTime); +// } +// } + } + + @Override + protected void onDraw(Canvas canvas) { + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_NONE) { + return; + } + + float x = 0.0f; + float y = 0.0f; + if (!mHorizontal) { + x = (mRight - mLeft) / 2; + y = mCurrentScrollOffset; + } else { + x = mCurrentScrollOffset; + y = (mBottom - mTop) / 2 + mTextSize/2; + if(Math.abs(mDensity - 1.5f) < 0.001f){ + y += mHdpiPositionAdjust; + } + } + + + final int restoreCount = canvas.save(); + + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_SMALL) { + Rect clipBounds = canvas.getClipBounds(); + clipBounds.inset(0, mSelectorElementHeight); + canvas.clipRect(clipBounds); + } + + // draw the selector wheel + int[] selectorIndices = mSelectorIndices; + for (int i = 0; i < selectorIndices.length; i++) { + int selectorIndex = selectorIndices[i]; + String scrollSelectorValue = mSelectorIndexToStringCache + .get(selectorIndex); + if(i - mSelectorMiddleItemIndex > 0 ){ + mSelectorWheelPaint.setColor(Color.WHITE); + mSelectorWheelPaint.setAlpha((2*mSelectorMiddleItemIndex - i) * SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE / mSelectorMiddleItemIndex); + }else if(i - mSelectorMiddleItemIndex < 0 ){ + mSelectorWheelPaint.setColor(Color.WHITE); + mSelectorWheelPaint.setAlpha(i * SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE / mSelectorMiddleItemIndex); + }else{ + mSelectorWheelPaint.setColor(Color.RED); + mSelectorWheelPaint.setAlpha(SELECTOR_TEXT_ALPHA_TRANSPARENT_NONE); + } + float fNumber = Float.valueOf(scrollSelectorValue).floatValue(); + + boolean bShowNumber = false; + float fWidthOfScale = mScaleWidth ; + float fGapBetweenNumAndScale = mGapBetweenNumAndScale * mDensity; + float fScaleLength = mScaleLengthShort * mDensity; + //every 0.5MHz show number. + if((int)(fNumber * 100)%50 == 0 ){ + if(!(selectorIndex == getMaxValue())){ + bShowNumber = true; + fScaleLength = mScaleLengthLong * mDensity; + } + } + if(bShowNumber){ + canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint); + } + canvas.drawRect(x, y+fGapBetweenNumAndScale, x+fWidthOfScale, y + fGapBetweenNumAndScale + fScaleLength , mSelectorWheelPaint); + +// } + if (mHorizontal) { + x += mSelectorElementWidth; + } else { + y += mSelectorElementHeight; + } + } + canvas.restoreToCount(restoreCount); + } + + @Override + public void sendAccessibilityEvent(int eventType) { + // Do not send accessibility events - we want the user to + // perceive this widget as several controls rather as a whole. + } + + /** + * Makes a measure spec that tries greedily to use the max value. + * + * @param measureSpec + * The measure spec. + * @param maxSize + * The max value for the size. + * @return A measure spec greedily imposing the max size. + */ + private int makeMeasureSpec(int measureSpec, int maxSize) { + if (maxSize == SIZE_UNSPECIFIED) { + return measureSpec; + } + final int size = MeasureSpec.getSize(measureSpec); + final int mode = MeasureSpec.getMode(measureSpec); + switch (mode) { + case MeasureSpec.EXACTLY: + return measureSpec; + case MeasureSpec.AT_MOST: + return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), + MeasureSpec.EXACTLY); + case MeasureSpec.UNSPECIFIED: + return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY); + default: + throw new IllegalArgumentException("Unknown measure mode: " + mode); + } + } + + /** + * Utility to reconcile a desired size and state, with constraints imposed + * by a MeasureSpec. Tries to respect the min size, unless a different size + * is imposed by the constraints. + * + * @param minSize + * The minimal desired size. + * @param measuredSize + * The currently measured size. + * @param measureSpec + * The current measure spec. + * @return The resolved size and state. + */ + private int resolveSizeAndStateRespectingMinSize(int minSize, + int measuredSize, int measureSpec) { + if (minSize != SIZE_UNSPECIFIED) { + final int desiredWidth = Math.max(minSize, measuredSize); + return resolveSizeAndState(desiredWidth, measureSpec, 0); + } else { + return measuredSize; + } + } + + /** + * Resets the selector indices and clear the cached string representation of + * these indices. + */ + private void initializeSelectorWheelIndices() { + mSelectorIndexToStringCache.clear(); + int[] selectorIdices = mSelectorIndices; + int current = getValue(); + for (int i = 0; i < mSelectorIndices.length; i++) { + int selectorIndex = current + (i - mSelectorMiddleItemIndex); + if (mWrapSelectorWheel) { + selectorIndex = getWrappedSelectorIndex(selectorIndex); + } + mSelectorIndices[i] = selectorIndex; + ensureCachedScrollSelectorValue(mSelectorIndices[i]); + } + } + + /** + * Sets the current value of this NumberPicker, and sets mPrevious to the + * previous value. If current is greater than mEnd less than mStart, the + * value of mCurrent is wrapped around. Subclasses can override this to + * change the wrapping behavior + * + * @param current + * the new value of the NumberPicker + */ + private void changeCurrent(int current) { + if (mValue == current) { + return; + } + // Wrap around the values if we go past the start or end + if (mWrapSelectorWheel) { + current = getWrappedSelectorIndex(current); + } + int previous = mValue; + setValue(current); + notifyChange(previous, current); + } + + /** + * Changes the current value by one which is increment or decrement based on + * the passes argument. + * + * @param increment + * True to increment, false to decrement. + */ + private void changeCurrentByOne(boolean increment) { + if (mFlingable) { + mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); + mPreviousScrollerY = 0; + mPreviousScrollerX = 0; + forceCompleteChangeCurrentByOneViaScroll(); + if (increment) { + if (mHorizontal) { + mFlingScroller.startScroll(0, 0, -mSelectorElementHeight, + 0, CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); + } else { + mFlingScroller.startScroll(0, 0, 0, + -mSelectorElementHeight, + CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); + } + + } else { + if (mHorizontal) { + mFlingScroller.startScroll(0, 0, mSelectorElementHeight, 0, + CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); + } else { + mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, + CHANGE_CURRENT_BY_ONE_SCROLL_DURATION); + } + + } + invalidate(); + } else { + if (increment) { + changeCurrent(mValue + 1); + } else { + changeCurrent(mValue - 1); + } + } + } + + /** + * Ensures that if we are in the process of changing the current value by + * one via scrolling the scroller gets to its final state and the value is + * updated. + */ + private void forceCompleteChangeCurrentByOneViaScroll() { + Scroller scroller = mFlingScroller; + if (!scroller.isFinished()) { + if (mHorizontal) { + final int xBeforeAbort = scroller.getCurrX(); + scroller.abortAnimation(); + final int xDelta = scroller.getCurrX() - xBeforeAbort; + scrollBy(xDelta, 0); + } else { + final int yBeforeAbort = scroller.getCurrY(); + scroller.abortAnimation(); + final int yDelta = scroller.getCurrY() - yBeforeAbort; + scrollBy(0, yDelta); + } + } + } + + /** + * Sets the <code>alpha</code> of the {@link Paint} for drawing the selector + * wheel. + */ + @SuppressWarnings("unused") + // Called via reflection + private void setSelectorPaintAlpha(int alpha) { + mSelectorWheelPaint.setAlpha(alpha); + invalidate(); + } + + /** + * @return If the <code>event</code> is in the visible <code>view</code>. + */ + private boolean isEventInVisibleViewHitRect(MotionEvent event, View view) { + if (view.getVisibility() == VISIBLE) { + view.getHitRect(mTempRect); + return mTempRect.contains((int) event.getX(), (int) event.getY()); + } + return false; + } + + /** + * Sets the <code>selectorWheelState</code>. + */ + private void setSelectorWheelState(int selectorWheelState) { + mSelectorWheelState = selectorWheelState; + if (selectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); + } + + if (mFlingable && selectorWheelState == SELECTOR_WHEEL_STATE_LARGE + && AccessibilityManager.getInstance(mContext).isEnabled()) { + AccessibilityManager.getInstance(mContext).interrupt(); + String text = mContext + .getString(R.string.number_picker_increment_scroll_action); + } + } + + private void initializeSelectorWheel() { + initializeSelectorWheelIndices(); + int[] selectorIndices = mSelectorIndices; + int totalTextHeight = selectorIndices.length * mTextSize; + int totalTextWidth = (selectorIndices.length - 1) * 2; + // set it horizontal + float totalTextGapHeight = (mBottom - mTop) - totalTextHeight; + float totalTextGapWidth = (mRight - mLeft) - totalTextWidth; + + float textGapCount = selectorIndices.length - 1; + if (mHorizontal) { + mSelectorTextGapWidth = (int) (totalTextGapWidth / textGapCount); + Log.d(TAG,"mSelectorTextGapWidth :" + mSelectorTextGapWidth); + mSelectorElementWidth = 2 + mSelectorTextGapWidth; + mInitialScrollOffset = INIT_SCROLL_OFFSET_HORIZONTAL; + } else { + mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f); + mSelectorElementHeight = mTextSize + mSelectorTextGapHeight; + mInitialScrollOffset = INIT_SCROLL_OFFSET_VERTICAL; + } + mCurrentScrollOffset = mInitialScrollOffset; + + } + + private void initializeFadingEdges() { + setVerticalFadingEdgeEnabled(true); + setFadingEdgeLength((mBottom - mTop - mTextSize) / 2); + } + + /** + * Callback invoked upon completion of a given <code>scroller</code>. + */ + private void onScrollerFinished(Scroller scroller) { + + if(mOnScrollFinishListener != null){ + mOnScrollFinishListener.onScrollFinish(mValue); + } + if (scroller == mFlingScroller) { + if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + postAdjustScrollerCommand(0); + onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + } else { +// updateInputTextView(); +// fadeSelectorWheel(mShowInputControlsAnimimationDuration); + } + } else { +// updateInputTextView(); +// showInputControls(mShowInputControlsAnimimationDuration); + } + } + + /** + * Handles transition to a given <code>scrollState</code> + */ + private void onScrollStateChange(int scrollState) { + if (mScrollState == scrollState) { + return; + } + mScrollState = scrollState; + if (mOnScrollListener != null) { + + mOnScrollListener.onScrollStateChange(this, scrollState); + } + } + + /** + * Flings the selector with the given <code>velocityY</code>. + */ + private void fling(int velocity) { + mPreviousScrollerY = 0; + mPreviousScrollerX = 0; + int velocityY = velocity; + int velocityX = velocity; + if (mHorizontal) { + if (velocityX > 0) { + mFlingScroller.fling(0, 0, velocityX, 0, 0, Integer.MAX_VALUE, + 0, 0); + } else { + mFlingScroller.fling(Integer.MAX_VALUE, 0, velocityX, 0, 0, + Integer.MAX_VALUE, 0, 0); + } + } else { + if (velocityY > 0) { + mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, + Integer.MAX_VALUE); + } else { + mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, + 0, Integer.MAX_VALUE); + } + } + + invalidate(); + } + + /** + * Hides the input controls which is the up/down arrows and the text field. + */ + private void hideInputControls() { + // mShowInputControlsAnimator.cancel(); + // mIncrementButton.setVisibility(INVISIBLE); + // mDecrementButton.setVisibility(INVISIBLE); +// mInputText.setVisibility(INVISIBLE); + } + + /** + * Show the input controls by making them visible and animating the alpha + * property up/down arrows. + * + * @param animationDuration + * The duration of the animation. + */ + private void showInputControls(long animationDuration) { +// updateIncrementAndDecrementButtonsVisibilityState(); +// mInputText.setVisibility(VISIBLE); +// mShowInputControlsAnimator.setDuration(animationDuration); +// mShowInputControlsAnimator.start(); + } + + /** + * Fade the selector wheel via an animation. + * + * @param animationDuration + * The duration of the animation. + */ + // mark this 1 + private void fadeSelectorWheel(long animationDuration) { +// mInputText.setVisibility(VISIBLE); +// mDimSelectorWheelAnimator.setDuration(animationDuration); +// mDimSelectorWheelAnimator.start(); + } + + /** + * Updates the visibility state of the increment and decrement buttons. + */ + private void updateIncrementAndDecrementButtonsVisibilityState() { + if (mWrapSelectorWheel || mValue < mMaxValue) { + // mIncrementButton.setVisibility(VISIBLE); + } else { + // mIncrementButton.setVisibility(INVISIBLE); + } + if (mWrapSelectorWheel || mValue > mMinValue) { + // mDecrementButton.setVisibility(VISIBLE); + } else { + // mDecrementButton.setVisibility(INVISIBLE); + } + } + + /** + * @return The wrapped index <code>selectorIndex</code> value. + */ + private int getWrappedSelectorIndex(int selectorIndex) { + if (selectorIndex > mMaxValue) { + return mMinValue + (selectorIndex - mMaxValue) + % (mMaxValue - mMinValue) - 1; + } else if (selectorIndex < mMinValue) { + return mMaxValue - (mMinValue - selectorIndex) + % (mMaxValue - mMinValue) + 1; + } + return selectorIndex; + } + + /** + * Increments the <code>selectorIndices</code> whose string representations + * will be displayed in the selector. + */ + private void incrementSelectorIndices(int[] selectorIndices) { + for (int i = 0; i < selectorIndices.length - 1; i++) { + selectorIndices[i] = selectorIndices[i + 1]; + } + int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1; + if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) { + nextScrollSelectorIndex = mMinValue; + } + selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex; + ensureCachedScrollSelectorValue(nextScrollSelectorIndex); + } + + /** + * Decrements the <code>selectorIndices</code> whose string representations + * will be displayed in the selector. + */ + private void decrementSelectorIndices(int[] selectorIndices) { + for (int i = selectorIndices.length - 1; i > 0; i--) { + selectorIndices[i] = selectorIndices[i - 1]; + } + int nextScrollSelectorIndex = selectorIndices[1] - 1; + if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) { + nextScrollSelectorIndex = mMaxValue; + } + selectorIndices[0] = nextScrollSelectorIndex; + ensureCachedScrollSelectorValue(nextScrollSelectorIndex); + } + + /** + * Ensures we have a cached string representation of the given <code> + * selectorIndex</code> + * to avoid multiple instantiations of the same string. + */ + private void ensureCachedScrollSelectorValue(int selectorIndex) { + SparseArray<String> cache = mSelectorIndexToStringCache; + String scrollSelectorValue = cache.get(selectorIndex); + if (scrollSelectorValue != null) { + return; + } + if (selectorIndex < mMinValue || selectorIndex > mMaxValue) { + scrollSelectorValue = ""; + } else { + if (mDisplayedValues != null) { + int displayedValueIndex = selectorIndex - mMinValue; + scrollSelectorValue = mDisplayedValues[displayedValueIndex]; + } else { + scrollSelectorValue = formatNumber(selectorIndex); + } + } + cache.put(selectorIndex, scrollSelectorValue); + } + + private String formatNumber(int value) { + return (mFormatter != null) ? mFormatter.format(value) : String + .valueOf(value); + } + + private void validateInputTextView(View v) { + String str = String.valueOf(((TextView) v).getText()); + if (TextUtils.isEmpty(str)) { + // Restore to the old value as we don't allow empty values + updateInputTextView(); + } else { + // Check the new value and ensure it's in range + int current = getSelectedPos(str.toString()); + changeCurrent(current); + } + } + + /** + * Updates the view of this NumberPicker. If displayValues were specified in + * the string corresponding to the index specified by the current value will + * be returned. Otherwise, the formatter specified in {@link #setFormatter} + * will be used to format the number. + */ + private void updateInputTextView() { + /* + * If we don't have displayed values then use the current number else + * find the correct value in the displayed values for the current + * number. + */ + // mark this 2 + if (mDisplayedValues == null) { +// mInputText.setText(TWO_DIGIT_FORMATTER.format(mValue)); + } else { +// mInputText.setText(mDisplayedValues[mValue - mMinValue]); + } +// mInputText.setSelection(mInputText.getText().length()); + + if (mFlingable + && AccessibilityManager.getInstance(mContext).isEnabled()) { +// String text = mContext.getString( +// R.string.number_picker_increment_scroll_mode, mInputText +// .getText()); +// mInputText.setContentDescription(text); + } + } + + /** + * Notifies the listener, if registered, of a change of the value of this + * NumberPicker. + */ + private void notifyChange(int previous, int current) { + if (mOnValueChangeListener != null) { + mOnValueChangeListener.onValueChange(this, previous, mValue); + } + } + + /** + * Posts a command for changing the current value by one. + * + * @param increment + * Whether to increment or decrement the value. + */ + private void postChangeCurrentByOneFromLongPress(boolean increment) { +// mInputText.clearFocus(); + removeAllCallbacks(); + if (mChangeCurrentByOneFromLongPressCommand == null) { + mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand(); + } + mChangeCurrentByOneFromLongPressCommand.setIncrement(increment); + post(mChangeCurrentByOneFromLongPressCommand); + } + + /** + * Removes all pending callback from the message queue. + */ + private void removeAllCallbacks() { + if (mChangeCurrentByOneFromLongPressCommand != null) { + removeCallbacks(mChangeCurrentByOneFromLongPressCommand); + } + if (mAdjustScrollerCommand != null) { + removeCallbacks(mAdjustScrollerCommand); + } + if (mSetSelectionCommand != null) { + removeCallbacks(mSetSelectionCommand); + } + } + + /** + * @return The selected index given its displayed <code>value</code>. + */ + private int getSelectedPos(String value) { + if (mDisplayedValues == null) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + // Ignore as if it's not a number we don't care + } + } else { + for (int i = 0; i < mDisplayedValues.length; i++) { + // Don't force the user to type in jan when ja will do + value = value.toLowerCase(); + if (mDisplayedValues[i].toLowerCase().startsWith(value)) { + return mMinValue + i; + } + } + + /* + * The user might have typed in a number into the month field i.e. + * 10 instead of OCT so support that too. + */ + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + + // Ignore as if it's not a number we don't care + } + } + return mMinValue; + } + + /** + * Posts an {@link SetSelectionCommand} from the given <code>selectionStart + * </code> to + * <code>selectionEnd</code>. + */ + private void postSetSelectionCommand(int selectionStart, int selectionEnd) { + if (mSetSelectionCommand == null) { + mSetSelectionCommand = new SetSelectionCommand(); + } else { + removeCallbacks(mSetSelectionCommand); + } + mSetSelectionCommand.mSelectionStart = selectionStart; + mSetSelectionCommand.mSelectionEnd = selectionEnd; + post(mSetSelectionCommand); + } + + /** + * Posts an {@link AdjustScrollerCommand} within the given <code> + * delayMillis</code> + * . + */ + private void postAdjustScrollerCommand(int delayMillis) { + if (mAdjustScrollerCommand == null) { + mAdjustScrollerCommand = new AdjustScrollerCommand(); + } else { + removeCallbacks(mAdjustScrollerCommand); + } + postDelayed(mAdjustScrollerCommand, delayMillis); + } + + /** + * Filter for accepting only valid indices or prefixes of the string + * representation of valid indices. + */ + class InputTextFilter extends NumberKeyListener { + + // XXX This doesn't allow for range limits when controlled by a + // soft input method! + public int getInputType() { + return InputType.TYPE_CLASS_TEXT; + } + + @Override + protected char[] getAcceptedChars() { + return DIGIT_CHARACTERS; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + if (mDisplayedValues == null) { + CharSequence filtered = super.filter(source, start, end, dest, + dstart, dend); + if (filtered == null) { + filtered = source.subSequence(start, end); + } + + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + dest.subSequence(dend, dest.length()); + + if ("".equals(result)) { + return result; + } + int val = getSelectedPos(result); + + /* + * Ensure the user can't type in a value greater than the max + * allowed. We have to allow less than min as the user might + * want to delete some numbers and then type a new number. + */ + if (val > mMaxValue) { + return ""; + } else { + return filtered; + } + } else { + CharSequence filtered = String.valueOf(source.subSequence( + start, end)); + if (TextUtils.isEmpty(filtered)) { + return ""; + } + String result = String.valueOf(dest.subSequence(0, dstart)) + + filtered + dest.subSequence(dend, dest.length()); + String str = String.valueOf(result).toLowerCase(); + for (String val : mDisplayedValues) { + String valLowerCase = val.toLowerCase(); + if (valLowerCase.startsWith(str)) { + postSetSelectionCommand(result.length(), val.length()); + return val.subSequence(dstart, val.length()); + } + } + return ""; + } + } + } + + /** + * Command for setting the input text selection. + */ + class SetSelectionCommand implements Runnable { + private int mSelectionStart; + + private int mSelectionEnd; + + public void run() { +// mInputText.setSelection(mSelectionStart, mSelectionEnd); + } + } + + /** + * Command for adjusting the scroller to show in its center the closest of + * the displayed items. + */ + class AdjustScrollerCommand implements Runnable { + public void run() { + mPreviousScrollerY = 0; + mPreviousScrollerX = 0; + if (mInitialScrollOffset == mCurrentScrollOffset) { + return; + } + if (mHorizontal) { + // adjust to the closest value + int deltaX = mInitialScrollOffset - mCurrentScrollOffset; + mAdjustScroller.startScroll(0, 0, deltaX, 0, + SELECTOR_ADJUSTMENT_DURATION_MILLIS); + } else { + // adjust to the closest value + int deltaY = mInitialScrollOffset - mCurrentScrollOffset; + if (Math.abs(deltaY) > mSelectorElementHeight / 2) { + deltaY += (deltaY > 0) ? -mSelectorElementHeight + : mSelectorElementHeight; + } + mAdjustScroller.startScroll(0, 0, 0, deltaY, + SELECTOR_ADJUSTMENT_DURATION_MILLIS); + } + + invalidate(); + } + } + + /** + * Command for changing the current value from a long press by one. + */ + class ChangeCurrentByOneFromLongPressCommand implements Runnable { + private boolean mIncrement; + + private void setIncrement(boolean increment) { + mIncrement = increment; + } + + public void run() { + changeCurrentByOne(mIncrement); + postDelayed(this, mLongPressUpdateInterval); + } + } +} diff --git a/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl new file mode 100644 index 0000000..7358149 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl @@ -0,0 +1,57 @@ +package com.caf.fmradio; + +import com.caf.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(); +} + diff --git a/fmapp2/src/com/caf/fmradio/IFMRadioServiceCallbacks.aidl b/fmapp2/src/com/caf/fmradio/IFMRadioServiceCallbacks.aidl new file mode 100644 index 0000000..1926f24 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/fmradio/IFMTransmitterService.aidl b/fmapp2/src/com/caf/fmradio/IFMTransmitterService.aidl new file mode 100644 index 0000000..79e01de --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/IFMTransmitterService.aidl @@ -0,0 +1,23 @@ +package com.caf.fmradio; + +import com.caf.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/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl b/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl new file mode 100644 index 0000000..641785d --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/IFMTransmitterServiceCallbacks.aidl @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package com.caf.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/fmapp2/src/com/caf/fmradio/PresetList.java b/fmapp2/src/com/caf/fmradio/PresetList.java new file mode 100644 index 0000000..b1a0214 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/fmradio/PresetStation.java b/fmapp2/src/com/caf/fmradio/PresetStation.java new file mode 100644 index 0000000..77bf648 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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) &&!TextUtils.isEmpty(name.trim())) + { + 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/fmapp2/src/com/caf/fmradio/Settings.java b/fmapp2/src/com/caf/fmradio/Settings.java new file mode 100644 index 0000000..f5c5d07 --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/Settings.java @@ -0,0 +1,397 @@ +/* + * 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.caf.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 java.util.Locale; + +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.caf.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 clearStationList() { + SharedPreferences sp = getSharedPreferences(FMRadio.SCAN_STATION_PREFS_NAME, 0); + SharedPreferences.Editor editor = sp.edit(); + editor.clear(); + editor.commit(); + } + 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(); + } + clearStationList(); + }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) { + if (Locale.getDefault().equals(Locale.CHINA)) { + mBandPreference + .setValueIndex(FmSharedPreferences.REGIONAL_BAND_CHINA); + }else { + mBandPreference + .setValueIndex(FmSharedPreferences.REGIONAL_BAND_NORTH_AMERICA); + } + if (mRxMode) { + mAudioPreference.setValueIndex(0); + if (FMRadio.RECORDING_ENABLE) { + mRecordDurPreference.setValueIndex(0); + } + mAfPref.setChecked(true); + FmSharedPreferences.SetDefaults(); + }else { + if (Locale.getDefault().equals(Locale.CHINA)) { + FmSharedPreferences + .setCountry(FmSharedPreferences.REGIONAL_BAND_CHINA); + }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/fmapp2/src/com/caf/fmradio/StationListActivity.java b/fmapp2/src/com/caf/fmradio/StationListActivity.java new file mode 100644 index 0000000..46b59ad --- /dev/null +++ b/fmapp2/src/com/caf/fmradio/StationListActivity.java @@ -0,0 +1,341 @@ +/* + * 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 "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.caf.fmradio; + +import java.util.ArrayList; +import java.util.HashMap; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.Handler.Callback; +import android.text.TextUtils; +import android.util.Log; +import android.view.ContextMenu; +import android.view.Gravity; +import android.view.MenuItem; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.SimpleAdapter; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; + +public class StationListActivity extends Activity implements + View.OnCreateContextMenuListener { + + private static final String LOGTAG = "StationList"; + private ListView mStationList; + private PresetList mPresetList; + private SimpleAdapter mAdapter; + private static IFMRadioService mService = null; + private static final int CONTEXT_MENU_RENAME = 1; + private static final int CONTEXT_MENU_DELETE = 2; + + static final int DIALOG_RENAME_ID = 0; + static final int DIALOG_DELETE_ID = 1; + // private static String mDialogTitle; + private static String mDialogStationName; + private static int mItemId; + private Dialog mRenameDialog; + private Dialog mDeleteDialog; + //whether is the first headset plug event + private boolean isFirst = true; + private ServiceConnection osc = new ServiceConnection() { + public void onServiceConnected(ComponentName classname, IBinder obj) { + mService = IFMRadioService.Stub.asInterface(obj); + if (mService != null) { + return; + } else { + Log.e(LOGTAG, "IFMRadioService onServiceConnected failed"); + } + } + + public void onServiceDisconnected(ComponentName classname) { + } + }; + + ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>(); + HashMap<Integer, Integer> mIndex = new HashMap<Integer, Integer>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.station_list); + bindService((new Intent()).setClass(this, FMRadioService.class), osc, 0); + + mStationList = (ListView) findViewById(R.id.station_list); + // mPresetList = new PresetList("StationList"); + mStationList.setOnCreateContextMenuListener(this); + mStationList.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, + long arg3) { + String freq = ((HashMap<String, String>) mAdapter.getItem(arg2)) + .get("freq"); + Float fFreq = Float.parseFloat(freq); + if (mService != null) { + try { + mService.tune((int) ((fFreq * 1000))); + } catch (RemoteException e) { + e.printStackTrace(); + } + } else { + Log.d(LOGTAG, "mService is null........"); + } + } + }); + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + // TODO Auto-generated method stub + AdapterContextMenuInfo mi = (AdapterContextMenuInfo) item.getMenuInfo(); + switch (item.getItemId()) { + case CONTEXT_MENU_RENAME: + showDialog(DIALOG_RENAME_ID); + break; + case CONTEXT_MENU_DELETE: + showDialog(DIALOG_DELETE_ID); + break; + + } + + return true; + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + // TODO Auto-generated method stub + AdapterContextMenuInfo mi = (AdapterContextMenuInfo) menuInfo; + menu.add(0, CONTEXT_MENU_RENAME, 0, getString(R.string.preset_rename)); + menu.add(0, CONTEXT_MENU_DELETE, 0, getString(R.string.preset_delete)); + mItemId = mi.position; + menu.setHeaderTitle(getString(R.string.station_name)+getNameFromId(mItemId)); + } + + @Override + protected void onResume() { + // TODO Auto-generated method stub + load(); + super.onResume(); + } + + private boolean stationNameExist(String name) { + for (HashMap<String, String> item : list) { + if (item != null && name.equals(item.get("name"))) { + return true; + } + } + return false; + } + + @Override + protected void onPrepareDialog(int id, Dialog dialog, Bundle b) { + // TODO Auto-generated method stub + // super.onPrepareDialog(id, dialog); + // After change system language, current function will be executed before + // onResume , so execute load to ensure adapter is not null. + load(); + switch (id) { + case DIALOG_RENAME_ID: + mRenameDialog.setTitle(getString(R.string.station_name)+getNameFromId(mItemId)); + final EditText editText = (EditText) mRenameDialog + .findViewById(R.id.name); + editText.setText(getNameFromId(mItemId)); + Button bOk = (Button) mRenameDialog.findViewById(R.id.save); + + bOk.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String rename = editText.getText().toString(); + if (TextUtils.isEmpty(rename) || TextUtils.isEmpty(rename.trim())) { + Context context = getApplicationContext(); + Toast toast = Toast.makeText(context, getString(R.string.station_name_empty), + Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL, 0, 0); + toast.show(); + } else if (stationNameExist(rename)) { + Context context = getApplicationContext(); + Toast toast = Toast.makeText(context, + getString(R.string.station_name_exist, rename), Toast.LENGTH_SHORT); + toast.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL, 0, 0); + toast.show(); + } else { + saveStationName(mItemId,rename); + mRenameDialog.dismiss(); + } + } + + }); + Button bCancel = (Button) mRenameDialog.findViewById(R.id.cancel); + bCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // TODO Auto-generated method stub + mRenameDialog.dismiss(); + } + }); + break; + case DIALOG_DELETE_ID: + mDeleteDialog.setTitle(getString(R.string.station_list_delete_station, getNameFromId(mItemId))); + TextView prompt = (TextView) mDeleteDialog.findViewById(R.id.prompt); + prompt.setText(getString(R.string.station_list_delete_station_prompt,getNameFromId(mItemId))); + Button bDelete = (Button) mDeleteDialog.findViewById(R.id.delete); + + bDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + deleteStation(mItemId); + mDeleteDialog.dismiss(); + } + }); + Button bCancelDelete = (Button) mDeleteDialog.findViewById(R.id.cancel); + bCancelDelete.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mDeleteDialog.dismiss(); + } + }); + break; + } + } + + @Override + protected Dialog onCreateDialog(int id, Bundle b) { + // TODO Auto-generated method stub + //should return different dialog + switch (id) { + case DIALOG_RENAME_ID: + mRenameDialog = new Dialog(this); + mRenameDialog.setContentView(R.layout.rename_dialog); + return mRenameDialog; + case DIALOG_DELETE_ID: + mDeleteDialog = new Dialog(this); + mDeleteDialog.setContentView(R.layout.delete_dialog); + return mDeleteDialog; + default: + ; + } + return null; + } + + private void saveStationName(int id, String name) { + Integer stationIndex = mIndex.get(new Integer(id)); + SharedPreferences sp = getSharedPreferences( + FMRadio.SCAN_STATION_PREFS_NAME, 0); + SharedPreferences.Editor editor = sp.edit(); + editor + .putString(FMRadio.STATION_NAME + (stationIndex.intValue()), + name); + editor.commit(); + load(); + } + + private void deleteStation(int id) { + SharedPreferences sp = getSharedPreferences( + FMRadio.SCAN_STATION_PREFS_NAME, 0); + Integer stationIndex = mIndex.get(new Integer(id)); + SharedPreferences.Editor editor = sp.edit(); + editor.remove(FMRadio.STATION_NAME + (stationIndex)); + editor.remove(FMRadio.STATION_FREQUENCY + (stationIndex)); + editor.commit(); + load(); + } + + private int getFrequencyFromId(int id) { + String freq = ((HashMap<String, String>) mAdapter.getItem(id)) + .get("freq"); + Float fFreq = Float.parseFloat(freq); + return (int) ((fFreq * 1000)); + } + + private String getNameFromId(int id) { + String name = ((HashMap<String, String>) mAdapter.getItem(id)) + .get("name"); + return name; + + } + + protected void load() { + list.clear(); + mIndex.clear(); + SharedPreferences sp = getSharedPreferences( + FMRadio.SCAN_STATION_PREFS_NAME, 0); + int station_number = sp.getInt(FMRadio.NUM_OF_STATIONS, 0); + for (int i = 1; i <= station_number; i++) { + HashMap<String, String> item = new HashMap<String, String>(); + String name = sp.getString(FMRadio.STATION_NAME + i, ""); + + int frequency = sp.getInt(FMRadio.STATION_FREQUENCY + i, 0); + + if (!name.equals("") && frequency != 0) { + item.put("name", name); + item.put("freq", String.valueOf(frequency / 1000.0f)); + mIndex.put(new Integer(list.size()), new Integer(i)); + list.add(item); + } + } + + mAdapter = new SimpleAdapter(this, list, R.layout.station_list_item, + new String[] { "name", "freq" }, new int[] { R.id.name, + R.id.freq }); + + mStationList.setAdapter(mAdapter); + } + + @Override + protected void onDestroy() { + unbindService(osc); + mService = null; + super.onDestroy(); + } + +} diff --git a/fmapp2/src/com/caf/hc_utils/A2dpDeviceStatus.java b/fmapp2/src/com/caf/hc_utils/A2dpDeviceStatus.java new file mode 100644 index 0000000..45b647e --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/hc_utils/FrequencyPicker.java b/fmapp2/src/com/caf/hc_utils/FrequencyPicker.java new file mode 100644 index 0000000..c88bb0a --- /dev/null +++ b/fmapp2/src/com/caf/hc_utils/FrequencyPicker.java @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. + * Not a Contribution + * + * 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.caf.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.caf.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/fmapp2/src/com/caf/hc_utils/FrequencyPickerDialog.java b/fmapp2/src/com/caf/hc_utils/FrequencyPickerDialog.java new file mode 100644 index 0000000..cac7dc1 --- /dev/null +++ b/fmapp2/src/com/caf/hc_utils/FrequencyPickerDialog.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved. + * Not a Contribution + * + * 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.caf.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.caf.fmradio.R; +import com.caf.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/fmapp2/src/com/caf/utils/A2dpDeviceStatus.java b/fmapp2/src/com/caf/utils/A2dpDeviceStatus.java new file mode 100644 index 0000000..3f1c010 --- /dev/null +++ b/fmapp2/src/com/caf/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.caf.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/fmapp2/src/com/caf/utils/FrequencyPicker.java b/fmapp2/src/com/caf/utils/FrequencyPicker.java new file mode 100644 index 0000000..1ea10e8 --- /dev/null +++ b/fmapp2/src/com/caf/utils/FrequencyPicker.java @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * 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.caf.utils; + + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.caf.fmradio_2.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); + return (frequencykhz); + } + + private int getFrequency50KHz(int kHz) + { + int frequencykhz = ((kHz - 1) * 50); + return (frequencykhz); + } + + private int getFrequency200KHz(int kHz, int min) + { + int frequencykhz = ((kHz - 1) * 200); + if((min % 200) != 0) + { + frequencykhz = ((kHz - 1) * 200) + 100; + } + return (frequencykhz); + } + + private int getCurrent100KHz(int frequency) + { + int khz = ((frequency % 1000) / 100); + return (khz + 1); + } + + private int getCurrent50KHz(int frequency) + { + int khz = ((frequency % 1000) / 50); + return (khz + 1); + } + + private int getCurrent200KHz(int frequency, int min) + { + int khz = ((frequency % 1000) / 200); + 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"}; + return khzStrings; + } + + private String[] get100KHzStrings() + { + String[] khzStrings= {"0","1","2","3","4","5", + "6","7","8","9"}; + 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"}; + return khzStrings; + } + private String[] getOdd200KHzStrings() + { + String[] khzStrings= {"1","3","5","7","9"}; + 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/fmapp2/src/com/caf/utils/FrequencyPickerDialog.java b/fmapp2/src/com/caf/utils/FrequencyPickerDialog.java new file mode 100644 index 0000000..3c6168b --- /dev/null +++ b/fmapp2/src/com/caf/utils/FrequencyPickerDialog.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Not a Contribution + * + * 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.caf.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.caf.fmradio.R; +import com.caf.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); + } +} |