blob: de3a9901b8c41e832ae93d928b05dfac4d2c8502 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.common.ui.view;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import com.android.systemui.R;
/**
* The layout contains a seekbar whose progress could be modified
* through the icons on two ends of the seekbar.
*/
public class SeekBarWithIconButtonsView extends LinearLayout {
private static final int DEFAULT_SEEKBAR_MAX = 6;
private static final int DEFAULT_SEEKBAR_PROGRESS = 0;
private ViewGroup mIconStartFrame;
private ViewGroup mIconEndFrame;
private ImageView mIconStart;
private ImageView mIconEnd;
private SeekBar mSeekbar;
private SeekBarChangeListener mSeekBarListener = new SeekBarChangeListener();
private String[] mStateLabels = null;
public SeekBarWithIconButtonsView(Context context) {
this(context, null);
}
public SeekBarWithIconButtonsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SeekBarWithIconButtonsView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SeekBarWithIconButtonsView(Context context,
AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
LayoutInflater.from(context).inflate(
R.layout.seekbar_with_icon_buttons, this, /* attachToRoot= */ true);
mIconStartFrame = findViewById(R.id.icon_start_frame);
mIconEndFrame = findViewById(R.id.icon_end_frame);
mIconStart = findViewById(R.id.icon_start);
mIconEnd = findViewById(R.id.icon_end);
mSeekbar = findViewById(R.id.seekbar);
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(
attrs,
R.styleable.SeekBarWithIconButtonsView_Layout,
defStyleAttr, defStyleRes
);
int max = typedArray.getInt(
R.styleable.SeekBarWithIconButtonsView_Layout_max, DEFAULT_SEEKBAR_MAX);
int progress = typedArray.getInt(
R.styleable.SeekBarWithIconButtonsView_Layout_progress,
DEFAULT_SEEKBAR_PROGRESS);
mSeekbar.setMax(max);
setProgress(progress);
int iconStartFrameContentDescriptionId = typedArray.getResourceId(
R.styleable.SeekBarWithIconButtonsView_Layout_iconStartContentDescription,
/* defValue= */ 0);
int iconEndFrameContentDescriptionId = typedArray.getResourceId(
R.styleable.SeekBarWithIconButtonsView_Layout_iconEndContentDescription,
/* defValue= */ 0);
if (iconStartFrameContentDescriptionId != 0) {
final String contentDescription =
context.getString(iconStartFrameContentDescriptionId);
mIconStartFrame.setContentDescription(contentDescription);
}
if (iconEndFrameContentDescriptionId != 0) {
final String contentDescription =
context.getString(iconEndFrameContentDescriptionId);
mIconEndFrame.setContentDescription(contentDescription);
}
} else {
mSeekbar.setMax(DEFAULT_SEEKBAR_MAX);
setProgress(DEFAULT_SEEKBAR_PROGRESS);
}
mSeekbar.setOnSeekBarChangeListener(mSeekBarListener);
mIconStartFrame.setOnClickListener((view) -> {
final int progress = mSeekbar.getProgress();
if (progress > 0) {
mSeekbar.setProgress(progress - 1);
setIconViewAndFrameEnabled(mIconStart, mSeekbar.getProgress() > 0);
}
});
mIconEndFrame.setOnClickListener((view) -> {
final int progress = mSeekbar.getProgress();
if (progress < mSeekbar.getMax()) {
mSeekbar.setProgress(progress + 1);
setIconViewAndFrameEnabled(mIconEnd, mSeekbar.getProgress() < mSeekbar.getMax());
}
});
}
private static void setIconViewAndFrameEnabled(View iconView, boolean enabled) {
iconView.setEnabled(enabled);
final ViewGroup iconFrame = (ViewGroup) iconView.getParent();
iconFrame.setEnabled(enabled);
}
/**
* Stores the String array we would like to use for describing the state of seekbar progress
* and updates the state description with current progress.
*
* @param labels The state descriptions to be announced for each progress.
*/
public void setProgressStateLabels(String[] labels) {
mStateLabels = labels;
if (mStateLabels != null) {
setSeekbarStateDescription();
}
}
/**
* Sets the state of seekbar based on current progress. The progress of seekbar is
* corresponding to the index of the string array. If the progress is larger than or equals
* to the length of the array, the state description is set to an empty string.
*/
private void setSeekbarStateDescription() {
mSeekbar.setStateDescription(
(mSeekbar.getProgress() < mStateLabels.length)
? mStateLabels[mSeekbar.getProgress()] : "");
}
/**
* Sets a onSeekbarChangeListener to the seekbar in the layout.
* We update the Start Icon and End Icon if needed when the seekbar progress is changed.
*/
public void setOnSeekBarChangeListener(
@Nullable SeekBar.OnSeekBarChangeListener onSeekBarChangeListener) {
mSeekBarListener.setOnSeekBarChangeListener(onSeekBarChangeListener);
}
/**
* Start and End icons might need to be updated when there is a change in seekbar progress.
* Icon Start will need to be enabled when the seekbar progress is larger than 0.
* Icon End will need to be enabled when the seekbar progress is less than Max.
*/
private void updateIconViewIfNeeded(int progress) {
setIconViewAndFrameEnabled(mIconStart, progress > 0);
setIconViewAndFrameEnabled(mIconEnd, progress < mSeekbar.getMax());
}
/**
* Sets max to the seekbar in the layout.
*/
public void setMax(int max) {
mSeekbar.setMax(max);
}
/**
* Sets progress to the seekbar in the layout.
* If the progress is smaller than or equals to 0, the IconStart will be disabled. If the
* progress is larger than or equals to Max, the IconEnd will be disabled. The seekbar progress
* will be constrained in {@link SeekBar}.
*/
public void setProgress(int progress) {
mSeekbar.setProgress(progress);
updateIconViewIfNeeded(progress);
}
private class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = null;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mStateLabels != null) {
setSeekbarStateDescription();
}
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
}
updateIconViewIfNeeded(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(seekBar);
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(seekBar);
}
}
void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener listener) {
mOnSeekBarChangeListener = listener;
}
}
}