Kore/app/src/main/java/org/xbmc/kore/utils/UIUtils.java

420 lines
17 KiB
Java

/*
* Copyright 2015 Synced Synapse. All rights reserved.
*
* 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 org.xbmc.kore.utils;
import android.animation.Animator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.WindowManager;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.type.GlobalType;
import org.xbmc.kore.jsonrpc.type.VideoType;
import org.xbmc.kore.ui.RemoteActivity;
import java.util.ArrayList;
import java.util.List;
/**
* General UI Utils
*/
public class UIUtils {
public static final float IMAGE_RESIZE_FACTOR = 1.0f;
public static final int initialButtonRepeatInterval = 400; // ms
public static final int buttonRepeatInterval = 80; // ms
/**
* Formats time based on seconds
* @param seconds seconds
* @return Formated string
*/
public static String formatTime(int seconds) {
return formatTime(seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60);
}
/**
* Formats time
*/
public static String formatTime(GlobalType.Time time) {
return formatTime(time.hours, time.minutes, time.seconds);
}
/**
* Formats time
*/
public static String formatTime(int hours, int minutes, int seconds) {
if (hours > 0) {
return String.format("%d:%02d:%02d", hours, minutes, seconds);
} else {
return String.format("%1d:%02d",minutes, seconds);
}
}
/**
* Loads an image into an imageview
* @param hostManager Hostmanager connected to the host
* @param imageUrl XBMC url of the image to load
* @param imageView Image view to load into
* @param imageWidth Width of the image, for caching purposes
* @param imageHeight Height of the image, for caching purposes
*/
public static void loadImageIntoImageview(HostManager hostManager,
String imageUrl, ImageView imageView,
int imageWidth, int imageHeight) {
// if (TextUtils.isEmpty(imageUrl)) {
// imageView.setImageResource(R.drawable.delete_ic_action_picture);
// return;
// }
if ((imageWidth) > 0 && (imageHeight > 0)) {
hostManager.getPicasso()
.load(hostManager.getHostInfo().getImageUrl(imageUrl))
.resize(imageWidth, imageHeight)
.centerCrop()
.into(imageView);
} else {
hostManager.getPicasso()
.load(hostManager.getHostInfo().getImageUrl(imageUrl))
.fit()
.centerCrop()
.into(imageView);
}
}
private static TypedArray characterAvatarColors = null;
private static int avatarColorsIdx = 0;
// private static Random randomGenerator = new Random();
/**
* Loads an image into an imageview, presenting an alternate charater avatar if empty
* @param hostManager Hostmanager connected to the host
* @param imageUrl XBMC url of the image to load
* @param stringAvatar Character avatar too present if image is null
* @param imageView Image view to load into
* @param imageWidth Width of the image, for caching purposes
* @param imageHeight Height of the image, for caching purposes
*/
public static void loadImageWithCharacterAvatar(
Context context, HostManager hostManager,
String imageUrl, String stringAvatar,
ImageView imageView,
int imageWidth, int imageHeight) {
CharacterDrawable avatarDrawable = getCharacterAvatar(context, stringAvatar);
if (TextUtils.isEmpty(imageUrl)) {
imageView.setImageDrawable(avatarDrawable);
return;
}
if ((imageWidth) > 0 && (imageHeight > 0)) {
hostManager.getPicasso()
.load(hostManager.getHostInfo().getImageUrl(imageUrl))
.placeholder(avatarDrawable)
.resize(imageWidth, imageHeight)
.centerCrop()
.into(imageView);
} else {
hostManager.getPicasso()
.load(hostManager.getHostInfo().getImageUrl(imageUrl))
.fit()
.centerCrop()
.into(imageView);
}
}
/**
* Returns a CharacterDrawable that is suitable to use as an avatar
* @param context Context
* @param str String to use to create the avatar
* @return Character avatar to use in a image view
*/
public static CharacterDrawable getCharacterAvatar(Context context, String str) {
// Load character avatar
if (characterAvatarColors == null) {
characterAvatarColors = context
.getResources()
.obtainTypedArray(R.array.character_avatar_colors);
}
char charAvatar = TextUtils.isEmpty(str) ?
' ' : str.charAt(0);
avatarColorsIdx = TextUtils.isEmpty(str) ? 0 :
Math.max(Character.getNumericValue(str.charAt(0)) +
Character.getNumericValue(str.charAt(str.length() - 1)) +
str.length(), 0) % characterAvatarColors.length();
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
return new CharacterDrawable(charAvatar, color);
}
/**
* Sets play/pause button icon on a ImageView, based on speed
* @param context Activity
* @param view ImageView/ImageButton
* @param speed Current player speed
*/
public static void setPlayPauseButtonIcon(Context context, ImageView view, int speed) {
int resAttrId = (speed == 1) ? R.attr.iconPause : R.attr.iconPlay;
int defaultResourceId = (speed == 1) ?
R.drawable.ic_pause_white_24dp :
R.drawable.ic_play_arrow_white_24dp;
TypedArray styledAttributes = context.obtainStyledAttributes(new int[]{resAttrId});
view.setImageResource(styledAttributes.getResourceId(0, defaultResourceId));
styledAttributes.recycle();
}
/**
* Fills the standard cast info list, consisting of a {@link android.widget.GridLayout}
* with actor images and a Textview with the name and the role of the additional cast.
* The number of actor presented on the {@link android.widget.GridLayout} is controlled
* through the global setting, and only actors with images are presented.
* The rest are presented in the additionalCastView TextView
*
* @param context Activity
* @param castList Cast list
* @param castListView GridLayout on which too show actors that have images
* @param additionalCastTitleView View with additional cast title
* @param additionalCastView Additional cast
*/
public static void setupCastInfo(final Context context,
List<VideoType.Cast> castList, GridLayout castListView,
TextView additionalCastTitleView, TextView additionalCastView) {
HostManager hostManager = HostManager.getInstance(context);
Resources resources = context.getResources();
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
View.OnClickListener castListClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Utils.openImdbForPerson(context, (String)v.getTag());
}
};
castListView.removeAllViews();
int numColumns = castListView.getColumnCount();
int layoutMarginPx = 2 * resources.getDimensionPixelSize(R.dimen.remote_content_hmargin);
int imageMarginPx = 2 * resources.getDimensionPixelSize(R.dimen.image_grid_margin);
int imageWidth = (displayMetrics.widthPixels - layoutMarginPx - numColumns * imageMarginPx) / numColumns;
int imageHeight = (int)(imageWidth * 1.2);
List<VideoType.Cast> noPicturesCastList = new ArrayList<VideoType.Cast>();
int maxCastPictures = Settings.DEFAULT_MAX_CAST_PICTURES;
int currentPictureNumber = 0;
for (int i = 0; i < castList.size(); i++) {
VideoType.Cast actor = castList.get(i);
if (((maxCastPictures == -1) || (currentPictureNumber < maxCastPictures)) &&
(actor.thumbnail != null)) {
// Present the picture
currentPictureNumber++;
View castView = LayoutInflater.from(context).inflate(R.layout.grid_item_cast, castListView, false);
ImageView castPicture = (ImageView) castView.findViewById(R.id.picture);
TextView castName = (TextView) castView.findViewById(R.id.name);
TextView castRole = (TextView) castView.findViewById(R.id.role);
castView.getLayoutParams().width = imageWidth;
castView.getLayoutParams().height = (int) (imageHeight * 1.2);
castView.setTag(actor.name);
castView.setOnClickListener(castListClickListener);
castName.setText(actor.name);
castRole.setText(actor.role);
UIUtils.loadImageWithCharacterAvatar(context, hostManager,
actor.thumbnail, actor.name,
castPicture, imageWidth, imageHeight);
castListView.addView(castView);
} else {
noPicturesCastList.add(actor);
}
}
// Additional cast
if (noPicturesCastList.size() > 0) {
additionalCastTitleView.setVisibility(View.VISIBLE);
additionalCastView.setVisibility(View.VISIBLE);
StringBuilder castListText = new StringBuilder();
boolean first = true;
for (VideoType.Cast cast : noPicturesCastList) {
if (!first) castListText.append("\n");
first = false;
if (!TextUtils.isEmpty(cast.role)) {
castListText.append(String.format(context.getString(R.string.cast_list_text),
cast.name, cast.role));
} else {
castListText.append(cast.name);
}
}
additionalCastView.setText(castListText);
} else {
additionalCastTitleView.setVisibility(View.GONE);
additionalCastView.setVisibility(View.GONE);
}
}
/**
* Simple wrapper to {@link NetUtils#sendWolMagicPacket(String, String, int)}
* that sends a WoL magic packet in a new thread
*
* @param context Context
* @param hostInfo Host to send WoL
*/
public static void sendWolAsync(Context context, final HostInfo hostInfo) {
if (hostInfo == null)
return;
// Send WoL magic packet on a new thread
new Thread(new Runnable() {
@Override
public void run() {
NetUtils.sendWolMagicPacket(hostInfo.getMacAddress(),
hostInfo.getAddress(), hostInfo.getWolPort());
}
}).start();
Toast.makeText(context, R.string.wol_sent, Toast.LENGTH_SHORT).show();
}
// /**
// * Sets the default {@link android.support.v4.widget.SwipeRefreshLayout} color scheme
// * @param swipeRefreshLayout layout
// */
// public static void setSwipeRefreshLayoutColorScheme(SwipeRefreshLayout swipeRefreshLayout) {
// Resources.Theme theme = swipeRefreshLayout.getContext().getTheme();
// TypedArray styledAttributes = theme.obtainStyledAttributes(new int[] {
// R.attr.refreshColor1,
// R.attr.refreshColor2,
// R.attr.refreshColor3,
// R.attr.refreshColor4,
// });
//
// swipeRefreshLayout.setColorScheme(styledAttributes.getResourceId(0, android.R.color.holo_blue_dark),
// styledAttributes.getResourceId(1, android.R.color.holo_purple),
// styledAttributes.getResourceId(2, android.R.color.holo_red_dark),
// styledAttributes.getResourceId(3, android.R.color.holo_green_dark));
// styledAttributes.recycle();
// }
// /**
// * Sets a views padding top/bottom to account for the system bars
// * (Top status and action bar, bottom nav bar, right nav bar if in ladscape mode)
// *
// * @param context Context
// * @param view View to pad
// * @param padTop Whether to set views paddingTop
// * @param padRight Whether to set views paddingRight (for nav bar in landscape mode)
// * @param padBottom Whether to set views paddingBottom
// */
// public static void setPaddingForSystemBars(Activity context, View view,
// boolean padTop, boolean padRight, boolean padBottom) {
// //if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
// SystemBarTintManager tintManager = new SystemBarTintManager(context);
// SystemBarTintManager.SystemBarConfig config = tintManager.getConfig();
//
// view.setPadding(view.getPaddingLeft(),
// padTop ? config.getPixelInsetTop(true) : view.getPaddingTop(),
// padRight? config.getPixelInsetRight() : view.getPaddingRight(),
// padBottom ? config.getPixelInsetBottom() : view.getPaddingBottom());
// }
/**
* Returns a theme resource Id given the value stored in Shared Preferences
* @param prefThemeValue Shared Preferences value for the theme
* @return Android resource id of the theme
*/
public static int getThemeResourceId(String prefThemeValue) {
switch (Integer.valueOf(prefThemeValue)) {
case 0:
return R.style.NightTheme;
case 1:
return R.style.DayTheme;
case 2:
return R.style.MistTheme;
case 3:
return R.style.SolarizedLightTheme;
case 4:
return R.style.SolarizedDarkTheme;
default:
return R.style.NightTheme;
}
}
/**
* Launches the remote activity, performing a circular reveal animation if
* Lollipop or later
*
* @param context Context
* @param centerX Center X of the animation
* @param centerY Center Y of the animation
* @param exitTransitionView View to reveal. Should occupy the whole screen and
* be invisible before calling this
*/
@TargetApi(21)
public static void switchToRemoteWithAnimation(final Context context,
int centerX, int centerY,
final View exitTransitionView) {
final Intent launchIntent = new Intent(context, RemoteActivity.class);
if (Utils.isLollipopOrLater()) {
// Show the animation
int endRadius = Math.max(exitTransitionView.getHeight(), exitTransitionView.getWidth());
Animator exitAnim = ViewAnimationUtils.createCircularReveal(exitTransitionView,
centerX, centerY, 0, endRadius);
exitAnim.setDuration(200);
exitAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
// Launch remote activity
context.startActivity(launchIntent);
}
@Override public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
exitTransitionView.setVisibility(View.VISIBLE);
exitAnim.start();
} else {
// No animation show, just launch the remote
context.startActivity(launchIntent);
}
}
}