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