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 
17 package com.android.car.radio;
18 
19 import android.support.annotation.Nullable;
20 import android.util.Log;
21 import android.view.LayoutInflater;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.widget.TextView;
25 import com.android.car.radio.service.RadioStation;
26 
27 import java.util.List;
28 
29 /**
30  * A {@link com.android.car.radio.CarouselView.Adapter} that supplies the views to be displayed
31  * from a list of {@link RadioStation}s.
32  */
33 public class PrescannedRadioStationAdapter extends CarouselView.Adapter {
34     private static final String TAG = "PreScanAdapter";
35 
36     private List<RadioStation> mStations;
37     private int mCurrentPosition;
38 
39     /**
40      * Sets the {@link RadioStation}s that will be used to bind to the views to be displayed.
41      */
setStations(List<RadioStation> stations)42     public void setStations(List<RadioStation> stations) {
43         mStations = stations;
44         notifyDataSetChanged();
45     }
46 
47     /**
48      * Sets the the station within the list passed to {@link #setStations(List)} that should be
49      * the first station displayed. A station is identified by the channel number and band.
50      *
51      * @return The index within the list of stations passed to {@link #setStations(List)} that the
52      * starting station can be found at.
53      */
setStartingStation(int channelNumber, int band)54     public int setStartingStation(int channelNumber, int band) {
55         getIndexOrInsertForStation(channelNumber, band);
56         return mCurrentPosition;
57     }
58 
59     /**
60      * Returns the station that is currently the first station to be displayed. This value can be
61      * different from the value returned by {@link #setStartingStation(int, int)} if either
62      * {@link #getPrevStation()} or {@link #getNextStation()} have been called.
63      */
getCurrentPosition()64     public int getCurrentPosition() {
65         return mCurrentPosition;
66     }
67 
68     /**
69      * Returns the previous station in the list based off the value returned by
70      * {@link #getCurrentPosition()}. After calling this method, the current position returned by
71      * that method will be the index of the {@link RadioStation} returned by this method.
72      */
73     @Nullable
getPrevStation()74     public RadioStation getPrevStation() {
75         if (mStations == null) {
76             return null;
77         }
78 
79         if (--mCurrentPosition < 0) {
80             mCurrentPosition = mStations.size() - 1;
81         }
82 
83         return mStations.get(mCurrentPosition);
84     }
85 
86     /**
87      * Returns the next station in the list based off the value returned by
88      * {@link #getCurrentPosition()}. After calling this method, the current position returned by
89      * that method will be the index of the {@link RadioStation} returned by this method.
90      */
91     @Nullable
getNextStation()92     public RadioStation getNextStation() {
93         if (mStations == null) {
94             return null;
95         }
96 
97         if (++mCurrentPosition >= mStations.size()) {
98             mCurrentPosition = 0;
99         }
100 
101         return mStations.get(mCurrentPosition);
102     }
103 
104     /**
105      * Returns the index in the list set in {@link #setStations(List)} that corresponds to the
106      * given channel number and band. If the given combination does not exist within the list, then
107      * it will be inserted in ascending order.
108      *
109      * @return An index into the list or -1 if no list of stations has been set.
110      */
getIndexOrInsertForStation(int channelNumber, int band)111     public int getIndexOrInsertForStation(int channelNumber, int band) {
112         if (mStations == null) {
113             mCurrentPosition = -1;
114             return -1;
115         }
116 
117         int indexToInsert = 0;
118 
119         for (int i = 0, size = mStations.size(); i < size; i++) {
120             RadioStation station = mStations.get(i);
121 
122             if (channelNumber >= station.getChannelNumber()) {
123                 // Need to add 1 to the index because the channel should be inserted after it.
124                 indexToInsert = i + 1;
125             }
126 
127             if (station.getChannelNumber() == channelNumber && station.getRadioBand() == band) {
128                 mCurrentPosition = i;
129                 return i;
130             }
131         }
132 
133         // If this path is reached, that means an exact match for the station was not found in
134         // the given list. Instead, insert the station into the list and return that index.
135         RadioStation stationToInsert = new RadioStation(channelNumber, 0 /* subChannel */,
136                 band, null /* rds */);
137         mStations.add(indexToInsert, stationToInsert);
138         notifyDataSetChanged();
139 
140         mCurrentPosition = indexToInsert;
141         return indexToInsert;
142     }
143 
144     @Override
createView(ViewGroup parent)145     public View createView(ViewGroup parent) {
146         return LayoutInflater.from(parent.getContext()).inflate(R.layout.radio_channel, parent,
147                 false);
148     }
149 
150     @Override
bindView(View view, int position, boolean isFirstView)151     public void bindView(View view, int position, boolean isFirstView) {
152         if (Log.isLoggable(TAG, Log.DEBUG)) {
153             Log.d(TAG, "bindView(); position: " + position + "; isFirstView: " + isFirstView);
154         }
155 
156         if (mStations == null || position >= mStations.size()) {
157             return;
158         }
159 
160         TextView radioChannel = (TextView) view.findViewById(R.id.radio_list_station_channel);
161         TextView radioBandView = (TextView) view.findViewById(R.id.radio_list_station_band);
162 
163         RadioStation station = mStations.get(position);
164 
165         if (Log.isLoggable(TAG, Log.DEBUG)) {
166             Log.d(TAG, "binding station: " + station);
167         }
168 
169         int radioBand = station.getRadioBand();
170 
171         radioChannel.setText(RadioChannelFormatter.formatRadioChannel(radioBand,
172                 station.getChannelNumber()));
173 
174         if (isFirstView) {
175             radioBandView.setText(RadioChannelFormatter.formatRadioBand(view.getContext(),
176                     radioBand));
177         } else {
178             radioBandView.setText(null);
179         }
180     }
181 
182     @Override
getItemCount()183     public int getItemCount() {
184         return mStations == null ? 0 : mStations.size();
185     }
186 }
187