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