1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.car.kitchensink.radio; 18 19 import android.hardware.radio.Flags; 20 import android.hardware.radio.ProgramSelector; 21 import android.hardware.radio.RadioManager; 22 import android.hardware.radio.RadioTuner; 23 import android.os.Handler; 24 import android.view.View; 25 import android.widget.Button; 26 import android.widget.EditText; 27 import android.widget.RadioButton; 28 import android.widget.RadioGroup; 29 import android.widget.Switch; 30 31 import com.android.car.broadcastradio.support.platform.ProgramSelectorExt; 32 33 import com.google.android.car.kitchensink.R; 34 35 public final class AmFmTunerFragment extends RadioTunerFragment { 36 37 private int mLastFmFrequency; 38 private int mLastAmFrequency; 39 private boolean mIsFmBand = true; 40 41 private RadioButton mFmRadioButton; 42 private RadioButton mAmRadioButton; 43 private Switch mFmHdSwitch; 44 private Switch mAmHdSwitch; 45 private EditText mFrequencyInput; 46 private RadioGroup mAmFmBandSelection; 47 AmFmTunerFragment(RadioManager radioManager, int moduleId, RadioManager.BandConfig fmBandConfig, RadioManager.BandConfig amBandConfig, Handler handler, RadioTestFragment.TunerListener tunerListener)48 AmFmTunerFragment(RadioManager radioManager, int moduleId, 49 RadioManager.BandConfig fmBandConfig, RadioManager.BandConfig amBandConfig, 50 Handler handler, RadioTestFragment.TunerListener tunerListener) { 51 super(radioManager, moduleId, handler, tunerListener); 52 mLastFmFrequency = fmBandConfig.getLowerLimit(); 53 mLastAmFrequency = amBandConfig.getLowerLimit(); 54 } 55 56 @Override setupTunerView(View view)57 void setupTunerView(View view) { 58 mFmHdSwitch = view.findViewById(R.id.toggle_fm_hd_state); 59 mAmHdSwitch = view.findViewById(R.id.toggle_am_hd_state); 60 mFrequencyInput = view.findViewById(R.id.input_am_fm_frequency); 61 mAmFmBandSelection = view.findViewById(R.id.button_fm_am_selection); 62 mFmRadioButton = view.findViewById(R.id.button_radio_fm); 63 mAmRadioButton = view.findViewById(R.id.button_radio_am); 64 Button tuneButton = view.findViewById(R.id.button_radio_tune); 65 Button stepUpButton = view.findViewById(R.id.button_radio_step_up); 66 Button stepDownButton = view.findViewById(R.id.button_radio_step_down); 67 68 mFmHdSwitch.setVisibility(View.VISIBLE); 69 view.findViewById(R.id.text_fm_hd_state).setVisibility(View.VISIBLE); 70 view.findViewById(R.id.layout_tune).setVisibility(View.VISIBLE); 71 view.findViewById(R.id.layout_step).setVisibility(View.VISIBLE); 72 73 if (Flags.hdRadioImproved()) { 74 if (mRadioTuner.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_AM)) { 75 mAmHdSwitch.setVisibility(View.VISIBLE); 76 view.findViewById(R.id.text_am_hd_state).setVisibility(View.VISIBLE); 77 mAmHdSwitch.setChecked(!mRadioTuner.isConfigFlagSet( 78 RadioManager.CONFIG_FORCE_ANALOG_AM)); 79 } 80 if (!mRadioTuner.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG_FM)) { 81 mFmHdSwitch.setVisibility(View.INVISIBLE); 82 view.findViewById(R.id.text_fm_hd_state).setVisibility(View.INVISIBLE); 83 } else { 84 mFmHdSwitch.setChecked(!mRadioTuner.isConfigFlagSet( 85 RadioManager.CONFIG_FORCE_ANALOG_FM)); 86 } 87 } else { 88 if (!mRadioTuner.isConfigFlagSupported(RadioManager.CONFIG_FORCE_ANALOG)) { 89 mFmHdSwitch.setVisibility(View.INVISIBLE); 90 view.findViewById(R.id.text_fm_hd_state).setVisibility(View.INVISIBLE); 91 } else { 92 mFmHdSwitch.setChecked(!mRadioTuner.isConfigFlagSet( 93 RadioManager.CONFIG_FORCE_ANALOG)); 94 } 95 } 96 mFmHdSwitch.setOnCheckedChangeListener( 97 (buttonView, isChecked) -> handleHdEnable(/* isFm= */ true, isChecked)); 98 mAmHdSwitch.setOnCheckedChangeListener( 99 (buttonView, isChecked) -> handleHdEnable(/* isFm= */ false, isChecked)); 100 tuneButton.setOnClickListener((v) -> tuneToInputStation()); 101 mFmRadioButton.setOnClickListener((v) -> handleBandSwitching(/* isFm= */ true)); 102 mAmRadioButton.setOnClickListener((v) -> handleBandSwitching(/* isFm= */ false)); 103 if (mIsFmBand) { 104 mFmRadioButton.setChecked(true); 105 mFrequencyInput.setText(ProgramSelectorExt.formatAmFmFrequency(mLastFmFrequency, 106 ProgramSelectorExt.NAME_NO_MODULATION)); 107 } else { 108 mAmRadioButton.setChecked(true); 109 mFrequencyInput.setText(ProgramSelectorExt.formatAmFmFrequency(mLastAmFrequency, 110 ProgramSelectorExt.NAME_NO_MODULATION)); 111 } 112 stepUpButton.setOnClickListener((v) -> handleStep(RadioTuner.DIRECTION_UP)); 113 stepDownButton.setOnClickListener((v) -> handleStep(RadioTuner.DIRECTION_DOWN)); 114 115 mProgramInfoAdapter = new AmFmProgramInfoAdapter(getContext(), R.layout.program_info_item, 116 new RadioManager.ProgramInfo[]{}, this); 117 } 118 tuneToInputStation()119 private void tuneToInputStation() { 120 int selectedButtonId = mAmFmBandSelection.getCheckedRadioButtonId(); 121 ProgramSelector sel; 122 try { 123 double frequencyInput = Double.parseDouble(mFrequencyInput.getText().toString()); 124 switch (selectedButtonId) { 125 case R.id.button_radio_fm: 126 int fmFrequency = (int) Math.round(frequencyInput * 1_000); 127 sel = ProgramSelector.createAmFmSelector(RadioManager.BAND_FM, fmFrequency); 128 break; 129 case R.id.button_radio_am: 130 int amFrequency = (int) Math.round(frequencyInput); 131 sel = ProgramSelector.createAmFmSelector(RadioManager.BAND_AM, amFrequency); 132 break; 133 default: 134 mTuningTextView.setText(getString(R.string.radio_error, 135 "Unsupported input selector type")); 136 return; 137 } 138 } catch (Exception e) { 139 mTuningTextView.setText(getString(R.string.radio_error, e.getMessage())); 140 return; 141 } 142 handleTune(sel); 143 } 144 handleBandSwitching(boolean isFm)145 private void handleBandSwitching(boolean isFm) { 146 if (isFm) { 147 mFrequencyInput.setText(ProgramSelectorExt.formatAmFmFrequency(mLastFmFrequency, 148 ProgramSelectorExt.NAME_NO_MODULATION)); 149 } else { 150 mFrequencyInput.setText(ProgramSelectorExt.formatAmFmFrequency(mLastAmFrequency, 151 ProgramSelectorExt.NAME_NO_MODULATION)); 152 } 153 mIsFmBand = isFm; 154 ProgramSelector initialSel = isFm ? ProgramSelector.createAmFmSelector( 155 RadioManager.BAND_FM, mLastFmFrequency) 156 : ProgramSelector.createAmFmSelector(RadioManager.BAND_AM, mLastAmFrequency); 157 handleTune(initialSel); 158 } 159 handleStep(int direction)160 private void handleStep(int direction) { 161 if (mRadioTuner == null) { 162 mTuningTextView.setText(getString(R.string.radio_error, NULL_TUNER_WARNING)); 163 return; 164 } 165 mTuningTextView.setText(getString(R.string.radio_status, TUNING_TEXT)); 166 try { 167 mRadioTuner.step(direction, /* skipSubChannel= */ false); 168 } catch (Exception e) { 169 mTuningTextView.setText(getString(R.string.radio_error, e.getMessage())); 170 } 171 mListener.onTunerPlay(); 172 } 173 handleHdEnable(boolean isFm, boolean hdEnabled)174 private void handleHdEnable(boolean isFm, boolean hdEnabled) { 175 if (mRadioTuner == null) { 176 mTuningTextView.setText(getString(R.string.radio_error, NULL_TUNER_WARNING)); 177 return; 178 } 179 mTuningTextView.setText(getString(R.string.empty)); 180 int configFlag; 181 if (Flags.hdRadioImproved()) { 182 configFlag = isFm ? RadioManager.CONFIG_FORCE_ANALOG_FM 183 : RadioManager.CONFIG_FORCE_ANALOG_AM; 184 } else { 185 configFlag = RadioManager.CONFIG_FORCE_ANALOG; 186 } 187 try { 188 mRadioTuner.setConfigFlag(configFlag, !hdEnabled); 189 } catch (Exception e) { 190 mTuningTextView.setText(getString(R.string.radio_error, e.getMessage())); 191 } 192 } 193 194 @Override getChannelName(RadioManager.ProgramInfo info)195 CharSequence getChannelName(RadioManager.ProgramInfo info) { 196 CharSequence channelText = null; 197 if (info != null) { 198 int primaryIdType = info.getSelector().getPrimaryId().getType(); 199 if (primaryIdType == ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY 200 || primaryIdType == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT) { 201 channelText = ProgramSelectorExt.getDisplayName(info.getSelector(), /* flags= */ 0); 202 long amFmFrequency = ProgramSelectorExt.getFrequency(info.getSelector()); 203 mIsFmBand = ProgramSelectorExt.isFmFrequency(amFmFrequency); 204 if (mIsFmBand) { 205 mLastFmFrequency = (int) amFmFrequency; 206 } else if (ProgramSelectorExt.isAmFrequency(amFmFrequency)) { 207 mLastAmFrequency = (int) amFmFrequency; 208 } 209 } 210 } 211 if (mViewCreated) { 212 if (channelText == null) { 213 channelText = getString(R.string.radio_na); 214 } 215 if (mIsFmBand) { 216 if (!mFmRadioButton.isChecked()) { 217 mFmRadioButton.setChecked(true); 218 } 219 } else { 220 if (!mAmRadioButton.isChecked()) { 221 mAmRadioButton.setChecked(true); 222 } 223 } 224 } 225 return channelText; 226 } 227 228 @Override updateConfigFlag(int flag, boolean value)229 void updateConfigFlag(int flag, boolean value) { 230 if (flag == RadioManager.CONFIG_FORCE_ANALOG) { 231 mFmHdSwitch.setChecked(!value); 232 mAmHdSwitch.setChecked(!value); 233 } else if (Flags.hdRadioImproved()) { 234 if (flag == RadioManager.CONFIG_FORCE_ANALOG_FM) { 235 mFmHdSwitch.setChecked(!value); 236 } else if (flag == RadioManager.CONFIG_FORCE_ANALOG_AM) { 237 mAmHdSwitch.setChecked(!value); 238 } 239 } 240 } 241 } 242