Android - UniversalMusicPlayer 代码
1、LogHelper
package com.example.android.uamp.utils;
import android.util.Log;
import com.example.android.uamp.BuildConfig;
public class LogHelper {
private static final String LOG_PREFIX = "uamp_";
private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
private static final int MAX_LOG_TAG_LENGTH = 23;
public static String makeLogTag(String str) {
if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
}
return LOG_PREFIX + str;
}
/**
* Don't use this when obfuscating class names!
*/
public static String makeLogTag(Class cls) {
return makeLogTag(cls.getSimpleName());
}
public static void v(String tag, Object... messages) {
// Only log VERBOSE if build type is DEBUG
if (BuildConfig.DEBUG) {
log(tag, Log.VERBOSE, null, messages);
}
}
public static void d(String tag, Object... messages) {
// Only log DEBUG if build type is DEBUG
if (BuildConfig.DEBUG) {
log(tag, Log.DEBUG, null, messages);
}
}
public static void i(String tag, Object... messages) {
log(tag, Log.INFO, null, messages);
}
public static void w(String tag, Object... messages) {
log(tag, Log.WARN, null, messages);
}
public static void w(String tag, Throwable t, Object... messages) {
log(tag, Log.WARN, t, messages);
}
public static void e(String tag, Object... messages) {
log(tag, Log.ERROR, null, messages);
}
public static void e(String tag, Throwable t, Object... messages) {
log(tag, Log.ERROR, t, messages);
}
public static void log(String tag, int level, Throwable t, Object... messages) {
if (Log.isLoggable(tag, level)) {
String message;
if (t == null && messages != null && messages.length == 1) {
// handle this common case without the extra cost of creating a stringbuffer:
message = messages[0].toString();
} else {
StringBuilder sb = new StringBuilder();
if (messages != null) for (Object m : messages) {
sb.append(m);
}
if (t != null) {
sb.append("\n").append(Log.getStackTraceString(t));
}
message = sb.toString();
}
Log.println(level, tag, message);
}
}
}
2、ImageCache
package com.example.android.uamp;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.util.LruCache;
import com.example.android.uamp.utils.BitmapHelper;
import com.example.android.uamp.utils.LogHelper;
import java.io.IOException;
/**
* Implements a basic cache of album arts, with async loading support.
*/
public final class AlbumArtCache {
private static final String TAG = LogHelper.makeLogTag(AlbumArtCache.class);
private static final int MAX_ALBUM_ART_CACHE_SIZE = 12*1024*1024; // 12 MB
private static final int MAX_ART_WIDTH = 800; // pixels
private static final int MAX_ART_HEIGHT = 480; // pixels
// Resolution reasonable for carrying around as an icon (generally in
// MediaDescription.getIconBitmap). This should not be bigger than necessary, because
// the MediaDescription object should be lightweight. If you set it too high and try to
// serialize the MediaDescription, you may get FAILED BINDER TRANSACTION errors.
private static final int MAX_ART_WIDTH_ICON = 128; // pixels
private static final int MAX_ART_HEIGHT_ICON = 128; // pixels
private static final int BIG_BITMAP_INDEX = 0;
private static final int ICON_BITMAP_INDEX = 1;
private final LruCache<String, Bitmap[]> mCache;
private static final AlbumArtCache sInstance = new AlbumArtCache();
public static AlbumArtCache getInstance() {
return sInstance;
}
private AlbumArtCache() {
// Holds no more than MAX_ALBUM_ART_CACHE_SIZE bytes, bounded by maxmemory/4 and
// Integer.MAX_VALUE:
int maxSize = Math.min(MAX_ALBUM_ART_CACHE_SIZE,
(int) (Math.min(Integer.MAX_VALUE, Runtime.getRuntime().maxMemory()/4)));
mCache = new LruCache<String, Bitmap[]>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap[] value) {
return value[BIG_BITMAP_INDEX].getByteCount()
+ value[ICON_BITMAP_INDEX].getByteCount();
}
};
}
public Bitmap getBigImage(String artUrl) {
Bitmap[] result = mCache.get(artUrl);
return result == null ? null : result[BIG_BITMAP_INDEX];
}
public Bitmap getIconImage(String artUrl) {
Bitmap[] result = mCache.get(artUrl);
return result == null ? null : result[ICON_BITMAP_INDEX];
}
public void fetch(final String artUrl, final FetchListener listener) {
// WARNING: for the sake of simplicity, simultaneous multi-thread fetch requests
// are not handled properly: they may cause redundant costly operations, like HTTP
// requests and bitmap rescales. For production-level apps, we recommend you use
// a proper image loading library, like Glide.
Bitmap[] bitmap = mCache.get(artUrl);
if (bitmap != null) {
LogHelper.d(TAG, "getOrFetch: album art is in cache, using it", artUrl);
listener.onFetched(artUrl, bitmap[BIG_BITMAP_INDEX], bitmap[ICON_BITMAP_INDEX]);
return;
}
LogHelper.d(TAG, "getOrFetch: starting asynctask to fetch ", artUrl);
new AsyncTask<Void, Void, Bitmap[]>() {
@Override
protected Bitmap[] doInBackground(Void[] objects) {
Bitmap[] bitmaps;
try {
Bitmap bitmap = BitmapHelper.fetchAndRescaleBitmap(artUrl,
MAX_ART_WIDTH, MAX_ART_HEIGHT);
Bitmap icon = BitmapHelper.scaleBitmap(bitmap,
MAX_ART_WIDTH_ICON, MAX_ART_HEIGHT_ICON);
bitmaps = new Bitmap[] {bitmap, icon};
mCache.put(artUrl, bitmaps);
} catch (IOException e) {
return null;
}
LogHelper.d(TAG, "doInBackground: putting bitmap in cache. cache size=" +
mCache.size());
return bitmaps;
}
@Override
protected void onPostExecute(Bitmap[] bitmaps) {
if (bitmaps == null) {
listener.onError(artUrl, new IllegalArgumentException("got null bitmaps"));
} else {
listener.onFetched(artUrl,
bitmaps[BIG_BITMAP_INDEX], bitmaps[ICON_BITMAP_INDEX]);
}
}
}.execute();
}
public static abstract class FetchListener {
public abstract void onFetched(String artUrl, Bitmap bigImage, Bitmap iconImage);
public void onError(String artUrl, Exception e) {
LogHelper.e(TAG, e, "AlbumArtFetchListener: error while downloading " + artUrl);
}
}
}
3、BitmapHelper
package com.example.android.uamp.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class BitmapHelper {
private static final String TAG = LogHelper.makeLogTag(BitmapHelper.class);
// Max read limit that we allow our input stream to mark/reset.
private static final int MAX_READ_LIMIT_PER_IMG = 1024 * 1024;
public static Bitmap scaleBitmap(Bitmap src, int maxWidth, int maxHeight) {
double scaleFactor = Math.min(
((double) maxWidth)/src.getWidth(), ((double) maxHeight)/src.getHeight());
return Bitmap.createScaledBitmap(src,
(int) (src.getWidth() * scaleFactor), (int) (src.getHeight() * scaleFactor), false);
}
public static Bitmap scaleBitmap(int scaleFactor, InputStream is) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
return BitmapFactory.decodeStream(is, null, bmOptions);
}
public static int findScaleFactor(int targetW, int targetH, InputStream is) {
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, bmOptions);
int actualW = bmOptions.outWidth;
int actualH = bmOptions.outHeight;
// Determine how much to scale down the image
return Math.min(actualW/targetW, actualH/targetH);
}
@SuppressWarnings("SameParameterValue")
public static Bitmap fetchAndRescaleBitmap(String uri, int width, int height)
throws IOException {
URL url = new URL(uri);
BufferedInputStream is = null;
try {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(urlConnection.getInputStream());
is.mark(MAX_READ_LIMIT_PER_IMG);
int scaleFactor = findScaleFactor(width, height, is);
LogHelper.d(TAG, "Scaling bitmap ", uri, " by factor ", scaleFactor, " to support ",
width, "x", height, "requested dimension");
is.reset();
return scaleBitmap(scaleFactor, is);
} finally {
if (is != null) {
is.close();
}
}
}
}
4、网络是否正常
package com.example.android.uamp.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
* Generic reusable network methods.
*/
public class NetworkHelper {
/**
* @param context to use to check for network connectivity.
* @return true if connected, false otherwise.
*/
public static boolean isOnline(Context context) {
ConnectivityManager connMgr = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
}
5、从主题中获取颜色值
public class ResourceHelper {
/**
* Get a color value from a theme attribute.
* @param context used for getting the color.
* @param attribute theme attribute.
* @param defaultColor default to use.
* @return color value
*/
public static int getThemeColor(Context context, int attribute, int defaultColor) {
int themeColor = 0;
String packageName = context.getPackageName();
try {
Context packageContext = context.createPackageContext(packageName, 0);
ApplicationInfo applicationInfo =
context.getPackageManager().getApplicationInfo(packageName, 0);
packageContext.setTheme(applicationInfo.theme);
Resources.Theme theme = packageContext.getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {attribute});
themeColor = ta.getColor(0, defaultColor);
ta.recycle();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return themeColor;
}
}
浙公网安备 33010602011771号