package eu.hgross.blaubotcam.views; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import eu.hgross.blaubot.android.views.ViewUtils; import eu.hgross.blaubotcam.R; import eu.hgross.blaubotcam.audio.BlaubotWalkieTalkie; import eu.hgross.blaubotcam.audio.IPlaybackListener; import eu.hgross.blaubotcam.audio.WalkieTalkieMessage; /** * Displays status of the walkietalkie playback on received messagees. * Attach it to a BlaubotWalkieTalkie. * * @author Henning Gross {@literal (mail.to@henning-gross.de)} */ public class AudioViewer extends FrameLayout implements IPlaybackListener { private static final String LOG_TAG = "AudioViewer"; private Handler mUiHandler; /** * Maps the unique device id to the number of current playbacks for this device */ private ConcurrentHashMap<String, AtomicInteger> mPlaybacks; /** * Maps the unique device id to the view, displaying the last image */ private ConcurrentHashMap<String, View> mViews; /** * The walkie talkie instance to be visualized */ private BlaubotWalkieTalkie mWalkieTalkie; /** * The main view containing the audio indicator icon */ private View mMainView; /** * Indicates if there is any playback at the moment */ private ImageView mAudioIndicatorIcon; /** * Indicates the number of current playbakcs */ private TextView mNumberOfPlaybacksTextView; /** * Is GONE by default and can be made visible by clicking on the indicator. * Shows a list of all current playbacks. */ private LinearLayout mCurrentPlaybacksContainer; private Drawable mTalkingPeasantsDrawable; private Drawable mNoOneTalkingDrawable; public AudioViewer(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public AudioViewer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } private void initView() { mViews = new ConcurrentHashMap<>(); mPlaybacks = new ConcurrentHashMap<>(); mUiHandler = new Handler(Looper.getMainLooper()); mMainView = inflate(getContext(), R.layout.audio_viewer, null); mAudioIndicatorIcon = (ImageView) mMainView.findViewById(R.id.audioIndicatorIcon); mNumberOfPlaybacksTextView = (TextView) mMainView.findViewById(R.id.numberOfPlaybacksTextView); mCurrentPlaybacksContainer = (LinearLayout) mMainView.findViewById(R.id.currentPlaybacksContainer); mTalkingPeasantsDrawable = getResources().getDrawable(R.drawable.ic_peasant_talking); mNoOneTalkingDrawable = getResources().getDrawable(R.drawable.ic_stopped); mAudioIndicatorIcon.setImageDrawable(mNoOneTalkingDrawable); // add onclick to indicator to toggle the container mMainView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { final boolean isVisible = mCurrentPlaybacksContainer.getVisibility() == VISIBLE; mCurrentPlaybacksContainer.setVisibility(isVisible ? GONE : VISIBLE); } }); mCurrentPlaybacksContainer.setVisibility(VISIBLE); addView(mMainView); } public void setWalkieTalkie(BlaubotWalkieTalkie walkieTalkie) { this.mWalkieTalkie = walkieTalkie; this.mWalkieTalkie.addPlaybackListener(this); updateUI(null); } /** * updates the state images for all clients and the image only for the given uniqueDeviceId * * @param message the image message that needs to be rendered */ private void updateUI(final WalkieTalkieMessage message) { // find out if we have any current playback int sum = 0; for (AtomicInteger i : mPlaybacks.values()) { sum += i.get(); } final int numberOfCurrentPlaybacks = sum; final int numberOfPlaybacksByCurrentMessageSender = message == null ? 0 : mPlaybacks.get(message.getSenderUniqueDeviceId()).get(); mUiHandler.post(new Runnable() { @Override public void run() { // maintain main View mNumberOfPlaybacksTextView.setText(numberOfCurrentPlaybacks == 0 ? "Not playing any audio" : "Playing " + numberOfCurrentPlaybacks + " audio messages."); mAudioIndicatorIcon.setImageDrawable(numberOfCurrentPlaybacks > 0 ? mTalkingPeasantsDrawable : mNoOneTalkingDrawable); // maintain the view for the current message's sender (if any message) if (message != null) { final String senderUniqueDeviceId = message.getSenderUniqueDeviceId(); // add the sender's data final String numBytes = ViewUtils.humanReadableByteCount(message.getAudioBytes().length, true); View playbackListItem = createPlaybackListItemNameElement(getContext(), mTalkingPeasantsDrawable, senderUniqueDeviceId, numBytes); final boolean added = mViews.putIfAbsent(senderUniqueDeviceId, playbackListItem) == null; if (!added) { playbackListItem = mViews.get(senderUniqueDeviceId); // just update TextView titleTextView = (TextView) playbackListItem.findViewById(R.id.text); titleTextView.setText(numBytes); } else { // only add, if view was newly created (not reused) mCurrentPlaybacksContainer.addView(playbackListItem); } if (numberOfPlaybacksByCurrentMessageSender <= 0) { // remove view, if no playbacks anymore mCurrentPlaybacksContainer.removeView(playbackListItem); mViews.remove(senderUniqueDeviceId); } } System.out.println("numViews:" + mCurrentPlaybacksContainer.getChildCount()); } }); } /** * Creates a list item * @param context the context * @param uniqueDeviceId the unique device id * @param title the title text to be displayed instead of the state name * @return the view */ private static View createPlaybackListItemNameElement(Context context, Drawable icon, String uniqueDeviceId, String title) { View item = inflate(context, R.layout.audio_viewer_playback_list_item, null); TextView uniqueDeviceIdTextView = (TextView) item.findViewById(R.id.uniqueDeviceIdLabel); TextView titleTextView = (TextView) item.findViewById(R.id.text); ImageView iconImageView = (ImageView) item.findViewById(R.id.icon); iconImageView.setImageDrawable(icon); uniqueDeviceIdTextView.setText(uniqueDeviceId); titleTextView.setText(title); return item; } @Override public void beforePlayback(WalkieTalkieMessage walkieTalkieMessage) { mPlaybacks.putIfAbsent(walkieTalkieMessage.getSenderUniqueDeviceId(), new AtomicInteger(0)); final AtomicInteger counter = mPlaybacks.get(walkieTalkieMessage.getSenderUniqueDeviceId()); counter.incrementAndGet(); updateUI(walkieTalkieMessage); } @Override public void afterPlayback(WalkieTalkieMessage walkieTalkieMessage) { final AtomicInteger counter = mPlaybacks.get(walkieTalkieMessage.getSenderUniqueDeviceId()); counter.decrementAndGet(); updateUI(walkieTalkieMessage); } }