1 /*
2  * Copyright (c) 2016, 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 package com.android.car.radio;
17 
18 import android.content.Context;
19 import android.hardware.radio.ProgramSelector;
20 import android.view.View;
21 import android.widget.TextView;
22 
23 import androidx.annotation.NonNull;
24 
25 import com.android.car.radio.bands.ProgramType;
26 import com.android.car.radio.bands.RegionConfig;
27 import com.android.car.radio.widget.BandSelectorFlat;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Objects;
32 
33 public class ManualTunerController {
34     private final Object mLock = new Object();
35 
36     private final List<View> mDigitButtons = new ArrayList<>();
37     private final View mEnterButton;
38     private final TextView mChannelDisplay;
39     private BandSelectorFlat mBandSelector;
40 
41     private ProgramType mProgramType;
42     private final RegionConfig mRegionConfig;
43     private final TuningDoneListener mDoneListener;
44     private int mEnteredDigits;
45 
46     /**
47      * Listener for manual tuning done event.
48      */
49     public interface TuningDoneListener {
50         /**
51          * Called when the user accepted channel selection.
52          */
onDone(@onNull ProgramSelector sel)53         void onDone(@NonNull ProgramSelector sel);
54     }
55 
ManualTunerController(Context context, View container, @NonNull RegionConfig regionConfig, @NonNull TuningDoneListener doneListener)56     public ManualTunerController(Context context, View container,
57             @NonNull RegionConfig regionConfig, @NonNull TuningDoneListener doneListener) {
58         mRegionConfig = Objects.requireNonNull(regionConfig);
59         mDoneListener = Objects.requireNonNull(doneListener);
60 
61         mChannelDisplay = Objects.requireNonNull(container.findViewById(R.id.manual_tuner_channel));
62         mEnterButton = container.findViewById(R.id.manual_tuner_done_button);
63         mBandSelector = container.findViewById(R.id.manual_tuner_band_selector);
64 
65         mEnterButton.setOnClickListener(this::onDoneClick);
66         mBandSelector.setCallback(this::switchProgramType);
67         mBandSelector.setSupportedProgramTypes(regionConfig.getSupportedProgramTypes());
68 
69         View dialpad = container.findViewById(R.id.dialpad_layout);
70         View.OnClickListener digitClickListener = this::onDigitClick;
71         for (int i = 0; i <= 9; i++) {
72             View btn = dialpad.findViewWithTag(Integer.toString(i));
73             btn.setOnClickListener(digitClickListener);
74             mDigitButtons.add(i, btn);
75         }
76         View backspace = dialpad.findViewById(R.id.manual_tuner_backspace);
77         backspace.setOnClickListener(this::onBackspaceClick);
78 
79         switchProgramType(ProgramType.FM);
80     }
81 
updateDisplay()82     private void updateDisplay() {
83         synchronized (mLock) {
84             mChannelDisplay.setText(mProgramType.format(mEnteredDigits));
85             mEnterButton.setEnabled(mProgramType.isComplete(mRegionConfig, mEnteredDigits));
86             boolean[] valid = mProgramType.getValidAppendices(mRegionConfig, mEnteredDigits);
87             for (int i = 0; i < 10; i++) {
88                 mDigitButtons.get(i).setEnabled(valid[i]);
89             }
90         }
91     }
92 
onDigitClick(View btn)93     private void onDigitClick(View btn) {
94         int digit = Integer.parseInt((String) btn.getTag());
95         synchronized (mLock) {
96             mEnteredDigits *= 10;
97             mEnteredDigits += digit;
98         }
99         updateDisplay();
100     }
101 
onBackspaceClick(View btn)102     private void onBackspaceClick(View btn) {
103         synchronized (mLock) {
104             mEnteredDigits /= 10;
105         }
106         updateDisplay();
107     }
108 
onDoneClick(View btn)109     private void onDoneClick(View btn) {
110         synchronized (mLock) {
111             mDoneListener.onDone(mProgramType.parseDigits(mEnteredDigits));
112             mEnteredDigits = 0;
113         }
114         updateDisplay();
115     }
116 
117     /**
118      * Switches program type (band).
119      */
switchProgramType(@onNull ProgramType pt)120     public void switchProgramType(@NonNull ProgramType pt) {
121         Objects.requireNonNull(pt);
122         synchronized (mLock) {
123             if (mProgramType == pt) return;
124             mProgramType = pt;
125             mEnteredDigits = 0;
126             mBandSelector.setType(pt);
127         }
128         updateDisplay();
129     }
130 }
131