1 /*
2  * Copyright (C) 2018 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.car.Car;
20 import android.content.Intent;
21 import android.graphics.drawable.Drawable;
22 import android.os.Bundle;
23 import android.view.KeyEvent;
24 
25 import androidx.annotation.NonNull;
26 import androidx.fragment.app.FragmentActivity;
27 import androidx.viewpager.widget.ViewPager;
28 
29 import com.android.car.media.common.source.MediaSource;
30 import com.android.car.media.common.source.MediaSourceViewModel;
31 import com.android.car.radio.bands.ProgramType;
32 import com.android.car.radio.util.Log;
33 import com.android.car.ui.toolbar.MenuItem;
34 import com.android.car.ui.toolbar.TabLayout;
35 import com.android.car.ui.toolbar.Toolbar;
36 
37 import java.util.Arrays;
38 import java.util.List;
39 
40 /**
41  * The main activity for the radio app.
42  */
43 public class RadioActivity extends FragmentActivity {
44     private static final String TAG = "BcRadioApp.activity";
45 
46     /**
47      * Intent action for notifying that the radio state has changed.
48      */
49     private static final String ACTION_RADIO_APP_STATE_CHANGE =
50             "android.intent.action.RADIO_APP_STATE_CHANGE";
51 
52     /**
53      * Boolean Intent extra indicating if the radio is the currently in the foreground.
54      */
55     private static final String EXTRA_RADIO_APP_FOREGROUND =
56             "android.intent.action.RADIO_APP_STATE";
57 
58     private RadioController mRadioController;
59     private BandController mBandController = new BandController();
60     private Toolbar mToolbar;
61     private RadioPagerAdapter mRadioPagerAdapter;
62 
63     @Override
onCreate(Bundle savedInstanceState)64     protected void onCreate(Bundle savedInstanceState) {
65         super.onCreate(savedInstanceState);
66 
67         Log.d(TAG, "Radio app main activity created");
68 
69         setContentView(R.layout.radio_activity);
70 
71         mRadioController = new RadioController(this);
72         mRadioController.getCurrentProgram().observe(this, info -> {
73             ProgramType programType = ProgramType.fromSelector(info.getSelector());
74             if (programType != null) {
75                 mBandController.setType(programType);
76                 updateMenuItems();
77             }
78         });
79 
80         mRadioPagerAdapter =
81                 new RadioPagerAdapter(this, getSupportFragmentManager(), mRadioController);
82         ViewPager viewPager = findViewById(R.id.viewpager);
83         viewPager.setAdapter(mRadioPagerAdapter);
84 
85         mToolbar = requireViewById(R.id.toolbar);
86         mToolbar.registerOnTabSelectedListener(t ->
87                 viewPager.setCurrentItem(mToolbar.getTabLayout().getTabPosition(t)));
88 
89         updateMenuItems();
90         setupTabsWithViewPager(viewPager);
91 
92         MediaSourceViewModel model = MediaSourceViewModel.get(getApplication());
93         model.getPrimaryMediaSource().observe(this, source -> {
94             if (source != null) {
95                 // Always go through the trampoline activity to keep all the dispatching logic
96                 // there.
97                 startActivity(new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE));
98             }
99         });
100     }
101 
102     @Override
onStart()103     protected void onStart() {
104         super.onStart();
105 
106         mRadioController.start();
107 
108         Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE);
109         broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, true);
110         sendBroadcast(broadcast);
111     }
112 
113     @Override
onStop()114     protected void onStop() {
115         super.onStop();
116 
117         Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE);
118         broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, false);
119         sendBroadcast(broadcast);
120     }
121 
122     @Override
onDestroy()123     protected void onDestroy() {
124         super.onDestroy();
125 
126         mRadioController.shutdown();
127 
128         Log.d(TAG, "Radio app main activity destroyed");
129     }
130 
131     @Override
onKeyDown(int keyCode, KeyEvent event)132     public boolean onKeyDown(int keyCode, KeyEvent event) {
133         switch (keyCode) {
134             case KeyEvent.KEYCODE_MEDIA_STEP_FORWARD:
135                 mRadioController.step(true);
136                 return true;
137             case KeyEvent.KEYCODE_MEDIA_STEP_BACKWARD:
138                 mRadioController.step(false);
139                 return true;
140             default:
141                 return super.onKeyDown(keyCode, event);
142         }
143     }
144 
145     /**
146      * Set whether background scanning is supported, to know whether to show the browse tab or not.
147      */
setProgramListSupported(boolean supported)148     public void setProgramListSupported(boolean supported) {
149         if (supported && mRadioPagerAdapter.addBrowseTab()) {
150             updateTabs();
151         }
152     }
153 
154     /**
155      * Sets supported program types.
156      */
setSupportedProgramTypes(@onNull List<ProgramType> supported)157     public void setSupportedProgramTypes(@NonNull List<ProgramType> supported) {
158         mBandController.setSupportedProgramTypes(supported);
159     }
160 
setupTabsWithViewPager(ViewPager viewPager)161     private void setupTabsWithViewPager(ViewPager viewPager) {
162         viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
163             @Override
164             public void onPageSelected(int position) {
165                 mToolbar.selectTab(position);
166             }
167         });
168         updateTabs();
169     }
170 
updateMenuItems()171     private void updateMenuItems() {
172         ProgramType currentBand = mBandController.getCurrentBand();
173         MenuItem bandSelectorMenuItem = MenuItem.builder(this)
174                 .setIcon(currentBand != null ? currentBand.getResourceId() : 0)
175                 .setOnClickListener(i -> {
176                     ProgramType programType = mBandController.switchToNext();
177                     mRadioController.switchBand(programType);
178                 })
179                 .build();
180 
181         Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(this, false);
182         MenuItem appSelectorMenuItem = MenuItem.builder(this)
183                 .setIcon(R.drawable.ic_app_switch)
184                 .setOnClickListener(m -> startActivity(appSelectorIntent))
185                 .build();
186 
187         mToolbar.setMenuItems(Arrays.asList(bandSelectorMenuItem, appSelectorMenuItem));
188     }
189 
updateTabs()190     private void updateTabs() {
191         mToolbar.clearAllTabs();
192         for (int i = 0; i < mRadioPagerAdapter.getCount(); i++) {
193             Drawable icon = mRadioPagerAdapter.getPageIcon(i);
194             CharSequence title = mRadioPagerAdapter.getPageTitle(i);
195             mToolbar.addTab(new TabLayout.Tab(icon, title));
196         }
197     }
198 }
199