CarVolumeDialogImpl.java revision d7468d4d546ee452f344da8e6d18ff7c9823fbbb
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; 22aea00237c77545d670e0425f1839608905d253b3Lin Guoimport android.annotation.Nullable; 23e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.app.Dialog; 24e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.app.KeyguardManager; 25d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.Car; 26d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.CarNotConnectedException; 27d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.media.CarAudioManager; 28d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.car.media.ICarVolumeCallback; 29d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.ComponentName; 30e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.content.Context; 31e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.content.DialogInterface; 32d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.ServiceConnection; 33d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.res.TypedArray; 34d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.content.res.XmlResourceParser; 35e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.graphics.Color; 36e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.graphics.drawable.ColorDrawable; 37aea00237c77545d670e0425f1839608905d253b3Lin Guoimport android.graphics.PixelFormat; 38a65cf94800673efc7717b6ca4467051c1e332f76Lin Guoimport android.graphics.drawable.Drawable; 39d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.media.AudioAttributes; 40e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Debug; 41e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Handler; 42d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.os.IBinder; 43e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Looper; 44e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.os.Message; 45d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.AttributeSet; 46e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.util.Log; 47d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.SparseArray; 48d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport android.util.Xml; 49e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.ContextThemeWrapper; 50e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.Gravity; 51e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.MotionEvent; 52e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.View; 53e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.ViewGroup; 54e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.Window; 55e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.view.WindowManager; 56e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.widget.SeekBar; 57e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xueimport android.widget.SeekBar.OnSeekBarChangeListener; 58e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 59d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guoimport androidx.annotation.DrawableRes; 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)); 347d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int progress = getSeekbarValue(mCarAudioManager, volumeGroupId); 348d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setProgress(progress); 349d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setOnSeekBarChangeListener( 350d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager)); 351d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setPrimaryActionIcon(mContext.getResources().getDrawable(volumeItem.icon)); 352d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (supplementalIconId != 0) { 353d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId); 354d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setSupplementalIcon(supplementalIcon, true, 355d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo supplementalIconOnClickListener); 356d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 357d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo listItem.setSupplementalEmptyIcon(true); 358e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 359e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 360d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeLineItems.add(listItem); 361d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem = listItem; 362d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.progress = progress; 363d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return listItem; 364d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 365d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 366d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private VolumeItem findVolumeItem(SeekbarListItem targetItem) { 367d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int i = 0; i < mVolumeItems.size(); ++i) { 368d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mVolumeItems.valueAt(i); 369d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (volumeItem.listItem == targetItem) { 370d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return volumeItem; 371d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 372e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 373d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return null; 374d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 375d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 376d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private void cleanupAudioManager() { 377d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 378d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder()); 379d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 380d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 381e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 382d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeLineItems.clear(); 383d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = null; 384d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 385e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 386d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class H extends Handler { 387d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int SHOW = 1; 388d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static final int DISMISS = 2; 389e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 390d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public H() { 391d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super(Looper.getMainLooper()); 392e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 393e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 394d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 395d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void handleMessage(Message msg) { 396d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo switch (msg.what) { 397d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo case SHOW: 398d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo showH(msg.arg1); 399d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo break; 400d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo case DISMISS: 401d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo dismissH(msg.arg1); 402d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo break; 403d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo default: 404d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 405e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 406d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 407e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 408d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class CustomDialog extends Dialog implements DialogInterface { 409d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public CustomDialog(Context context) { 410d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super(context, com.android.systemui.R.style.qs_theme); 411e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 412e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 413d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 414d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public boolean dispatchTouchEvent(MotionEvent ev) { 415d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo rescheduleTimeoutH(); 416d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return super.dispatchTouchEvent(ev); 417e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 418e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 419d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 420d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void onStart() { 421d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.setCanceledOnTouchOutside(true); 422d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.onStart(); 423e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 424e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 425d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 426d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo protected void onStop() { 427d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo super.onStop(); 428e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 429e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 430d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 431d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public boolean onTouchEvent(MotionEvent event) { 432d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (isShowing()) { 433d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { 434d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE); 435d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return true; 436d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 437d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 438d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return false; 439e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 440d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 441e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 442d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class ExpandIconListener implements View.OnClickListener { 443d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 444d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onClick(final View v) { 445d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mExpanded = !mExpanded; 446d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Animator inAnimator; 447d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mExpanded) { 448d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) { 449d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Adding the items which are not coming from the default item. 450d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); 451d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (volumeItem.defaultItem) { 452d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Set progress here due to the progress of seekbar may not be updated. 453d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem.setProgress(volumeItem.progress); 454d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 455d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo addSeekbarListItem(volumeItem, groupId, 0, null); 456d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 457d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 458d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator = AnimatorInflater.loadAnimator( 459d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_in_rotate_up); 460d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 461d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Only keeping the default stream if it is not expended. 462d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Iterator itr = mVolumeLineItems.iterator(); 463d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo while (itr.hasNext()) { 464d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next(); 465d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = findVolumeItem(seekbarListItem); 466d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!volumeItem.defaultItem) { 467d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo itr.remove(); 468d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } else { 469d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Set progress here due to the progress of seekbar may not be updated. 470d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo seekbarListItem.setProgress(volumeItem.progress); 471d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 472d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 473d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator = AnimatorInflater.loadAnimator( 474d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_in_rotate_down); 475d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 476d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 477d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Animator outAnimator = AnimatorInflater.loadAnimator( 478d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mContext, R.anim.car_arrow_fade_out); 479d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS); 480d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo AnimatorSet animators = new AnimatorSet(); 481d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.playTogether(outAnimator, inAnimator); 482d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.setTarget(v); 483d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo animators.start(); 484d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter.notifyDataSetChanged(); 485e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 486d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 487e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 488d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener { 489d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final int mVolumeGroupId; 490d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final CarAudioManager mCarAudioManager; 491e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 492d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) { 493d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mVolumeGroupId = volumeGroupId; 494d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = carAudioManager; 495e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 496e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 497d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 498d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 499d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (!fromUser) { 500d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // For instance, if this event is originated from AudioService, 501d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // we can ignore it as it has already been handled and doesn't need to be 502d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // sent back down again. 503d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 504d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 505d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 506d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mCarAudioManager == null) { 507d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.w(TAG, "Ignoring volume change event because the car isn't connected"); 508d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo return; 509d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 510d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; 511d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); 512d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 513d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 514d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 515e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 516e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 517d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 518d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onStartTrackingTouch(SeekBar seekBar) {} 519e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 520d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 521d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onStopTrackingTouch(SeekBar seekBar) {} 522d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 523e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 524d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() { 525d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 526d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onGroupVolumeChanged(int groupId) { 527d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); 528d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int value = getSeekbarValue(mCarAudioManager, groupId); 529d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Do not update the progress if it is the same as before. When car audio manager sets its 530d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // group volume caused by the seekbar progress changed, it also triggers this callback. 531d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Updating the seekbar at the same time could block the continuous seeking. 532d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (value != volumeItem.progress) { 533d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.listItem.setProgress(value); 534d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.progress = value; 535d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo show(Events.SHOW_REASON_VOLUME_CHANGED); 536d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 537e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 538e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 539d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 540d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onMasterMuteChanged() { 541d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // ignored 542e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 543d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }; 544e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue 545d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private final ServiceConnection mServiceConnection = new ServiceConnection() { 546d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 547d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onServiceConnected(ComponentName name, IBinder service) { 548d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo try { 549d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mExpanded = false; 550d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); 551d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo int volumeGroupCount = mCarAudioManager.getVolumeGroupCount(); 552d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // Populates volume slider items from volume groups to UI. 553d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo for (int groupId = 0; groupId < volumeGroupCount; groupId++) { 554d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo VolumeItem volumeItem = getVolumeItemForUsages( 555d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.getUsagesForVolumeGroupId(groupId)); 556d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mAvailableVolumeItems.add(volumeItem); 557d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // The first one is the default item. 558d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (groupId == 0) { 559d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo volumeItem.defaultItem = true; 560d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down, 561d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo new ExpandIconListener()); 562d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 563d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 564d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 565d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo // If list is already initiated, update its content. 566d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo if (mPagedListAdapter != null) { 567d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mPagedListAdapter.notifyDataSetChanged(); 568d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 569d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder()); 570d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } catch (CarNotConnectedException e) { 571d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo Log.e(TAG, "Car is not connected!", e); 572d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 573a65cf94800673efc7717b6ca4467051c1e332f76Lin Guo } 574a65cf94800673efc7717b6ca4467051c1e332f76Lin Guo 575d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo /** 576d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * This does not get called when service is properly disconnected. 577d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * So we need to also handle cleanups in destroy(). 578d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo */ 579d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo @Override 580d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo public void onServiceDisconnected(ComponentName name) { 581d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo cleanupAudioManager(); 582e53f749995373f6271ae4a9ac7b3c6007aabd8e9Lujiang Xue } 583d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo }; 584d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo 585d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo /** 586d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo * Wrapper class which contains information of each volume group. 587d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo */ 588d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private static class VolumeItem { 589d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private @AudioAttributes.AttributeUsage int usage; 590d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private int rank; 591d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private boolean defaultItem = false; 592d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private @DrawableRes int icon; 593d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private SeekbarListItem listItem; 594d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo private int progress; 595d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo } 596d7468d4d546ee452f344da8e6d18ff7c9823fbbbLin Guo} 597