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