CarVolumeDialogImpl.java revision c8cdc593227638fdefbb9dbf2644fb8e4bf31fe6
1e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue/* 2e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * Copyright (C) 2018 The Android Open Source Project 3e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * 4e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * Licensed under the Apache License, Version 2.0 (the "License"); 5e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * you may not use this file except in compliance with the License. 6e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * You may obtain a copy of the License at 7e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * 8e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * http://www.apache.org/licenses/LICENSE-2.0 9e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * 10e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * Unless required by applicable law or agreed to in writing, software 11e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * distributed under the License is distributed on an "AS IS" BASIS, 12e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * See the License for the specific language governing permissions and 14e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * limitations under the License. 15e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue */ 16e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 17e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xuepackage com.android.systemui.volume; 18e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 19a65cf94800673efc7717b6ca4467051c1e332f76Lin Guoimport android.animation.Animator; 20a65cf94800673efc7717b6ca4467051c1e332f76Lin Guoimport android.animation.AnimatorInflater; 21a65cf94800673efc7717b6ca4467051c1e332f76Lin Guoimport android.animation.AnimatorSet; 2230ceb78bc1d9cdbd77d6a8e90383ee68b481051dLin Guoimport android.annotation.DrawableRes; 23aea00237c77545d670e0425f1839608905d253b3Lin Guoimport android.annotation.Nullable; 24e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.app.Dialog; 25e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.app.KeyguardManager; 26d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.Car; 27d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.CarNotConnectedException; 28d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.media.CarAudioManager; 29d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.media.ICarVolumeCallback; 30d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.ComponentName; 31e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.content.Context; 32e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.content.DialogInterface; 33d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.ServiceConnection; 34d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.res.TypedArray; 35d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.res.XmlResourceParser; 36e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.graphics.Color; 37e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.graphics.drawable.ColorDrawable; 38aea00237c77545d670e0425f1839608905d253b3Lin Guoimport android.graphics.PixelFormat; 39a65cf94800673efc7717b6ca4467051c1e332f76Lin Guoimport android.graphics.drawable.Drawable; 40d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.media.AudioAttributes; 41e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Debug; 42e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Handler; 43d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.os.IBinder; 44e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Looper; 45e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Message; 46d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.AttributeSet; 47e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.util.Log; 48d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.SparseArray; 49d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.Xml; 50e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.ContextThemeWrapper; 51e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.Gravity; 52e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.MotionEvent; 53e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.View; 54e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.ViewGroup; 55e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.Window; 56e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.WindowManager; 57e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.widget.SeekBar; 58e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.widget.SeekBar.OnSeekBarChangeListener; 59e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 60aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.ListItem; 61aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.ListItemAdapter; 62aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.ListItemAdapter.BackgroundStyle; 63aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.ListItemProvider.ListProvider; 64aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.PagedListView; 65aea00237c77545d670e0425f1839608905d253b3Lin Guoimport androidx.car.widget.SeekbarListItem; 66aea00237c77545d670e0425f1839608905d253b3Lin Guo 67d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport java.util.Iterator; 68d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport org.xmlpull.v1.XmlPullParserException; 69d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 70d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport java.io.IOException; 71e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport java.io.PrintWriter; 72e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport java.util.ArrayList; 73e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport java.util.List; 74e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 75e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport com.android.systemui.R; 76e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport com.android.systemui.plugins.VolumeDialog; 77e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 78e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue/** 79e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * Car version of the volume dialog. 80e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * 81e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue * Methods ending in "H" must be called on the (ui) handler. 82e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue */ 83e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xuepublic class CarVolumeDialogImpl implements VolumeDialog { 84d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final String TAG = Util.logTag(CarVolumeDialogImpl.class); 85d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 86d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; 87d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final String XML_TAG_VOLUME_ITEM = "item"; 88d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int HOVERING_TIMEOUT = 16000; 89d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int NORMAL_TIMEOUT = 3000; 90d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250; 91d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int DISMISS_DELAY_IN_MILLIS = 50; 92d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100; 93d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 94d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final Context mContext; 95d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final H mHandler = new H(); 96d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 97d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private Window mWindow; 98d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private CustomDialog mDialog; 99d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private PagedListView mListView; 100d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private ListItemAdapter mPagedListAdapter; 101d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // All the volume items. 102d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>(); 103d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Available volume items in car audio manager. 104d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>(); 105d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Volume items in the PagedListView. 106d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final List<ListItem> mVolumeLineItems = new ArrayList<>(); 107d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final KeyguardManager mKeyguard; 108d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 109d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private Car mCar; 110d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private CarAudioManager mCarAudioManager; 111d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 112d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private boolean mHovering; 113d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private boolean mShowing; 114d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private boolean mExpanded; 115d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 116d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public CarVolumeDialogImpl(Context context) { 117d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); 118d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 119d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCar = Car.createCar(mContext, mServiceConnection); 120d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 121d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 122d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void init(int windowType, Callback callback) { 123d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo initDialog(); 124d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 125d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCar.connect(); 126d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 127d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 128d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 129d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void destroy() { 130d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeCallbacksAndMessages(null); 131d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 132d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo cleanupAudioManager(); 133d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup 134d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // audio manager beforehand. 135d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCar.disconnect(); 136d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 137d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 138d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private void initDialog() { 139d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo loadAudioUsageItems(); 140d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeLineItems.clear(); 141d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog = new CustomDialog(mContext); 142d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 143d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHovering = false; 144d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mShowing = false; 145d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mExpanded = false; 146d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow = mDialog.getWindow(); 147d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.requestFeature(Window.FEATURE_NO_TITLE); 148d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 149d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND 150d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); 151d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 152d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 153d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 154d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED 155d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 156d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); 157d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY); 158d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast); 159d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo final WindowManager.LayoutParams lp = mWindow.getAttributes(); 160d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo lp.format = PixelFormat.TRANSLUCENT; 161d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo lp.setTitle(VolumeDialogImpl.class.getSimpleName()); 162d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; 163d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo lp.windowAnimations = -1; 164d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.setAttributes(lp); 165d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 166d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 167d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog.setCanceledOnTouchOutside(true); 168d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog.setContentView(R.layout.car_volume_dialog); 169d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog.setOnShowListener(dialog -> { 170d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setTranslationY(-mListView.getHeight()); 171d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setAlpha(0); 172d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.animate() 173d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .alpha(1) 174d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .translationY(0) 175d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) 176d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator()) 177d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .start(); 178d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }); 179d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView = (PagedListView) mWindow.findViewById(R.id.volume_list); 180d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setOnHoverListener((v, event) -> { 181d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int action = event.getActionMasked(); 182d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) 183d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo || (action == MotionEvent.ACTION_HOVER_MOVE); 184d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rescheduleTimeoutH(); 185d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return true; 186d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }); 187d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 188d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems), 189d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo BackgroundStyle.PANEL); 190d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setAdapter(mPagedListAdapter); 191d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setMaxPages(PagedListView.UNLIMITED_PAGES); 192d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 193d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 194d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void show(int reason) { 195d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget(); 196d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 197d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 198d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void dismiss(int reason) { 199d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget(); 200d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 201d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 202d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private void showH(int reason) { 203d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (D.BUG) { 204d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]); 205e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 206e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 207d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeMessages(H.SHOW); 208d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeMessages(H.DISMISS); 209d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rescheduleTimeoutH(); 210d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Refresh the data set before showing. 211d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter.notifyDataSetChanged(); 212d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mShowing) { 213d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 214e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 215d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mShowing = true; 216e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 217d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog.show(); 218d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked()); 219d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 220e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 221d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void rescheduleTimeoutH() { 222d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeMessages(H.DISMISS); 223d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo final int timeout = computeTimeoutH(); 224d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.sendMessageDelayed(mHandler 225d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout); 226e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 227d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (D.BUG) { 228d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); 229e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 230d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 231e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 232d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private int computeTimeoutH() { 233d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT; 234d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 235e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 236d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void dismissH(int reason) { 237d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (D.BUG) { 238d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]); 239e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 240e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 241d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeMessages(H.DISMISS); 242d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mHandler.removeMessages(H.SHOW); 243d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!mShowing) { 244d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 245e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 246e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 247d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.animate().cancel(); 248d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mShowing = false; 249d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 250d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setTranslationY(0); 251d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.setAlpha(1); 252d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mListView.animate() 253d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .alpha(0) 254d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .translationY(-mListView.getHeight()) 255d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) 256d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) 257d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .withEndAction(() -> mHandler.postDelayed(() -> { 258d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (D.BUG) { 259d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.d(TAG, "mDialog.dismiss()"); 260d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 261d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mDialog.dismiss(); 262d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }, DISMISS_DELAY_IN_MILLIS)) 263d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo .start(); 264d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 265d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason); 266d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 267d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 268d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void dump(PrintWriter writer) { 269d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo writer.println(VolumeDialogImpl.class.getSimpleName() + " state:"); 270d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo writer.print(" mShowing: "); writer.println(mShowing); 271d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 272d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 273d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private void loadAudioUsageItems() { 274d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) { 275d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo AttributeSet attrs = Xml.asAttributeSet(parser); 276d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int type; 277d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Traverse to the first start tag 278d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT 279d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo && type != XmlResourceParser.START_TAG) { 280d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 281d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 282d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) { 283d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo throw new RuntimeException("Meta-data does not start with carVolumeItems tag"); 284d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 285d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int outerDepth = parser.getDepth(); 286d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int rank = 0; 287d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT 288d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) { 289d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (type == XmlResourceParser.END_TAG) { 290d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo continue; 291d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 292d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { 293d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo TypedArray item = mContext.getResources().obtainAttributes( 294d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo attrs, R.styleable.carVolumeItems_item); 295d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); 296d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (usage >= 0) { 297d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = new VolumeItem(); 298d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.usage = usage; 299d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.rank = rank; 300d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0); 301d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeItems.put(usage, volumeItem); 302d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rank++; 303d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 304d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo item.recycle(); 305d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 306d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 307d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (XmlPullParserException | IOException e) { 308d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Error parsing volume groups configuration", e); 309aea00237c77545d670e0425f1839608905d253b3Lin Guo } 310d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 311d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 312d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private VolumeItem getVolumeItemForUsages(int[] usages) { 313d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int rank = Integer.MAX_VALUE; 314d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem result = null; 315d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int usage : usages) { 316d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mVolumeItems.get(usage); 317d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (volumeItem.rank < rank) { 318d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rank = volumeItem.rank; 319d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo result = volumeItem; 320d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 321e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 322d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return result; 323d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 324d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 325d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { 326d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 327d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return carAudioManager.getGroupVolume(volumeGroupId); 328d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 329d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 330e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 331d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return 0; 332d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 333d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 334d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) { 335d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 336d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return carAudioManager.getGroupMaxVolume(volumeGroupId); 337d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 338d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 339e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 340d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return 0; 341d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 342d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 343d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId, 344d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) { 345d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo SeekbarListItem listItem = new SeekbarListItem(mContext); 346d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId)); 347a412b4565d8a0ee23bee9929c1ad681a051713ddLin Guo int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint); 348d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); 349d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setProgress(progress); 350d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setOnSeekBarChangeListener( 351d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); 352a412b4565d8a0ee23bee9929c1ad681a051713ddLin Guo Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon); 353a412b4565d8a0ee23bee9929c1ad681a051713ddLin Guo primaryIcon.setTint(color); 354a412b4565d8a0ee23bee9929c1ad681a051713ddLin Guo listItem.setPrimaryActionIcon(primaryIcon); 355d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (supplementalIconId != 0) { 356d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); 357a412b4565d8a0ee23bee9929c1ad681a051713ddLin Guo supplementalIcon.setTint(color); 358d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setSupplementalIcon(supplementalIcon, true, 359d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo supplementalIconOnClickListener); 360d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 361d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setSupplementalEmptyIcon(true); 362e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 363e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 364d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeLineItems.add(listItem); 365d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem = listItem; 366d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.progress = progress; 367d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return listItem; 368d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 369d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 370d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private VolumeItem findVolumeItem(SeekbarListItem targetItem) { 371d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int i = 0; i < mVolumeItems.size(); ++i) { 372d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mVolumeItems.valueAt(i); 373d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (volumeItem.listItem == targetItem) { 374d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return volumeItem; 375d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 376e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 377d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return null; 378d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 379d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 380d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private void cleanupAudioManager() { 381d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 382d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder()); 383d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 384d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 385e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 386d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeLineItems.clear(); 387d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = null; 388d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 389e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 390d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class H extends Handler { 391d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int SHOW = 1; 392d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int DISMISS = 2; 393e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 394d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public H() { 395d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super(Looper.getMainLooper()); 396e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 397e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 398d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 399d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void handleMessage(Message msg) { 400d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo switch (msg.what) { 401d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo case SHOW: 402d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo showH(msg.arg1); 403d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo break; 404d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo case DISMISS: 405d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo dismissH(msg.arg1); 406d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo break; 407d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo default: 408d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 409e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 410d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 411e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 412d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class CustomDialog extends Dialog implements DialogInterface { 413d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public CustomDialog(Context context) { 414d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super(context, com.android.systemui.R.style.qs_theme); 415e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 416e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 417d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 418d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public boolean dispatchTouchEvent(MotionEvent ev) { 419d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rescheduleTimeoutH(); 420d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return super.dispatchTouchEvent(ev); 421e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 422e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 423d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 424d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void onStart() { 425d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.setCanceledOnTouchOutside(true); 426d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.onStart(); 427e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 428e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 429d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 430d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void onStop() { 431d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.onStop(); 432e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 433e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 434d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 435d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public boolean onTouchEvent(MotionEvent event) { 436d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (isShowing()) { 437d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { 438d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE); 439d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return true; 440d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 441d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 442d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return false; 443e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 444d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 445e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 446d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class ExpandIconListener implements View.OnClickListener { 447d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 448d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onClick(final View v) { 449d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mExpanded = !mExpanded; 450d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Animator inAnimator; 451d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mExpanded) { 452d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { 453d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Adding the items which are not coming from the default item. 454d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); 455d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (volumeItem.defaultItem) { 456d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Set progress here due to the progress of seekbar may not be updated. 457d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem.setProgress(volumeItem.progress); 458d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 459d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo addSeekbarListItem(volumeItem, groupId, 0, null); 460d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 461d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 462d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator = AnimatorInflater.loadAnimator( 463d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_in_rotate_up); 464d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 465d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Only keeping the default stream if it is not expended. 466d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Iterator itr = mVolumeLineItems.iterator(); 467d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo while (itr.hasNext()) { 468d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next(); 469d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = findVolumeItem(seekbarListItem); 470d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!volumeItem.defaultItem) { 471d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo itr.remove(); 472d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 473d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Set progress here due to the progress of seekbar may not be updated. 474d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo seekbarListItem.setProgress(volumeItem.progress); 475d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 476d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 477d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator = AnimatorInflater.loadAnimator( 478d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_in_rotate_down); 479d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 480d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 481d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Animator outAnimator = AnimatorInflater.loadAnimator( 482d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_out); 483d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS); 484d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo AnimatorSet animators = new AnimatorSet(); 485d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.playTogether(outAnimator, inAnimator); 486d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.setTarget(v); 487d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.start(); 488d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter.notifyDataSetChanged(); 489e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 490d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 491e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 492d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener { 493d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final int mVolumeGroupId; 494d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final CarAudioManager mCarAudioManager; 495e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 496d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) { 497d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeGroupId = volumeGroupId; 498d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = carAudioManager; 499e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 500e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 501d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 502d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 503d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!fromUser) { 504d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // For instance, if this event is originated from AudioService, 505d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // we can ignore it as it has already been handled and doesn't need to be 506d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // sent back down again. 507d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 508d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 509d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 510d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mCarAudioManager == null) { 511d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.w(TAG, "Ignoring volume change event because the car isn't connected"); 512d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 513d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 514d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; 515d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); 516d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 517d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 518d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 519e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 520e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 521d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 522d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onStartTrackingTouch(SeekBar seekBar) {} 523e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 524d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 525d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onStopTrackingTouch(SeekBar seekBar) {} 526d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 527e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 528d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() { 529d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 530c8cdc593227638fdefbb9dbf2644fb8e4bf31fe6Hongwei Wang public void onGroupVolumeChanged(int groupId, int flags) { 531d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); 532d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int value = getSeekbarValue(mCarAudioManager, groupId); 533d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Do not update the progress if it is the same as before. When car audio manager sets its 534d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // group volume caused by the seekbar progress changed, it also triggers this callback. 535d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Updating the seekbar at the same time could block the continuous seeking. 536d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (value != volumeItem.progress) { 537d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem.setProgress(value); 538d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.progress = value; 539d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo show(Events.SHOW_REASON_VOLUME_CHANGED); 540d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 541e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 542e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 543d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 544c8cdc593227638fdefbb9dbf2644fb8e4bf31fe6Hongwei Wang public void onMasterMuteChanged(int flags) { 545d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // ignored 546e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 547d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }; 548e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 549d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final ServiceConnection mServiceConnection = new ServiceConnection() { 550d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 551d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onServiceConnected(ComponentName name, IBinder service) { 552d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 553d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mExpanded = false; 554d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); 555d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); 556d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Populates volume slider items from volume groups to UI. 557d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int groupId = 0; groupId < volumeGroupCount; groupId++) { 558d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = getVolumeItemForUsages( 559d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.getUsagesForVolumeGroupId(groupId)); 560d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mAvailableVolumeItems.add(volumeItem); 561d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // The first one is the default item. 562d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (groupId == 0) { 563d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.defaultItem = true; 564d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down, 565d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo new ExpandIconListener()); 566d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 567d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 568d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 569d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // If list is already initiated, update its content. 570d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mPagedListAdapter != null) { 571d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter.notifyDataSetChanged(); 572d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 573d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder()); 574d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 575d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 576d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 577a65cf94800673efc7717b6ca4467051c1e332f76Lin Guo } 578a65cf94800673efc7717b6ca4467051c1e332f76Lin Guo 579d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo /** 580d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * This does not get called when service is properly disconnected. 581d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * So we need to also handle cleanups in destroy(). 582d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo */ 583d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 584d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onServiceDisconnected(ComponentName name) { 585d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo cleanupAudioManager(); 586e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 587d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }; 588d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 589d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo /** 590d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * Wrapper class which contains information of each volume group. 591d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo */ 592d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static class VolumeItem { 593d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private @AudioAttributes.AttributeUsage int usage; 594d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private int rank; 595d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private boolean defaultItem = false; 596d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private @DrawableRes int icon; 597d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private SeekbarListItem listItem; 598d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private int progress; 599d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 600d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo} 601