1 /* 2 * Copyright (C) 2017 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 android.car.cluster.sample; 17 18 import static android.car.cluster.sample.SampleClusterServiceImpl.LOCAL_BINDING_ACTION; 19 20 import android.car.cluster.sample.SampleClusterServiceImpl.Listener; 21 import android.content.ComponentName; 22 import android.content.Intent; 23 import android.content.ServiceConnection; 24 import android.os.Bundle; 25 import android.os.IBinder; 26 import android.util.Log; 27 import android.util.SparseArray; 28 import android.view.InputDevice; 29 import android.view.KeyEvent; 30 import android.view.View; 31 import android.view.inputmethod.InputMethodManager; 32 import android.widget.Button; 33 34 import androidx.fragment.app.Fragment; 35 import androidx.fragment.app.FragmentActivity; 36 import androidx.fragment.app.FragmentManager; 37 import androidx.fragment.app.FragmentPagerAdapter; 38 import androidx.viewpager.widget.ViewPager; 39 40 import java.lang.reflect.InvocationTargetException; 41 import java.util.HashMap; 42 43 public class MainClusterActivity extends FragmentActivity 44 implements Listener { 45 private static final String TAG = MainClusterActivity.class.getSimpleName(); 46 47 private ViewPager mPager; 48 49 private SampleClusterServiceImpl mService; 50 51 private HashMap<Button, Facet<?>> mButtonToFacet = new HashMap<>(); 52 private SparseArray<Facet<?>> mOrderToFacet = new SparseArray<>(); 53 54 private InputMethodManager mInputMethodManager; 55 56 private final View.OnFocusChangeListener mFacetButtonFocusListener = 57 new View.OnFocusChangeListener() { 58 @Override 59 public void onFocusChange(View v, boolean hasFocus) { 60 if (hasFocus) { 61 mPager.setCurrentItem(mButtonToFacet.get(v).order); 62 } 63 } 64 }; 65 66 @Override 67 protected void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 setContentView(R.layout.activity_main); 70 71 mInputMethodManager = getSystemService(InputMethodManager.class); 72 73 Intent intent = new Intent(this, SampleClusterServiceImpl.class); 74 intent.setAction(LOCAL_BINDING_ACTION); 75 bindService(intent, 76 new ServiceConnection() { 77 @Override 78 public void onServiceConnected(ComponentName name, IBinder service) { 79 Log.i(TAG, "onServiceConnected, name: " + name + ", service: " + service); 80 mService = ((SampleClusterServiceImpl.LocalBinder) service) 81 .getService(); 82 mService.registerListener(MainClusterActivity.this); 83 } 84 85 @Override 86 public void onServiceDisconnected(ComponentName name) { 87 Log.i(TAG, "onServiceDisconnected, name: " + name); 88 mService = null; 89 } 90 }, BIND_AUTO_CREATE); 91 92 registerFacets( 93 new Facet<>(findViewById(R.id.btn_nav), 0, NavigationFragment.class), 94 new Facet<>(findViewById(R.id.btn_phone), 1, PhoneFragment.class), 95 new Facet<>(findViewById(R.id.btn_music), 2, MusicFragment.class), 96 new Facet<>(findViewById(R.id.btn_car_info), 3, CarInfoFragment.class)); 97 98 mPager = findViewById(R.id.pager); 99 mPager.setAdapter(new ClusterPageAdapter(getSupportFragmentManager())); 100 mOrderToFacet.get(0).button.requestFocus(); 101 } 102 103 @Override 104 protected void onDestroy() { 105 super.onDestroy(); 106 if (mService != null) { 107 mService.unregisterListener(); 108 } 109 } 110 111 @Override 112 public void onKeyEvent(KeyEvent event) { 113 Log.i(TAG, "onKeyEvent, event: " + event); 114 115 // This is a hack. We use SOURCE_CLASS_POINTER here because this type of input is associated 116 // with the display. otherwise this event will be ignored in ViewRootImpl because injecting 117 // KeyEvent w/o activity being focused is useless. 118 event.setSource(event.getSource() | InputDevice.SOURCE_CLASS_POINTER); 119 mInputMethodManager.dispatchKeyEventFromInputMethod(getCurrentFocus(), event); 120 } 121 122 123 public class ClusterPageAdapter extends FragmentPagerAdapter { 124 public ClusterPageAdapter(FragmentManager fm) { 125 super(fm); 126 } 127 128 @Override 129 public int getCount() { 130 return mButtonToFacet.size(); 131 } 132 133 @Override 134 public Fragment getItem(int position) { 135 return mOrderToFacet.get(position).getOrCreateFragment(); 136 } 137 } 138 139 private void registerFacets(Facet<?>... facets) { 140 for (Facet<?> f : facets) { 141 registerFacet(f); 142 } 143 } 144 145 private <T> void registerFacet(Facet<T> facet) { 146 mOrderToFacet.append(facet.order, facet); 147 mButtonToFacet.put(facet.button, facet); 148 149 facet.button.setOnFocusChangeListener(mFacetButtonFocusListener); 150 } 151 152 private static class Facet<T> { 153 Button button; 154 Class<T> clazz; 155 int order; 156 157 Facet(Button button, int order, Class<T> clazz) { 158 this.button = button; 159 this.order = order; 160 this.clazz = clazz; 161 } 162 163 private Fragment mFragment; 164 165 Fragment getOrCreateFragment() { 166 if (mFragment == null) { 167 try { 168 mFragment = (Fragment) clazz.getConstructors()[0].newInstance(); 169 } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { 170 throw new RuntimeException(e); 171 } 172 } 173 return mFragment; 174 } 175 } 176 177 SampleClusterServiceImpl getService() { 178 return mService; 179 } 180 } 181