package me.barrasso.android.volume.popup;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.os.Build;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.otto.Subscribe;
import me.barrasso.android.volume.R;
import me.barrasso.android.volume.LogUtils;
import me.barrasso.android.volume.media.VolumePanelInfo;
import me.barrasso.android.volume.ui.BackgroundLinearLayout;
import me.barrasso.android.volume.ui.OnTouchClickListener;
import me.barrasso.android.volume.media.StreamResources;
import me.barrasso.android.volume.ui.transition.TransitionCompat;
import me.barrasso.android.volume.utils.SettingsHelper;
import me.barrasso.android.volume.utils.Utils;
import static me.barrasso.android.volume.LogUtils.LOGD;
import static me.barrasso.android.volume.LogUtils.LOGI;
/**
* Windows Phone/ Metro-UI style {@link me.barrasso.android.volume.popup.VolumePanel}
* designed to display the volume_3 as text with an option to toggle the three ringer
* modes and media playback controls while music is playing.
*/
public final class WPVolumePanel extends VolumePanel {
public static final String TAG = WPVolumePanel.class.getSimpleName();
public static final VolumePanelInfo<WPVolumePanel> VOLUME_PANEL_INFO =
new VolumePanelInfo<WPVolumePanel>(WPVolumePanel.class);
public WPVolumePanel(PopupWindowManager pWindowManager) {
super(pWindowManager);
}
private TransitionCompat transition;
ViewGroup trackInfo;
ViewGroup musicPanel;
ViewGroup root;
TextView ringerText;
TextView volText, streamText;
TextView artist, title;
ImageView icon;
ImageButton playPause, mBtnPrev, mBtnNext;
/**
* @param color The color to lighten.
* @param factor Lightening factor, 0 - 1.0
*/
public static int lighten(final int color, final float factor) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] = 1.0f - factor * (1.0f - hsv[2]);
return Color.HSVToColor(hsv);
}
public static int stripAlpha(final int color) {
return Color.rgb(Color.red(color), Color.green(color), Color.blue(color));
}
@Override public void onCreate() {
super.onCreate();
Context context = getContext();
transition = TransitionCompat.get();
// Get all relevant layouts/ views.
LayoutInflater inflater = LayoutInflater.from(context);
root = (ViewGroup) inflater.inflate(R.layout.wp_volume_adjust, null);
musicPanel = (ViewGroup) root.findViewById(R.id.music_panel);
trackInfo = (ViewGroup) musicPanel.findViewById(R.id.track_info);
musicPanel.setVisibility(View.VISIBLE);
playPause = (ImageButton) musicPanel.findViewById(R.id.media_play_pause);
mBtnNext = (ImageButton) musicPanel.findViewById(R.id.media_next);
mBtnPrev = (ImageButton) musicPanel.findViewById(R.id.media_previous);
volText = (TextView) root.findViewById(R.id.volume_text);
streamText = (TextView) root.findViewById(R.id.streamName);
icon = (ImageView) root.findViewById(R.id.stream_icon);
ringerText = (TextView) root.findViewById(R.id.ringer_mode);
artist = ((TextView) trackInfo.findViewById(R.id.track_artist));
title = ((TextView) trackInfo.findViewById(R.id.track_song));
// Launch the music app when the track information is clicked.
trackInfo.setOnTouchListener(new OnTouchClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hide();
launchMusicApp();
}
}));
// Handle toggling the vibrate/ ringer mode when clicked.
icon.setOnTouchListener(new OnTouchClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
toggleRinger();
onUserInteraction();
}
}));
// Set Segoe UI as the default font for all text.
Typeface segoe = Typeface.createFromAsset(context.getAssets(), "fonts/Segoe-Regular.ttf");
Typeface segoeBold = Typeface.createFromAsset(context.getAssets(), "fonts/Segoe-Bold.ttf");
volText.setTypeface(segoe);
ringerText.setTypeface(segoe);
streamText.setTypeface(segoe);
artist.setTypeface(segoe);
title.setTypeface(segoeBold);
volText.setPaintFlags(volText.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
artist.setPaintFlags(artist.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
title.setPaintFlags(title.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
streamText.setPaintFlags(streamText.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
ringerText.setPaintFlags(ringerText.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
attachPlaybackListeners(musicPanel, new MediaButtonClickListener());
onRingerModeChange(mRingerMode);
mLayout = root;
}
/*package*/ void toggleRinger() {
LogUtils.LOGI(TAG, "toggleRinger()");
int newMode = Utils.nextRingerMode(AudioManager.ADJUST_RAISE, mRingerMode, mAudioHelper.hasVibrator());
if (newMode != mRingerMode) {
mAudioManager.setRingerMode(newMode);
}
}
@Override public void onRingerModeChange(int ringerMode) {
LOGD(TAG, "onRingerModeChange(" + ringerMode + ")");
switch (ringerMode) {
case AudioManager.RINGER_MODE_VIBRATE:
icon.setImageResource(R.drawable.uvc_ringer_vibrate_dark);
ringerText.setText(R.string.vibrate);
break;
case AudioManager.RINGER_MODE_SILENT:
icon.setImageResource(R.drawable.uvc_ringer_off_dark);
ringerText.setText(R.string.silent);
break;
case AudioManager.RINGER_MODE_NORMAL:
icon.setImageResource(R.drawable.uvc_ringer_on_dark);
if (vibrateWhenRinging())
ringerText.setText(R.string.vibrate_ring);
else
ringerText.setText(R.string.ring);
break;
}
}
@Override
public int getColor() {
return stripAlpha(color);
}
@Override
public int getBackgroundColor() {
return stripAlpha(backgroundColor);
}
@Override
public void setTertiaryColor(final int newColor) {
super.setTertiaryColor(newColor);
title.setTextColor(newColor);
artist.setTextColor(newColor);
streamText.setTextColor(newColor);
ringerText.setTextColor(newColor);
}
@SuppressWarnings("unused")
@Subscribe
public void onPlaybackEvent(Pair<MediaMetadataCompat, PlaybackStateCompat> mediaInfo) {
// NOTE: This MUST be added to the final descendant of VolumePanel!
LOGI(TAG, "onPlaybackEvent()");
this.onPlayStateChanged(mediaInfo);
}
@Override public void screen(boolean on) {
super.screen(on);
setEnableMarquee(on);
}
@Override public void onVisibilityChanged(int visibility) {
super.onVisibilityChanged(visibility);
switch (visibility) {
case View.GONE:
setEnableMarquee(false);
break;
case View.VISIBLE:
setEnableMarquee(true);
break;
}
}
private void setEnableMarquee(boolean enabled) {
LOGD(TAG, "setEnableMarquee(" + enabled + ')');
if (artist != null) artist.setSelected(enabled);
if (title != null) title.setSelected(enabled);
}
protected boolean hasAlbumArt;
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void onPlayStateChanged(Pair<MediaMetadataCompat, PlaybackStateCompat> mediaInfo) {
if (!created) return;
super.onPlayStateChanged(mediaInfo);
LOGI(TAG, "onPlayStateChanged()");
// Update button visibility based on the transport flags.
if (null == mediaInfo.second || mediaInfo.second.getActions() <= 0) {
mBtnNext.setVisibility(View.VISIBLE);
mBtnPrev.setVisibility(View.VISIBLE);
playPause.setVisibility(View.VISIBLE);
} else {
final long flags = mediaInfo.second.getActions();
setVisibilityBasedOnFlag(mBtnPrev, flags, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
setVisibilityBasedOnFlag(mBtnNext, flags, PlaybackStateCompat.ACTION_SKIP_TO_NEXT);
setVisibilityBasedOnFlag(playPause, flags,
PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_PLAY_PAUSE);
}
// Update the text and visibility based on the information we have.
// Protocol: if either artist or title is visible, make the panel visible.
String sTitle = mediaInfo.first.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
String sArtist = mediaInfo.first.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
boolean hasArtist = !TextUtils.isEmpty(sArtist);
boolean hasTitle = !TextUtils.isEmpty(sTitle);
if (mMusicActive && (hasArtist || hasTitle)) {
transition.beginDelayedTransition(musicPanel, TransitionCompat.KEY_AUDIO_TRANSITION);
}
playPause.setImageResource(((mMusicActive ? R.drawable.pause : R.drawable.play)));
artist.setVisibility((mMusicActive && hasArtist) ? View.VISIBLE : View.GONE);
title.setVisibility((mMusicActive && hasTitle) ? View.VISIBLE : View.GONE);
artist.setText((mMusicActive && hasArtist) ? sArtist : "");
title.setText((mMusicActive && hasTitle) ? sTitle : "");
trackInfo.setVisibility(((mMusicActive && (hasArtist || hasTitle)) ? View.VISIBLE : View.GONE));
// If we have album art, use it! We'll do this last in case we run into strange
// issues with sizing/ layouts.
if (root instanceof BackgroundLinearLayout) {
BackgroundLinearLayout back = (BackgroundLinearLayout) root;
if (mMusicActive) {
Bitmap albumArt = mediaInfo.first.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
if (null != albumArt) {
final BitmapDrawable d = new BitmapDrawable(getResources(), albumArt);
back.setCustomBackgroundColor(getWPBackgroundColor());
back.setCustomBackground(d);
hasAlbumArt = true;
} else {
back.setCustomBackground(null);
hasAlbumArt = false;
}
} else {
back.setCustomBackground(null);
hasAlbumArt = false;
}
}
}
@Override
public void onStreamVolumeChange(int streamType, int volume, int max) {
// Update the icon & progress based on the volume_3 change.
StreamResources resources = StreamResources.resourceForStreamType(streamType);
resources.setVolume(volume);
trackInfo.setVisibility((mMusicActive ? View.VISIBLE : View.GONE));
LOGD(TAG, "onStreamVolumeChange(" + streamType + ", " + volume + ", " + max + ")");
SettingsHelper settingsHelper = SettingsHelper.getInstance(getContext());
if (settingsHelper.hasProperty(VolumePanel.class, VolumePanel.COLOR)) {
volText.setText(buildVolumeText(getResources(), volume, max, getColor(), lighten(getColor(), 0.75f)));
} else {
volText.setText(buildVolumeText(getResources(), volume, max));
}
volText.setTag(resources);
if (null != streamText) streamText.setText(resources.getDescRes());
if (!mMusicActive || !hasAlbumArt) {
root.setBackground(new ColorDrawable(getWPBackgroundColor()));
}
show();
}
protected int getWPBackgroundColor() {
SettingsHelper settingsHelper = SettingsHelper.getInstance(getContext());
int mBackColor = getResources().getColor(R.color.windows_phone_theme_dark);
if (settingsHelper.hasProperty(VolumePanel.class, VolumePanel.BACKGROUND)) {
mBackColor = getBackgroundColor();
}
return mBackColor;
}
public static SpannableStringBuilder buildVolumeText(Resources res, final int volume, final int max) {
return buildVolumeText(res, volume, max, res.getColor(R.color.wp_volume_index), res.getColor(R.color.wp_volume_max));
}
/** Build WP-style volume_3 text with a big volume_3 and a small maximum. */
public static SpannableStringBuilder buildVolumeText(Resources res, final int volume,
final int max, final int volColor, final int maxColor) {
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(String.valueOf(volume));
builder.append("/");
builder.append(String.valueOf(max));
int volEnd = String.valueOf(volume).length(),
len = builder.length();
builder.setSpan(new ForegroundColorSpan(maxColor), 0, volEnd, 0);
builder.setSpan(new ForegroundColorSpan(volColor), volEnd, len, 0);
builder.setSpan(new RelativeSizeSpan(1.40f), 0, volEnd, 0);
builder.setSpan(new RelativeSizeSpan(0.80f), volEnd, len, 0);
return builder;
}
@Override
public void setStretch(boolean stretchIt) {
super.setStretch(stretchIt);
onWindowAttributesChanged();
}
@Override
public void onRotationChanged(int rotation) {
super.onRotationChanged(rotation);
onWindowAttributesChanged();
}
@Override public boolean supportsMediaPlayback() { return true; }
@Override public boolean isInteractive() { return true; }
@Override public WindowManager.LayoutParams getWindowLayoutParams() {
int flags = (
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED );
WindowManager.LayoutParams WPARAMS = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0,
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, flags, PixelFormat.RGB_565);
WPARAMS.windowAnimations = android.R.style.Animation_Dialog;
WPARAMS.packageName = getContext().getPackageName();
WPARAMS.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
WPARAMS.setTitle(TAG);
Resources res = getResources();
final int panelWidth = getNotificationPanelWidth();
final int maxWidth = ((panelWidth > 0) ? panelWidth : res.getDimensionPixelSize(R.dimen.notification_panel_width));
final int menuWidth = res.getDimensionPixelSize(R.dimen.max_menu_width);
final int screenWidth = getWindowWidth();
if (stretch || (maxWidth <= 0) || (!res.getBoolean(R.bool.isTablet) && screenWidth < menuWidth)) {
WPARAMS.gravity = (Gravity.FILL_HORIZONTAL | Gravity.TOP);
} else {
WPARAMS.gravity = (Gravity.CENTER_HORIZONTAL | Gravity.TOP);
WPARAMS.width = (maxWidth <= 0) ? menuWidth : maxWidth;
}
WPARAMS.screenBrightness = WPARAMS.buttonBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
return WPARAMS;
}
}