/* * Copyright (C) 2012 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.volley.misc; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.io.Reader; import java.io.StringWriter; import java.nio.charset.Charset; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.os.StrictMode; import android.text.TextUtils; import android.util.Log; import org.apache.http.Header; import org.apache.http.HttpResponse; /** * Class containing some static utility methods. */ public class Utils { public static final int ANIMATION_FADE_IN_TIME = 200; public static final String SCHEME_FILE = ContentResolver.SCHEME_FILE; public static final String SCHEME_CONTENT = ContentResolver.SCHEME_CONTENT; public static final String SCHEME_ANDROID_RESOURCE = ContentResolver.SCHEME_ANDROID_RESOURCE; public static final String SCHEME_VIDEO = "video"; public static final String SCHEME_ASSETS = "asset"; private Utils() { }; /** * Interface for components that are internally scrollable left-to-right. */ public static interface HorizontallyScrollable { /** * Return {@code true} if the component needs to receive right-to-left * touch movements. * * @param origX * the raw x coordinate of the initial touch * @param origY * the raw y coordinate of the initial touch */ public boolean interceptMoveLeft(float origX, float origY); /** * Return {@code true} if the component needs to receive left-to-right * touch movements. * * @param origX * the raw x coordinate of the initial touch * @param origY * the raw y coordinate of the initial touch */ public boolean interceptMoveRight(float origX, float origY); } @TargetApi(11) public static void enableStrictMode() { if (Utils.hasGingerbread()) { StrictMode.ThreadPolicy.Builder threadPolicyBuilder = new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog(); StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder().detectAll().penaltyLog(); if (Utils.hasHoneycomb()) { threadPolicyBuilder.penaltyFlashScreen(); } StrictMode.setThreadPolicy(threadPolicyBuilder.build()); StrictMode.setVmPolicy(vmPolicyBuilder.build()); } } public static boolean hasFroyo() { // Can use static final constants like FROYO, declared in later versions // of the OS since they are inlined at compile time. This is guaranteed // behavior. return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; } public static boolean hasGingerbread() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; } public static boolean hasGingerbreadMR1() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1; } public static boolean hasHoneycomb() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; } public static boolean hasHoneycombMR1() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1; } public static boolean hasJellyBean() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; } public static boolean hasJellyBeanMR2() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; } public static boolean hasKitKat() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; } /** * Check how much usable space is available at a given path. * * @param path * The path to check * @return The space available in bytes */ @SuppressWarnings("deprecation") @TargetApi(9) public static long getUsableSpace(File path) { if (Utils.hasGingerbread()) { return path.getUsableSpace(); } final StatFs stats = new StatFs(path.getPath()); return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); } /* * private File getDiskCacheDir(Context context, String uniqueName) { final * String cachePath = context.getCacheDir().getPath(); return new * File(cachePath + File.separator + uniqueName); } */ /** * Get a usable cache directory (external if available, internal otherwise). * * @param context * The context to use * @param uniqueName * A unique directory name to append to the cache dir * @return The cache dir */ public static File getDiskCacheDir(Context context, String uniqueName) { // Check if media is mounted or storage is built-in, if so, try and use // external cache dir // otherwise use internal cache dir // TODO: getCacheDir() should be moved to a background thread as it // attempts to create the // directory if it does not exist (no disk access should happen on the // main/UI thread). final String cachePath; if (isExternalMounted() && null != getExternalCacheDir(context)) { cachePath = getExternalCacheDir(context).getPath(); } else { cachePath = context.getCacheDir().getPath(); } Log.i("Cache dir", cachePath + File.separator + uniqueName); return new File(cachePath + File.separator + uniqueName); } /** * Get the external app cache directory. * * @param context * The context to use * @return The external cache dir */ private static File getExternalCacheDir(Context context) { // TODO: This needs to be moved to a background thread to ensure no disk // access on the // main/UI thread as unfortunately getExternalCacheDir() calls mkdirs() // for us (even // though the Volley library will later try and call mkdirs() as well // from a background // thread). return context.getExternalCacheDir(); } @SuppressLint("NewApi") private static boolean isExternalMounted() { if (Utils.hasGingerbread()) { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable(); } return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } public static final Charset US_ASCII = Charset.forName("US-ASCII"); public static final Charset UTF_8 = Charset.forName("UTF-8"); public static String readFully(Reader reader) throws IOException { try { StringWriter writer = new StringWriter(); char[] buffer = new char[1024]; int count; while ((count = reader.read(buffer)) != -1) { writer.write(buffer, 0, count); } return writer.toString(); } finally { reader.close(); } } /** * Deletes the contents of {@code dir}. Throws an IOException if any file * could not be deleted, or if {@code dir} is not a readable directory. */ public static void deleteContents(File dir) throws IOException { File[] files = dir.listFiles(); if (files == null) { throw new IOException("not a readable directory: " + dir); } for (File file : files) { if (file.isDirectory()) { deleteContents(file); } if (!file.delete()) { throw new IOException("failed to delete file: " + file); } } } public static void closeQuietly(/* Auto */Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (RuntimeException rethrown) { throw rethrown; } catch (Exception ignored) { } } } public static boolean isSpecialType(String url){ boolean isSpecial = url.startsWith(SCHEME_FILE) || url.startsWith(SCHEME_VIDEO) || url.startsWith(SCHEME_CONTENT) || url.startsWith(SCHEME_ANDROID_RESOURCE); return isSpecial; } public static String getSchemeBaseUrl(String type, String url){ return type + "://" + url; } public static String getSchemeBaseUrl(String type, int id){ return type + "://" + id; } public static String getHeader(HttpResponse response, String key) { Header header = response.getFirstHeader(key); return header == null ? null : header.getValue(); } public static boolean isSupportRange(HttpResponse response) { if (TextUtils.equals(getHeader(response, "Accept-Ranges"), "bytes")) { return true; } String value = getHeader(response, "Content-Range"); return value != null && value.startsWith("bytes"); } public static boolean isGzipContent(HttpResponse response) { return TextUtils.equals(getHeader(response, "Content-Encoding"), "gzip"); } }