From 0581b7cc53d27b5e46cebc327640b25e737eb42b Mon Sep 17 00:00:00 2001 From: seven332 Date: Sun, 6 Apr 2014 11:11:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=9B=B4=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E7=9A=84=20Log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/com/hippo/ehviewer/BeautifyScreen.java | 222 +- src/com/hippo/ehviewer/DiskCache.java | 612 ++-- src/com/hippo/ehviewer/ImageLoadManager.java | 2 +- .../ehviewer/activity/DownloadActivity.java | 2 +- .../ehviewer/activity/FavouriteActivity.java | 2 +- .../ehviewer/activity/MangaActivity.java | 2 +- .../activity/MangaDetailActivity.java | 2 +- .../activity/MangaDownloadActivity.java | 2 +- .../hippo/ehviewer/gallery/GalleryView.java | 2 +- .../hippo/ehviewer/gallery/common/Utils.java | 2 +- .../hippo/ehviewer/gallery/data/ImageSet.java | 2 +- .../gallery/glrenderer/BasicTexture.java | 2 +- .../gallery/glrenderer/GLES11Canvas.java | 2 +- .../gallery/glrenderer/GLES20Canvas.java | 2 +- .../gallery/glrenderer/MovieTexture.java | 2 +- .../gallery/glrenderer/RawTexture.java | 2 +- .../gallery/glrenderer/UploadedTexture.java | 2 +- .../hippo/ehviewer/gallery/ui/GLRootView.java | 2 +- src/com/hippo/ehviewer/gallery/ui/GLView.java | 2 +- .../ehviewer/gallery/util/GalleryUtils.java | 2 +- .../hippo/ehviewer/network/Downloader.java | 2 +- .../hippo/ehviewer/network/HttpHelper.java | 2 +- .../ehviewer/service/DownloadService.java | 2 +- src/com/hippo/ehviewer/util/Cache.java | 2 +- src/com/hippo/ehviewer/util/Crash.java | 392 +-- src/com/hippo/ehviewer/util/EhClient.java | 2 +- src/com/hippo/ehviewer/util/Log.java | 59 + src/com/hippo/ehviewer/util/ThreadPool.java | 2 +- .../view/GetPaddingRelativeLayout.java | 2 +- src/com/hippo/ehviewer/view/OlImageView.java | 2 +- .../hippo/ehviewer/view/SuperImageView.java | 3016 ++++++++--------- .../hippo/ehviewer/widget/AutoWrapLayout.java | 2 +- .../hippo/ehviewer/widget/DrawerLayout.java | 2 +- .../disklrucache/DiskLruCache.java | 2 +- 34 files changed, 2209 insertions(+), 2150 deletions(-) create mode 100644 src/com/hippo/ehviewer/util/Log.java diff --git a/src/com/hippo/ehviewer/BeautifyScreen.java b/src/com/hippo/ehviewer/BeautifyScreen.java index f3d01921c..13c516473 100644 --- a/src/com/hippo/ehviewer/BeautifyScreen.java +++ b/src/com/hippo/ehviewer/BeautifyScreen.java @@ -1,111 +1,111 @@ -package com.hippo.ehviewer; - -import com.hippo.ehviewer.util.Ui; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.Resources; -import android.os.Build; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Gravity; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.FrameLayout.LayoutParams; - -@SuppressLint("NewApi") -public class BeautifyScreen { - private static final String TAG = "BeautifyScreen"; - private static Context mContext; - private static int mStatusBarHeight = 0; - private static int mNavigationBarHeight = 0; - - private static int sbbId = 0; - private static int nbbId = 1; - - private BeautifyScreen(){} - - public static void init(Context context) { - mContext = context; - if (Build.VERSION.SDK_INT >= 19) { - Resources resources = context.getResources(); - int sbhResourceId = resources.getIdentifier("status_bar_height", - "dimen", "android"); - if (sbhResourceId > 0) - mStatusBarHeight = resources - .getDimensionPixelSize(sbhResourceId); - int nbhResourceId = resources.getIdentifier( - "navigation_bar_height", "dimen", "android"); - if (nbhResourceId > 0) - mNavigationBarHeight = resources - .getDimensionPixelSize(nbhResourceId); - } - } - - public static void ColourfyScreen(Activity activity) { - if (Build.VERSION.SDK_INT >= 19) { - Resources resources = activity.getResources(); - Window win = activity.getWindow(); - - WindowManager.LayoutParams winParams = win.getAttributes(); - winParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; - win.setAttributes(winParams); - - ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); - // Add status bar background - if (mStatusBarHeight > 0) { - View statusBarView = new View(mContext); - LayoutParams params1 = new LayoutParams( - LayoutParams.MATCH_PARENT, mStatusBarHeight); - params1.gravity = Gravity.TOP; - statusBarView.setLayoutParams(params1); - statusBarView.setBackgroundColor(resources - .getColor(R.color.blue_dark)); - statusBarView.setId(sbbId); - decorViewGroup.addView(statusBarView); - } - - // Add navigation bar background - if (Ui.hasNavigationBar() && mNavigationBarHeight > 0 && - isPortrait(activity)) { - View navigationBarView = new View(mContext); - LayoutParams params2 = new LayoutParams( - LayoutParams.MATCH_PARENT, mNavigationBarHeight); - params2.gravity = Gravity.BOTTOM; - navigationBarView.setLayoutParams(params2); - navigationBarView.setBackgroundColor(resources - .getColor(R.color.blue_dark)); - navigationBarView.setId(nbbId); - decorViewGroup.addView(navigationBarView); - } - } - } - - private static boolean isPortrait(Activity activity) { - DisplayMetrics dm = new DisplayMetrics(); - activity.getWindowManager().getDefaultDisplay().getMetrics(dm); - return dm.heightPixels > dm.widthPixels; - } - - public static void fixColour(Activity activity) { - if (Build.VERSION.SDK_INT < 19) - return; - Window win = activity.getWindow(); - ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); - View navigationBarView = decorViewGroup.findViewById(nbbId); - if (navigationBarView == null) - return; - if (!isPortrait(activity)) - navigationBarView.setVisibility(View.GONE); - else - navigationBarView.setVisibility(View.VISIBLE); - } -} +package com.hippo.ehviewer; + +import com.hippo.ehviewer.util.Ui; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Resources; +import android.os.Build; +import android.util.DisplayMetrics; +import com.hippo.ehviewer.util.Log; +import android.view.Gravity; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout.LayoutParams; + +@SuppressLint("NewApi") +public class BeautifyScreen { + private static final String TAG = "BeautifyScreen"; + private static Context mContext; + private static int mStatusBarHeight = 0; + private static int mNavigationBarHeight = 0; + + private static int sbbId = 0; + private static int nbbId = 1; + + private BeautifyScreen(){} + + public static void init(Context context) { + mContext = context; + if (Build.VERSION.SDK_INT >= 19) { + Resources resources = context.getResources(); + int sbhResourceId = resources.getIdentifier("status_bar_height", + "dimen", "android"); + if (sbhResourceId > 0) + mStatusBarHeight = resources + .getDimensionPixelSize(sbhResourceId); + int nbhResourceId = resources.getIdentifier( + "navigation_bar_height", "dimen", "android"); + if (nbhResourceId > 0) + mNavigationBarHeight = resources + .getDimensionPixelSize(nbhResourceId); + } + } + + public static void ColourfyScreen(Activity activity) { + if (Build.VERSION.SDK_INT >= 19) { + Resources resources = activity.getResources(); + Window win = activity.getWindow(); + + WindowManager.LayoutParams winParams = win.getAttributes(); + winParams.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS + | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; + win.setAttributes(winParams); + + ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); + // Add status bar background + if (mStatusBarHeight > 0) { + View statusBarView = new View(mContext); + LayoutParams params1 = new LayoutParams( + LayoutParams.MATCH_PARENT, mStatusBarHeight); + params1.gravity = Gravity.TOP; + statusBarView.setLayoutParams(params1); + statusBarView.setBackgroundColor(resources + .getColor(R.color.blue_dark)); + statusBarView.setId(sbbId); + decorViewGroup.addView(statusBarView); + } + + // Add navigation bar background + if (Ui.hasNavigationBar() && mNavigationBarHeight > 0 && + isPortrait(activity)) { + View navigationBarView = new View(mContext); + LayoutParams params2 = new LayoutParams( + LayoutParams.MATCH_PARENT, mNavigationBarHeight); + params2.gravity = Gravity.BOTTOM; + navigationBarView.setLayoutParams(params2); + navigationBarView.setBackgroundColor(resources + .getColor(R.color.blue_dark)); + navigationBarView.setId(nbbId); + decorViewGroup.addView(navigationBarView); + } + } + } + + private static boolean isPortrait(Activity activity) { + DisplayMetrics dm = new DisplayMetrics(); + activity.getWindowManager().getDefaultDisplay().getMetrics(dm); + return dm.heightPixels > dm.widthPixels; + } + + public static void fixColour(Activity activity) { + if (Build.VERSION.SDK_INT < 19) + return; + Window win = activity.getWindow(); + ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); + View navigationBarView = decorViewGroup.findViewById(nbbId); + if (navigationBarView == null) + return; + if (!isPortrait(activity)) + navigationBarView.setVisibility(View.GONE); + else + navigationBarView.setVisibility(View.VISIBLE); + } +} diff --git a/src/com/hippo/ehviewer/DiskCache.java b/src/com/hippo/ehviewer/DiskCache.java index 304928d0f..6c85c6ca5 100644 --- a/src/com/hippo/ehviewer/DiskCache.java +++ b/src/com/hippo/ehviewer/DiskCache.java @@ -1,306 +1,306 @@ -package com.hippo.ehviewer; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import com.hippo.ehviewer.util.Cache; -import com.hippo.ehviewer.util.Ui; -import com.hippo.ehviewer.util.Util; -import com.jakewharton.disklrucache.DiskLruCache; - -import android.content.Context; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Movie; -import android.os.Environment; -import android.util.Log; - -// For Gallery cover key is gid, -// for example , gid is 618395, token is 0439fa3666, -// so key is 618395 - -// For Gallery preview key is gid-preview-num-row, -// for example , gid is 618395, token is 0439fa3666, page2 -// so key is 618395-preview-2-0 - -// For Gallery image key is gid-preview-num, -// for example , gid is 618395, token is 0439fa3666, image2 -// so key is 618395-image-2 - -public class DiskCache { - private static String TAG = "ImageDiskCache"; - - private static final int VALUE_IDX = 0; - - private Context mContext; - private BitmapFactory.Options opt; - private final DiskLruCache diskLruCache; - - public DiskCache(Context context, String path, long maxSize) throws NameNotFoundException, IOException { - this.mContext = context; - - opt = Ui.getBitmapOpt(); - if (!Cache.hasSdCard()) { - throw new IllegalStateException("Has no SdCard."); - } - - File dir = new File(Environment.getExternalStorageDirectory() + path); - int version = mContext.getPackageManager().getPackageInfo( - mContext.getPackageName(), 0).versionCode; - version = 1; - diskLruCache = DiskLruCache.open(dir, version, 1, maxSize); - } - - public long size() { - return diskLruCache.size(); - } - - public long maxSize() { - return diskLruCache.getMaxSize(); - } - - public void setMaxSize(long maxSize) { - diskLruCache.setMaxSize(maxSize); - } - - public void close() { - try { - diskLruCache.close(); - } catch (IOException e) { - Log.d(TAG, "diskLruCache close error !"); - } - } - - public boolean hasKey(String key) { - DiskLruCache.Snapshot snapshot = null; - try { - snapshot = diskLruCache.get(key); - } catch (IOException e) { - } - if(snapshot==null) - return false; - snapshot.close(); - return true; - } - - public Object get(String key, int type) { - DiskLruCache.Snapshot snapshot = null; - try { - snapshot = diskLruCache.get(key); - } catch (IOException e) { - } - if (snapshot == null) - return null; - InputStream is = snapshot.getInputStream(VALUE_IDX); - if (type == Util.BITMAP) { - Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt); - return bitmap; - } else if (type == Util.MOVIE) { - is = new BufferedInputStream(is, 16 * 1024); - is.mark(16 * 1024); - Movie movie = Movie.decodeStream(is); - return movie; - } - return null; - } - - public boolean put(String key, InputStream is) throws IOException { - DiskLruCache.Editor editor = diskLruCache.edit(key); - if (editor == null) - return false; - BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX)); - OutputStream os = new CacheOutputStream(bos, editor); - Util.copy(is, os); - return true; - } - - public void clear() { - try { - diskLruCache.delete(); - } catch (IOException e) { - } - } - - private class CacheOutputStream extends FilterOutputStream { - - private final DiskLruCache.Editor editor; - private boolean failed = false; - - private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) { - super(os); - this.editor = editor; - } - - @Override - public void close() throws IOException { - IOException closeException = null; - try { - super.close(); - } catch (IOException e) { - closeException = e; - } - - if (failed) { - editor.abort(); - } else { - editor.commit(); - } - - if (closeException != null) throw closeException; - } - - @Override - public void flush() throws IOException { - try { - super.flush(); - } catch (IOException e) { - failed = true; - throw e; - } - } - - @Override - public void write(int oneByte) throws IOException { - try { - super.write(oneByte); - } catch (IOException e) { - failed = true; - throw e; - } - } - - @Override - public void write(byte[] buffer) throws IOException { - try { - super.write(buffer); - } catch (IOException e) { - failed = true; - throw e; - } - } - - @Override - public void write(byte[] buffer, int offset, int length) throws IOException { - try { - super.write(buffer, offset, length); - } catch (IOException e) { - failed = true; - throw e; - } - } - } - /* - private Context context = null; - private File dir = null; - int maxSize = 1024 * 10; - - public DiskCache(Context context, String path, int maxSize) { - this.context = context; - this.maxSize = maxSize; - if (maxSize <= 0) - maxSize = 1024 * 10; - - File rootdir = Environment.getExternalStorageDirectory(); - dir = new File(rootdir + path); - dir.mkdirs(); - } - - public boolean hasKey(String key) { - synchronized (key) { - - File file = new File(dir, key); - if (file.exists()) { - if (file.isDirectory()) - file.delete(); - else - return true; - } - return false; - } - } - - public Object get(String key, int type) { - synchronized (key) { - File file = new File(dir, key); - if (!file.isFile()) - return null; - try { - InputStream is = new FileInputStream(file); - if (type == Util.BITMAP) { - Bitmap bitmap = BitmapFactory.decodeStream(is, null, Util.opt); - return bitmap; - } else if (type == Util.MOVIE) { - is = new BufferedInputStream(is, 16 * 1024); - is.mark(16 * 1024); - Movie movie = Movie.decodeStream(is); - return movie; - } - } catch (FileNotFoundException e) { - Log.d(TAG, "WTF, I have checked that the file does exists."); - e.printStackTrace(); - } - return null; - } - } - - public boolean put(String key, InputStream is) throws IOException { - synchronized (key) { - File file = new File(dir, key); - try { - file.delete(); - file.createNewFile(); - } catch (IOException e) { - dir.mkdirs(); - file.delete(); - try { - file.createNewFile(); - } catch (IOException e1) { - Log.d(TAG, "WTF, mkdirs didn't work? Path is " + file.getPath()); - e1.printStackTrace(); - return false; - } - } - try { - Util.inputStream2File(is, file); - return true; - } catch (IOException e) { - Log.d(TAG, "Put " + key + " error."); - throw e; - } - } - } - - public void clear() { - clearDirectory(dir); - } - - private void clearDirectory(File dir) { - - for (File file: dir.listFiles()) { - if (file.isDirectory()) clearDirectory(file); - file.delete(); - } - - }*/ - /* - * private static String getStingMD5(String s) { String re = null; try { - * MessageDigest messageDigest =MessageDigest.getInstance(Algorithm); byte[] - * inputByteArray = s.getBytes(); messageDigest.update(inputByteArray); - * byte[] resultByteArray = messageDigest.digest(); re = - * byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { - * e.printStackTrace(); Log.d(TAG, e.getMessage()); } return re; } - * - * private static String byteArrayToHex(byte[] byteArray) { char[] hexDigits - * = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' }; - * char[] resultCharArray =new char[byteArray.length * 2]; int index = 0; - * for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 & - * 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } return new - * String(resultCharArray); } - */ -} +package com.hippo.ehviewer; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.hippo.ehviewer.util.Cache; +import com.hippo.ehviewer.util.Ui; +import com.hippo.ehviewer.util.Util; +import com.jakewharton.disklrucache.DiskLruCache; + +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Movie; +import android.os.Environment; +import com.hippo.ehviewer.util.Log; + +// For Gallery cover key is gid, +// for example , gid is 618395, token is 0439fa3666, +// so key is 618395 + +// For Gallery preview key is gid-preview-num-row, +// for example , gid is 618395, token is 0439fa3666, page2 +// so key is 618395-preview-2-0 + +// For Gallery image key is gid-preview-num, +// for example , gid is 618395, token is 0439fa3666, image2 +// so key is 618395-image-2 + +public class DiskCache { + private static String TAG = "ImageDiskCache"; + + private static final int VALUE_IDX = 0; + + private Context mContext; + private BitmapFactory.Options opt; + private final DiskLruCache diskLruCache; + + public DiskCache(Context context, String path, long maxSize) throws NameNotFoundException, IOException { + this.mContext = context; + + opt = Ui.getBitmapOpt(); + if (!Cache.hasSdCard()) { + throw new IllegalStateException("Has no SdCard."); + } + + File dir = new File(Environment.getExternalStorageDirectory() + path); + int version = mContext.getPackageManager().getPackageInfo( + mContext.getPackageName(), 0).versionCode; + version = 1; + diskLruCache = DiskLruCache.open(dir, version, 1, maxSize); + } + + public long size() { + return diskLruCache.size(); + } + + public long maxSize() { + return diskLruCache.getMaxSize(); + } + + public void setMaxSize(long maxSize) { + diskLruCache.setMaxSize(maxSize); + } + + public void close() { + try { + diskLruCache.close(); + } catch (IOException e) { + Log.d(TAG, "diskLruCache close error !"); + } + } + + public boolean hasKey(String key) { + DiskLruCache.Snapshot snapshot = null; + try { + snapshot = diskLruCache.get(key); + } catch (IOException e) { + } + if(snapshot==null) + return false; + snapshot.close(); + return true; + } + + public Object get(String key, int type) { + DiskLruCache.Snapshot snapshot = null; + try { + snapshot = diskLruCache.get(key); + } catch (IOException e) { + } + if (snapshot == null) + return null; + InputStream is = snapshot.getInputStream(VALUE_IDX); + if (type == Util.BITMAP) { + Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt); + return bitmap; + } else if (type == Util.MOVIE) { + is = new BufferedInputStream(is, 16 * 1024); + is.mark(16 * 1024); + Movie movie = Movie.decodeStream(is); + return movie; + } + return null; + } + + public boolean put(String key, InputStream is) throws IOException { + DiskLruCache.Editor editor = diskLruCache.edit(key); + if (editor == null) + return false; + BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX)); + OutputStream os = new CacheOutputStream(bos, editor); + Util.copy(is, os); + return true; + } + + public void clear() { + try { + diskLruCache.delete(); + } catch (IOException e) { + } + } + + private class CacheOutputStream extends FilterOutputStream { + + private final DiskLruCache.Editor editor; + private boolean failed = false; + + private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) { + super(os); + this.editor = editor; + } + + @Override + public void close() throws IOException { + IOException closeException = null; + try { + super.close(); + } catch (IOException e) { + closeException = e; + } + + if (failed) { + editor.abort(); + } else { + editor.commit(); + } + + if (closeException != null) throw closeException; + } + + @Override + public void flush() throws IOException { + try { + super.flush(); + } catch (IOException e) { + failed = true; + throw e; + } + } + + @Override + public void write(int oneByte) throws IOException { + try { + super.write(oneByte); + } catch (IOException e) { + failed = true; + throw e; + } + } + + @Override + public void write(byte[] buffer) throws IOException { + try { + super.write(buffer); + } catch (IOException e) { + failed = true; + throw e; + } + } + + @Override + public void write(byte[] buffer, int offset, int length) throws IOException { + try { + super.write(buffer, offset, length); + } catch (IOException e) { + failed = true; + throw e; + } + } + } + /* + private Context context = null; + private File dir = null; + int maxSize = 1024 * 10; + + public DiskCache(Context context, String path, int maxSize) { + this.context = context; + this.maxSize = maxSize; + if (maxSize <= 0) + maxSize = 1024 * 10; + + File rootdir = Environment.getExternalStorageDirectory(); + dir = new File(rootdir + path); + dir.mkdirs(); + } + + public boolean hasKey(String key) { + synchronized (key) { + + File file = new File(dir, key); + if (file.exists()) { + if (file.isDirectory()) + file.delete(); + else + return true; + } + return false; + } + } + + public Object get(String key, int type) { + synchronized (key) { + File file = new File(dir, key); + if (!file.isFile()) + return null; + try { + InputStream is = new FileInputStream(file); + if (type == Util.BITMAP) { + Bitmap bitmap = BitmapFactory.decodeStream(is, null, Util.opt); + return bitmap; + } else if (type == Util.MOVIE) { + is = new BufferedInputStream(is, 16 * 1024); + is.mark(16 * 1024); + Movie movie = Movie.decodeStream(is); + return movie; + } + } catch (FileNotFoundException e) { + Log.d(TAG, "WTF, I have checked that the file does exists."); + e.printStackTrace(); + } + return null; + } + } + + public boolean put(String key, InputStream is) throws IOException { + synchronized (key) { + File file = new File(dir, key); + try { + file.delete(); + file.createNewFile(); + } catch (IOException e) { + dir.mkdirs(); + file.delete(); + try { + file.createNewFile(); + } catch (IOException e1) { + Log.d(TAG, "WTF, mkdirs didn't work? Path is " + file.getPath()); + e1.printStackTrace(); + return false; + } + } + try { + Util.inputStream2File(is, file); + return true; + } catch (IOException e) { + Log.d(TAG, "Put " + key + " error."); + throw e; + } + } + } + + public void clear() { + clearDirectory(dir); + } + + private void clearDirectory(File dir) { + + for (File file: dir.listFiles()) { + if (file.isDirectory()) clearDirectory(file); + file.delete(); + } + + }*/ + /* + * private static String getStingMD5(String s) { String re = null; try { + * MessageDigest messageDigest =MessageDigest.getInstance(Algorithm); byte[] + * inputByteArray = s.getBytes(); messageDigest.update(inputByteArray); + * byte[] resultByteArray = messageDigest.digest(); re = + * byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { + * e.printStackTrace(); Log.d(TAG, e.getMessage()); } return re; } + * + * private static String byteArrayToHex(byte[] byteArray) { char[] hexDigits + * = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' }; + * char[] resultCharArray =new char[byteArray.length * 2]; int index = 0; + * for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 & + * 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } return new + * String(resultCharArray); } + */ +} diff --git a/src/com/hippo/ehviewer/ImageLoadManager.java b/src/com/hippo/ehviewer/ImageLoadManager.java index f628923f0..416710a18 100644 --- a/src/com/hippo/ehviewer/ImageLoadManager.java +++ b/src/com/hippo/ehviewer/ImageLoadManager.java @@ -12,7 +12,7 @@ import android.graphics.Bitmap; import android.os.Handler; import android.os.Message; import android.support.v4.util.LruCache; -import android.util.Log; +import com.hippo.ehviewer.util.Log; public class ImageLoadManager { private static final String TAG = "ImageLoadManager"; diff --git a/src/com/hippo/ehviewer/activity/DownloadActivity.java b/src/com/hippo/ehviewer/activity/DownloadActivity.java index fa859b978..b8b2ffa5f 100644 --- a/src/com/hippo/ehviewer/activity/DownloadActivity.java +++ b/src/com/hippo/ehviewer/activity/DownloadActivity.java @@ -33,7 +33,7 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; diff --git a/src/com/hippo/ehviewer/activity/FavouriteActivity.java b/src/com/hippo/ehviewer/activity/FavouriteActivity.java index 863400621..808f55899 100644 --- a/src/com/hippo/ehviewer/activity/FavouriteActivity.java +++ b/src/com/hippo/ehviewer/activity/FavouriteActivity.java @@ -8,7 +8,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; diff --git a/src/com/hippo/ehviewer/activity/MangaActivity.java b/src/com/hippo/ehviewer/activity/MangaActivity.java index 3cee4bda6..5d873bb38 100644 --- a/src/com/hippo/ehviewer/activity/MangaActivity.java +++ b/src/com/hippo/ehviewer/activity/MangaActivity.java @@ -37,7 +37,7 @@ import android.os.Build; import android.os.Bundle; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.Window; diff --git a/src/com/hippo/ehviewer/activity/MangaDetailActivity.java b/src/com/hippo/ehviewer/activity/MangaDetailActivity.java index 149b85852..868c5460b 100644 --- a/src/com/hippo/ehviewer/activity/MangaDetailActivity.java +++ b/src/com/hippo/ehviewer/activity/MangaDetailActivity.java @@ -33,7 +33,7 @@ import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; import android.util.DisplayMetrics; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; diff --git a/src/com/hippo/ehviewer/activity/MangaDownloadActivity.java b/src/com/hippo/ehviewer/activity/MangaDownloadActivity.java index c82a8234e..e94a7b793 100644 --- a/src/com/hippo/ehviewer/activity/MangaDownloadActivity.java +++ b/src/com/hippo/ehviewer/activity/MangaDownloadActivity.java @@ -30,7 +30,7 @@ import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.v4.view.PagerAdapter; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; diff --git a/src/com/hippo/ehviewer/gallery/GalleryView.java b/src/com/hippo/ehviewer/gallery/GalleryView.java index cdde4459a..df923e465 100644 --- a/src/com/hippo/ehviewer/gallery/GalleryView.java +++ b/src/com/hippo/ehviewer/gallery/GalleryView.java @@ -6,7 +6,7 @@ import android.graphics.Color; import android.graphics.Movie; import android.graphics.Rect; import android.graphics.RectF; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.MotionEvent; import com.hippo.ehviewer.R; diff --git a/src/com/hippo/ehviewer/gallery/common/Utils.java b/src/com/hippo/ehviewer/gallery/common/Utils.java index 517dc408d..3fc4f0fc4 100644 --- a/src/com/hippo/ehviewer/gallery/common/Utils.java +++ b/src/com/hippo/ehviewer/gallery/common/Utils.java @@ -23,7 +23,7 @@ import android.database.Cursor; import android.os.Build; import android.os.ParcelFileDescriptor; import android.text.TextUtils; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import java.io.Closeable; import java.io.IOException; diff --git a/src/com/hippo/ehviewer/gallery/data/ImageSet.java b/src/com/hippo/ehviewer/gallery/data/ImageSet.java index 54098dffd..ae8a2d251 100644 --- a/src/com/hippo/ehviewer/gallery/data/ImageSet.java +++ b/src/com/hippo/ehviewer/gallery/data/ImageSet.java @@ -18,7 +18,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Movie; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.util.SparseArray; // TODO 初期建立一个线程检索文件夹中所有文件以寻找到所有图片文件 diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/BasicTexture.java b/src/com/hippo/ehviewer/gallery/glrenderer/BasicTexture.java index 5933d7b94..63cdf31a1 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/BasicTexture.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/BasicTexture.java @@ -17,7 +17,7 @@ package com.hippo.ehviewer.gallery.glrenderer; import android.graphics.RectF; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import com.hippo.ehviewer.gallery.common.Utils; diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/GLES11Canvas.java b/src/com/hippo/ehviewer/gallery/glrenderer/GLES11Canvas.java index 4e987a102..240e86d3d 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/GLES11Canvas.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/GLES11Canvas.java @@ -22,7 +22,7 @@ import android.graphics.RectF; import android.opengl.GLU; import android.opengl.GLUtils; import android.opengl.Matrix; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import com.hippo.ehviewer.gallery.common.Utils; import com.hippo.ehviewer.util.IntArray; diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/GLES20Canvas.java b/src/com/hippo/ehviewer/gallery/glrenderer/GLES20Canvas.java index fa711ecb1..a963bdb81 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/GLES20Canvas.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/GLES20Canvas.java @@ -21,7 +21,7 @@ import android.graphics.RectF; import android.opengl.GLES20; import android.opengl.GLUtils; import android.opengl.Matrix; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import com.hippo.ehviewer.util.IntArray; diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/MovieTexture.java b/src/com/hippo/ehviewer/gallery/glrenderer/MovieTexture.java index 96761a8f4..a8f8cd568 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/MovieTexture.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/MovieTexture.java @@ -4,7 +4,7 @@ import junit.framework.Assert; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Movie; -import android.util.Log; +import com.hippo.ehviewer.util.Log; public class MovieTexture extends CanvasTexture { private static final int DEFAULT_MOVIE_DURATION = 1000; diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/RawTexture.java b/src/com/hippo/ehviewer/gallery/glrenderer/RawTexture.java index 7a6495ef3..665df0a40 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/RawTexture.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/RawTexture.java @@ -16,7 +16,7 @@ package com.hippo.ehviewer.gallery.glrenderer; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import javax.microedition.khronos.opengles.GL11; diff --git a/src/com/hippo/ehviewer/gallery/glrenderer/UploadedTexture.java b/src/com/hippo/ehviewer/gallery/glrenderer/UploadedTexture.java index 661f13256..ba54e9d71 100644 --- a/src/com/hippo/ehviewer/gallery/glrenderer/UploadedTexture.java +++ b/src/com/hippo/ehviewer/gallery/glrenderer/UploadedTexture.java @@ -19,7 +19,7 @@ package com.hippo.ehviewer.gallery.glrenderer; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.opengl.GLUtils; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import junit.framework.Assert; import java.util.HashMap; diff --git a/src/com/hippo/ehviewer/gallery/ui/GLRootView.java b/src/com/hippo/ehviewer/gallery/ui/GLRootView.java index 716cae4b6..52c332d36 100644 --- a/src/com/hippo/ehviewer/gallery/ui/GLRootView.java +++ b/src/com/hippo/ehviewer/gallery/ui/GLRootView.java @@ -26,7 +26,7 @@ import android.os.Build; import android.os.Process; import android.os.SystemClock; import android.util.AttributeSet; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; diff --git a/src/com/hippo/ehviewer/gallery/ui/GLView.java b/src/com/hippo/ehviewer/gallery/ui/GLView.java index 8c23bc95d..ae8fc8267 100644 --- a/src/com/hippo/ehviewer/gallery/ui/GLView.java +++ b/src/com/hippo/ehviewer/gallery/ui/GLView.java @@ -19,7 +19,7 @@ package com.hippo.ehviewer.gallery.ui; import android.annotation.SuppressLint; import android.graphics.Rect; import android.os.SystemClock; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.MotionEvent; import com.hippo.ehviewer.gallery.anim.CanvasAnimation; diff --git a/src/com/hippo/ehviewer/gallery/util/GalleryUtils.java b/src/com/hippo/ehviewer/gallery/util/GalleryUtils.java index 6e580a42a..0b560593b 100644 --- a/src/com/hippo/ehviewer/gallery/util/GalleryUtils.java +++ b/src/com/hippo/ehviewer/gallery/util/GalleryUtils.java @@ -27,7 +27,7 @@ import android.os.Environment; import android.os.StatFs; import android.provider.MediaStore; import android.util.DisplayMetrics; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.WindowManager; import com.hippo.ehviewer.util.ThreadPool.CancelListener; diff --git a/src/com/hippo/ehviewer/network/Downloader.java b/src/com/hippo/ehviewer/network/Downloader.java index 98ddfa488..87b476121 100644 --- a/src/com/hippo/ehviewer/network/Downloader.java +++ b/src/com/hippo/ehviewer/network/Downloader.java @@ -15,7 +15,7 @@ import java.net.UnknownHostException; import org.apache.http.conn.ConnectTimeoutException; import android.content.Context; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import com.hippo.ehviewer.R; import com.hippo.ehviewer.exception.FileException; diff --git a/src/com/hippo/ehviewer/network/HttpHelper.java b/src/com/hippo/ehviewer/network/HttpHelper.java index 435f6a26f..68f07c772 100644 --- a/src/com/hippo/ehviewer/network/HttpHelper.java +++ b/src/com/hippo/ehviewer/network/HttpHelper.java @@ -27,7 +27,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.support.v4.util.LruCache; -import android.util.Log; +import com.hippo.ehviewer.util.Log; /** * It is not thread safe diff --git a/src/com/hippo/ehviewer/service/DownloadService.java b/src/com/hippo/ehviewer/service/DownloadService.java index 1b3db318f..137d6cf0b 100644 --- a/src/com/hippo/ehviewer/service/DownloadService.java +++ b/src/com/hippo/ehviewer/service/DownloadService.java @@ -19,7 +19,7 @@ import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.support.v4.app.NotificationCompat; -import android.util.Log; +import com.hippo.ehviewer.util.Log; public class DownloadService extends Service { private static final int DOWNLOAD_NOTIFY_ID = -1; diff --git a/src/com/hippo/ehviewer/util/Cache.java b/src/com/hippo/ehviewer/util/Cache.java index ac8dcda06..8ade04ead 100644 --- a/src/com/hippo/ehviewer/util/Cache.java +++ b/src/com/hippo/ehviewer/util/Cache.java @@ -6,7 +6,7 @@ import android.graphics.Bitmap; import android.os.Build; import android.os.Environment; import android.support.v4.util.LruCache; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import com.hippo.ehviewer.DiskCache; import com.hippo.ehviewer.MangaDetail; diff --git a/src/com/hippo/ehviewer/util/Crash.java b/src/com/hippo/ehviewer/util/Crash.java index 74d314523..4ffa7ee91 100644 --- a/src/com/hippo/ehviewer/util/Crash.java +++ b/src/com/hippo/ehviewer/util/Crash.java @@ -1,196 +1,196 @@ -package com.hippo.ehviewer.util; - -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -public class Crash { - private static final String TAG = "Crash"; - - private static Context mContext; - - private static String externalCrashFilePath = "/EhViewer/crash"; - private static String internalCrashFilePath = "/crash"; - - private static final String NEW_CRAHS = "new_crash"; - private static final String LAST_CRASH_POSITION = "last_crash_position"; - private static final String LAST_CRASH_NAME = "last_crash_name"; - - private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); - - private static boolean mInit = false; - - /** - * Init Crash - * - * @param context Application context - */ - public static void init(Context context) { - if (mInit) - return; - mInit = true; - - mContext = context; - } - - /** - * Is init - * @return True if init - */ - public static boolean isInit() { - return mInit; - } - - /** - * Save throwable infomation to file - * - * @param ex The throwable to store - */ - public static void saveCrashInfo2File(Throwable ex) { - StringBuffer sb = new StringBuffer(); - collectDeviceInfo(sb); - - Writer writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - ex.printStackTrace(printWriter); - Throwable cause = ex.getCause(); - while (cause != null) { - cause.printStackTrace(printWriter); - cause = cause.getCause(); - } - printWriter.close(); - String result = writer.toString(); - Log.e(TAG, result); - sb.append("======== CrashInfo ========\n"); - sb.append(result); - try { - long timestamp = System.currentTimeMillis(); - String time = formatter.format(new Date()); - String fileName = "crash-" + time + "-" + timestamp + ".log"; - String path = null; - boolean position = false; - if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - path = Environment.getExternalStorageDirectory() + externalCrashFilePath; - position = true; - } - else { - path = mContext.getFilesDir() + internalCrashFilePath; - position = false; - } - File dir = new File(path); - if (!dir.exists()) { - dir.mkdirs(); - } - FileOutputStream fos = new FileOutputStream(new File(path, fileName)); - fos.write(sb.toString().getBytes()); - fos.close(); - // Record info for next time upload - SharedPreferences configPre = mContext.getSharedPreferences("config", 0); - SharedPreferences.Editor editor = configPre.edit(); - editor.putBoolean(NEW_CRAHS, true); - editor.putBoolean(LAST_CRASH_POSITION, position); - editor.putString(LAST_CRASH_NAME, fileName); - editor.commit(); - } catch (Exception e) { - Log.e(TAG, "An error occured while writing crash file...", e); - } - } - - private static void collectDeviceInfo(StringBuffer sb) { - try { - PackageManager pm = mContext.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); - if (pi != null) { - String versionName = pi.versionName == null ? "null" : pi.versionName; - String versionCode = String.valueOf(pi.versionCode); - sb.append("======== PackageInfo ========\n"); - sb.append("PackageName=" + pi.packageName + "\n"); - sb.append("VersionName=" + versionName + "\n"); - sb.append("VersionCode=" + versionCode + "\n"); - sb.append("\n"); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "an error occured when collect package info", e); - } - sb.append("======== DeviceInfo ========\n"); - sb.append("BOARD=" + Build.BOARD + "\n"); - sb.append("BOOTLOADER=" + Build.BOOTLOADER + "\n"); - sb.append("CPU_ABI=" + Build.CPU_ABI + "\n"); - sb.append("CPU_ABI2=" + Build.CPU_ABI2 + "\n"); - sb.append("DEVICE=" + Build.DEVICE + "\n"); - sb.append("DISPLAY=" + Build.DISPLAY + "\n"); - sb.append("FINGERPRINT=" + Build.FINGERPRINT + "\n"); - sb.append("HARDWARE=" + Build.HARDWARE + "\n"); - sb.append("HOST=" + Build.HOST + "\n"); - sb.append("ID=" + Build.ID + "\n"); - sb.append("MANUFACTURER=" + Build.MANUFACTURER + "\n"); - sb.append("MODEL=" + Build.MODEL + "\n"); - sb.append("PRODUCT=" + Build.PRODUCT + "\n"); - sb.append("RADIO=" + Build.getRadioVersion() + "\n"); - sb.append("SERIAL=" + Build.SERIAL + "\n"); - sb.append("TAGS=" + Build.TAGS + "\n"); - sb.append("TYPE=" + Build.TYPE + "\n"); - sb.append("USER=" + Build.USER + "\n"); - sb.append("CODENAME=" + Build.VERSION.CODENAME + "\n"); - sb.append("INCREMENTAL=" + Build.VERSION.INCREMENTAL + "\n"); - sb.append("RELEASE=" + Build.VERSION.RELEASE + "\n"); - sb.append("SDK=" + Build.VERSION.SDK_INT + "\n"); - sb.append("\n"); - } - - /** - * Check is there last crash - * - * @return Return the crash String or null if there is no - */ - public static String getLastCrash() { - SharedPreferences configPre = mContext.getSharedPreferences("config", 0); - if (!configPre.getBoolean(NEW_CRAHS, false)) - return null; - - boolean position = configPre.getBoolean(LAST_CRASH_POSITION, false); - String lastCrashName; - if ((lastCrashName = configPre.getString(LAST_CRASH_NAME, null)) == null) - return null; - - File lastCrashFile; - if (position) { - if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) - return null; - lastCrashFile = new File(Environment.getExternalStorageDirectory() + externalCrashFilePath, lastCrashName); - } - else { - lastCrashFile = new File(mContext.getFilesDir() + internalCrashFilePath, lastCrashName); - } - if (!lastCrashFile.isFile()) - return null; - - try { - byte[] buffer = new byte[(int) lastCrashFile.length()]; - DataInputStream din = new DataInputStream(new FileInputStream(lastCrashFile)); - din.readFully(buffer); - din.close(); - configPre.edit().putBoolean(NEW_CRAHS, false).commit(); - return new String(buffer); - } catch (Exception e) { - Log.e(TAG, "An error occured while reading crash file...", e); - } - return null; - } -} +package com.hippo.ehviewer.util; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; +import android.os.Environment; +import com.hippo.ehviewer.util.Log; + +public class Crash { + private static final String TAG = "Crash"; + + private static Context mContext; + + private static String externalCrashFilePath = "/EhViewer/crash"; + private static String internalCrashFilePath = "/crash"; + + private static final String NEW_CRAHS = "new_crash"; + private static final String LAST_CRASH_POSITION = "last_crash_position"; + private static final String LAST_CRASH_NAME = "last_crash_name"; + + private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + + private static boolean mInit = false; + + /** + * Init Crash + * + * @param context Application context + */ + public static void init(Context context) { + if (mInit) + return; + mInit = true; + + mContext = context; + } + + /** + * Is init + * @return True if init + */ + public static boolean isInit() { + return mInit; + } + + /** + * Save throwable infomation to file + * + * @param ex The throwable to store + */ + public static void saveCrashInfo2File(Throwable ex) { + StringBuffer sb = new StringBuffer(); + collectDeviceInfo(sb); + + Writer writer = new StringWriter(); + PrintWriter printWriter = new PrintWriter(writer); + ex.printStackTrace(printWriter); + Throwable cause = ex.getCause(); + while (cause != null) { + cause.printStackTrace(printWriter); + cause = cause.getCause(); + } + printWriter.close(); + String result = writer.toString(); + Log.e(TAG, result); + sb.append("======== CrashInfo ========\n"); + sb.append(result); + try { + long timestamp = System.currentTimeMillis(); + String time = formatter.format(new Date()); + String fileName = "crash-" + time + "-" + timestamp + ".log"; + String path = null; + boolean position = false; + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + path = Environment.getExternalStorageDirectory() + externalCrashFilePath; + position = true; + } + else { + path = mContext.getFilesDir() + internalCrashFilePath; + position = false; + } + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(new File(path, fileName)); + fos.write(sb.toString().getBytes()); + fos.close(); + // Record info for next time upload + SharedPreferences configPre = mContext.getSharedPreferences("config", 0); + SharedPreferences.Editor editor = configPre.edit(); + editor.putBoolean(NEW_CRAHS, true); + editor.putBoolean(LAST_CRASH_POSITION, position); + editor.putString(LAST_CRASH_NAME, fileName); + editor.commit(); + } catch (Exception e) { + Log.e(TAG, "An error occured while writing crash file...", e); + } + } + + private static void collectDeviceInfo(StringBuffer sb) { + try { + PackageManager pm = mContext.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES); + if (pi != null) { + String versionName = pi.versionName == null ? "null" : pi.versionName; + String versionCode = String.valueOf(pi.versionCode); + sb.append("======== PackageInfo ========\n"); + sb.append("PackageName=" + pi.packageName + "\n"); + sb.append("VersionName=" + versionName + "\n"); + sb.append("VersionCode=" + versionCode + "\n"); + sb.append("\n"); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "an error occured when collect package info", e); + } + sb.append("======== DeviceInfo ========\n"); + sb.append("BOARD=" + Build.BOARD + "\n"); + sb.append("BOOTLOADER=" + Build.BOOTLOADER + "\n"); + sb.append("CPU_ABI=" + Build.CPU_ABI + "\n"); + sb.append("CPU_ABI2=" + Build.CPU_ABI2 + "\n"); + sb.append("DEVICE=" + Build.DEVICE + "\n"); + sb.append("DISPLAY=" + Build.DISPLAY + "\n"); + sb.append("FINGERPRINT=" + Build.FINGERPRINT + "\n"); + sb.append("HARDWARE=" + Build.HARDWARE + "\n"); + sb.append("HOST=" + Build.HOST + "\n"); + sb.append("ID=" + Build.ID + "\n"); + sb.append("MANUFACTURER=" + Build.MANUFACTURER + "\n"); + sb.append("MODEL=" + Build.MODEL + "\n"); + sb.append("PRODUCT=" + Build.PRODUCT + "\n"); + sb.append("RADIO=" + Build.getRadioVersion() + "\n"); + sb.append("SERIAL=" + Build.SERIAL + "\n"); + sb.append("TAGS=" + Build.TAGS + "\n"); + sb.append("TYPE=" + Build.TYPE + "\n"); + sb.append("USER=" + Build.USER + "\n"); + sb.append("CODENAME=" + Build.VERSION.CODENAME + "\n"); + sb.append("INCREMENTAL=" + Build.VERSION.INCREMENTAL + "\n"); + sb.append("RELEASE=" + Build.VERSION.RELEASE + "\n"); + sb.append("SDK=" + Build.VERSION.SDK_INT + "\n"); + sb.append("\n"); + } + + /** + * Check is there last crash + * + * @return Return the crash String or null if there is no + */ + public static String getLastCrash() { + SharedPreferences configPre = mContext.getSharedPreferences("config", 0); + if (!configPre.getBoolean(NEW_CRAHS, false)) + return null; + + boolean position = configPre.getBoolean(LAST_CRASH_POSITION, false); + String lastCrashName; + if ((lastCrashName = configPre.getString(LAST_CRASH_NAME, null)) == null) + return null; + + File lastCrashFile; + if (position) { + if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) + return null; + lastCrashFile = new File(Environment.getExternalStorageDirectory() + externalCrashFilePath, lastCrashName); + } + else { + lastCrashFile = new File(mContext.getFilesDir() + internalCrashFilePath, lastCrashName); + } + if (!lastCrashFile.isFile()) + return null; + + try { + byte[] buffer = new byte[(int) lastCrashFile.length()]; + DataInputStream din = new DataInputStream(new FileInputStream(lastCrashFile)); + din.readFully(buffer); + din.close(); + configPre.edit().putBoolean(NEW_CRAHS, false).commit(); + return new String(buffer); + } catch (Exception e) { + Log.e(TAG, "An error occured while reading crash file...", e); + } + return null; + } +} diff --git a/src/com/hippo/ehviewer/util/EhClient.java b/src/com/hippo/ehviewer/util/EhClient.java index a779cd859..eb267b49d 100644 --- a/src/com/hippo/ehviewer/util/EhClient.java +++ b/src/com/hippo/ehviewer/util/EhClient.java @@ -43,7 +43,7 @@ import android.graphics.Movie; import android.os.Handler; import android.os.Message; import android.support.v4.util.LruCache; -import android.util.Log; +import com.hippo.ehviewer.util.Log; // TODO Stringbuffer to StringBuild diff --git a/src/com/hippo/ehviewer/util/Log.java b/src/com/hippo/ehviewer/util/Log.java new file mode 100644 index 000000000..cb0c0b763 --- /dev/null +++ b/src/com/hippo/ehviewer/util/Log.java @@ -0,0 +1,59 @@ +package com.hippo.ehviewer.util; + +public class Log { + private static final String NULL = "null"; + + public static int v(String tag, String msg) { + if (msg == null) + msg = NULL; + return android.util.Log.v(tag, msg); + } + public static int v(String tag, String msg, Throwable tr) { + if (msg == null) + msg = NULL; + return android.util.Log.v(tag, msg, tr); + } + public static int d(String tag, String msg) { + if (msg == null) + msg = NULL; + return android.util.Log.d(tag, msg); + } + public static int d(String tag, String msg, Throwable tr) { + if (msg == null) + msg = NULL; + return android.util.Log.d(tag, msg, tr); + } + public static int i(String tag, String msg) { + if (msg == null) + msg = NULL; + return android.util.Log.i(tag, msg); + } + public static int i(String tag, String msg, Throwable tr) { + if (msg == null) + msg = NULL; + return android.util.Log.i(tag, msg, tr); + } + public static int w(String tag, String msg) { + if (msg == null) + msg = NULL; + return android.util.Log.w(tag, msg); + } + public static int w(String tag, String msg, Throwable tr) { + if (msg == null) + msg = NULL; + return android.util.Log.w(tag, msg, tr); + } + public static int w(String tag, Throwable tr) { + return android.util.Log.w(tag, tr); + } + public static int e(String tag, String msg) { + if (msg == null) + msg = NULL; + return android.util.Log.e(tag, msg); + } + public static int e(String tag, String msg, Throwable tr) { + if (msg == null) + msg = NULL; + return android.util.Log.e(tag, msg, tr); + } +} diff --git a/src/com/hippo/ehviewer/util/ThreadPool.java b/src/com/hippo/ehviewer/util/ThreadPool.java index cd18591e5..0f1b41d9e 100644 --- a/src/com/hippo/ehviewer/util/ThreadPool.java +++ b/src/com/hippo/ehviewer/util/ThreadPool.java @@ -16,7 +16,7 @@ package com.hippo.ehviewer.util; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; diff --git a/src/com/hippo/ehviewer/view/GetPaddingRelativeLayout.java b/src/com/hippo/ehviewer/view/GetPaddingRelativeLayout.java index 888a0703c..24827c978 100644 --- a/src/com/hippo/ehviewer/view/GetPaddingRelativeLayout.java +++ b/src/com/hippo/ehviewer/view/GetPaddingRelativeLayout.java @@ -3,7 +3,7 @@ package com.hippo.ehviewer.view; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.View; import android.widget.RelativeLayout; diff --git a/src/com/hippo/ehviewer/view/OlImageView.java b/src/com/hippo/ehviewer/view/OlImageView.java index e949334f1..bc932a2ff 100644 --- a/src/com/hippo/ehviewer/view/OlImageView.java +++ b/src/com/hippo/ehviewer/view/OlImageView.java @@ -9,7 +9,7 @@ import android.graphics.Bitmap; import android.graphics.Movie; import android.support.v4.util.LruCache; import android.util.AttributeSet; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.View; public class OlImageView extends SuperImageView { diff --git a/src/com/hippo/ehviewer/view/SuperImageView.java b/src/com/hippo/ehviewer/view/SuperImageView.java index 02d9de04a..1da34bdb4 100644 --- a/src/com/hippo/ehviewer/view/SuperImageView.java +++ b/src/com/hippo/ehviewer/view/SuperImageView.java @@ -1,1509 +1,1509 @@ -package com.hippo.ehviewer.view; - -import android.annotation.SuppressLint; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Matrix; -import android.graphics.Movie; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.RectF; -import android.graphics.Xfermode; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.view.ViewDebug; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.RemoteViews.RemoteView; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; - -import com.hippo.ehviewer.R; - -// TODO use native to improve gif - -/** - * Displays an arbitrary image, such as an icon. The ImageView class can load - * images from various sources (such as resources or content providers), takes - * care of computing its measurement from the image so that it can be used in - * any layout manager, and provides various display options such as scaling and - * tinting. - * - * @attr ref android.R.styleable#ImageView_adjustViewBounds - * @attr ref android.R.styleable#ImageView_src - * @attr ref android.R.styleable#ImageView_maxWidth - * @attr ref android.R.styleable#ImageView_maxHeight - * @attr ref android.R.styleable#ImageView_tint - * @attr ref android.R.styleable#ImageView_scaleType - * @attr ref android.R.styleable#ImageView_cropToPadding - */ -@RemoteView -public class SuperImageView extends View { - private static String TAG = "SuperImageView"; - // for gif - private Movie mMovie = null; - // Not 0 mean the same timer - private long mMovieStart = 1; - private int mCurrentAnimationTime = 0; - - private static final int DEFAULT_MOVIEW_DURATION = 1000; - - // settable by the client - private Uri mUri; - private int mResource = 0; - private Matrix mMatrix; - private ScaleType mScaleType; - private boolean mHaveFrame = false; - private boolean mAdjustViewBounds = false; - private int mMaxWidth = Integer.MAX_VALUE; - private int mMaxHeight = Integer.MAX_VALUE; - - // these are applied to the drawable - private ColorFilter mColorFilter; - private Xfermode mXfermode; - private int mAlpha = 255; - private int mViewAlphaScale = 256; - private boolean mColorMod = false; - - private Drawable mDrawable = null; - private int[] mState = null; - private boolean mMergeState = false; - private int mLevel = 0; - private int mDrawableWidth; - private int mDrawableHeight; - private Matrix mDrawMatrix = null; - - // Avoid allocations... - private RectF mTempSrc = new RectF(); - private RectF mTempDst = new RectF(); - - private boolean mCropToPadding; - - private int mBaseline = -1; - private boolean mBaselineAlignBottom = false; - - // AdjustViewBounds behavior will be in compatibility mode for older apps. - private boolean mAdjustViewBoundsCompat = false; - - private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, - ScaleType.FIT_XY, ScaleType.FIT_START, ScaleType.FIT_CENTER, - ScaleType.FIT_END, ScaleType.CENTER, ScaleType.CENTER_CROP, - ScaleType.CENTER_INSIDE }; - - public SuperImageView(Context context) { - super(context); - initImageView(); - } - - public SuperImageView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SuperImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initImageView(); - - TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.SuperImageView, defStyle, 0); - - mBaselineAlignBottom = a.getBoolean( - R.styleable.SuperImageView_baselineAlignBottom, false); - - mBaseline = a.getDimensionPixelSize( - R.styleable.SuperImageView_baseline, -1); - - setAdjustViewBounds(a.getBoolean( - R.styleable.SuperImageView_adjustViewBounds, false)); - - setMaxWidth(a.getDimensionPixelSize( - R.styleable.SuperImageView_maxWidth, Integer.MAX_VALUE)); - - setMaxHeight(a.getDimensionPixelSize( - R.styleable.SuperImageView_maxHeight, Integer.MAX_VALUE)); - - int index = a.getInt(R.styleable.SuperImageView_scaleType, -1); - if (index >= 0) { - setScaleType(sScaleTypeArray[index]); - } - - int tint = a.getInt(R.styleable.SuperImageView_tint, 0); - if (tint != 0) { - setColorFilter(tint); - } - - int alpha = a.getInt(R.styleable.SuperImageView_drawableAlpha, 255); - if (alpha != 255) { - setAlpha(alpha); - } - - mCropToPadding = a.getBoolean(R.styleable.SuperImageView_cropToPadding, - false); - - int resId = a.getResourceId(R.styleable.SuperImageView_src, -1); - if (resId == -1) { - Drawable d = a.getDrawable(R.styleable.SuperImageView_src); - if (d != null) { - setImageDrawable(d); - } - } else - this.setImageResource(resId); - - a.recycle(); - - // need inflate syntax/reader for matrix - } - - private void initImageView() { - mMatrix = new Matrix(); - mScaleType = ScaleType.FIT_CENTER; - mAdjustViewBoundsCompat = getContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; - } - - @Override - protected boolean verifyDrawable(Drawable dr) { - return mDrawable == dr || super.verifyDrawable(dr); - } - - protected boolean verifyMovie(Movie mv) { - return mMovie == mv; - } - - @SuppressLint("NewApi") - @Override - public void jumpDrawablesToCurrentState() { - if (Build.VERSION.SDK_INT < 11) { - return; - } - - super.jumpDrawablesToCurrentState(); - if (mDrawable != null) - mDrawable.jumpToCurrentState(); - } - - @Override - public void invalidateDrawable(Drawable dr) { - if (dr == mDrawable) { - /* - * we invalidate the whole view in this case because it's very hard - * to know where the drawable actually is. This is made complicated - * because of the offsets and transformations that can be applied. - * In theory we could get the drawable's bounds and run them through - * the transformation and offsets, but this is probably not worth - * the effort. - */ - invalidate(); - } else { - super.invalidateDrawable(dr); - } - } - - public void invalidateMovie(Movie mv) { - if (mv == mMovie) { - /* - * we invalidate the whole view in this case because it's very hard - * to know where the drawable actually is. This is made complicated - * because of the offsets and transformations that can be applied. - * In theory we could get the drawable's bounds and run them through - * the transformation and offsets, but this is probably not worth - * the effort. - */ - invalidate(); - } - } - - @Override - public boolean hasOverlappingRendering() { - return (getBackground() != null && getBackground().getCurrent() != null); - } - - @SuppressLint("NewApi") - @Override - public void onPopulateAccessibilityEvent(AccessibilityEvent event) { - if (Build.VERSION.SDK_INT < 14) { - return; - } - - super.onPopulateAccessibilityEvent(event); - CharSequence contentDescription = getContentDescription(); - if (!TextUtils.isEmpty(contentDescription)) { - event.getText().add(contentDescription); - } - } - - /** - * True when ImageView is adjusting its bounds to preserve the aspect ratio - * of its drawable - * - * @return whether to adjust the bounds of this view to presrve the original - * aspect ratio of the drawable - * - * @see #setAdjustViewBounds(boolean) - * - * @attr ref android.R.styleable#ImageView_adjustViewBounds - */ - public boolean getAdjustViewBounds() { - return mAdjustViewBounds; - } - - /** - * Set this to true if you want the ImageView to adjust its bounds to - * preserve the aspect ratio of its drawable. - * - *

- * Note: If the application targets API level 17 or lower, - * adjustViewBounds will allow the drawable to shrink the view bounds, but - * not grow to fill available measured space in all cases. This is for - * compatibility with legacy {@link android.view.View.MeasureSpec - * MeasureSpec} and {@link android.widget.RelativeLayout RelativeLayout} - * behavior. - *

- * - * @param adjustViewBounds - * Whether to adjust the bounds of this view to preserve the - * original aspect ratio of the drawable. - * - * @see #getAdjustViewBounds() - * - * @attr ref android.R.styleable#ImageView_adjustViewBounds - */ - public void setAdjustViewBounds(boolean adjustViewBounds) { - mAdjustViewBounds = adjustViewBounds; - if (adjustViewBounds) { - setScaleType(ScaleType.FIT_CENTER); - } - } - - /** - * The maximum width of this view. - * - * @return The maximum width of this view - * - * @see #setMaxWidth(int) - * - * @attr ref android.R.styleable#ImageView_maxWidth - */ - public int getMaxWidth() { - return mMaxWidth; - } - - /** - * An optional argument to supply a maximum width for this view. Only valid - * if {@link #setAdjustViewBounds(boolean)} has been set to true. To set an - * image to be a maximum of 100 x 100 while preserving the original aspect - * ratio, do the following: 1) set adjustViewBounds to true 2) set maxWidth - * and maxHeight to 100 3) set the height and width layout params to - * WRAP_CONTENT. - * - *

- * Note that this view could be still smaller than 100 x 100 using this - * approach if the original image is small. To set an image to a fixed size, - * specify that size in the layout params and then use - * {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine - * how to fit the image within the bounds. - *

- * - * @param maxWidth - * maximum width for this view - * - * @see #getMaxWidth() - * - * @attr ref android.R.styleable#ImageView_maxWidth - */ - public void setMaxWidth(int maxWidth) { - mMaxWidth = maxWidth; - } - - /** - * The maximum height of this view. - * - * @return The maximum height of this view - * - * @see #setMaxHeight(int) - * - * @attr ref android.R.styleable#ImageView_maxHeight - */ - public int getMaxHeight() { - return mMaxHeight; - } - - /** - * An optional argument to supply a maximum height for this view. Only valid - * if {@link #setAdjustViewBounds(boolean)} has been set to true. To set an - * image to be a maximum of 100 x 100 while preserving the original aspect - * ratio, do the following: 1) set adjustViewBounds to true 2) set maxWidth - * and maxHeight to 100 3) set the height and width layout params to - * WRAP_CONTENT. - * - *

- * Note that this view could be still smaller than 100 x 100 using this - * approach if the original image is small. To set an image to a fixed size, - * specify that size in the layout params and then use - * {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine - * how to fit the image within the bounds. - *

- * - * @param maxHeight - * maximum height for this view - * - * @see #getMaxHeight() - * - * @attr ref android.R.styleable#ImageView_maxHeight - */ - public void setMaxHeight(int maxHeight) { - mMaxHeight = maxHeight; - } - - /** - * Return the view's drawable, or null if no drawable has been assigned. - */ - public Drawable getDrawable() { - return mDrawable; - } - - public int getResourceWidth() { - return mDrawableWidth; - } - - public int getResourceHeight() { - return mDrawableHeight; - } - - public Movie getMovie() { - return mMovie; - } - - public boolean isEmpty() { - if (mMovie == null && mDrawable == null) - return true; - else - return false; - } - - /** - * Sets a drawable as the content of this ImageView. - * - *

- * This does Bitmap reading and decoding on the UI thread, which can cause a - * latency hiccup. If that's a concern, consider using - * {@link #setImageDrawable(android.graphics.drawable.Drawable)} or - * {@link #setImageBitmap(android.graphics.Bitmap)} and - * {@link android.graphics.BitmapFactory} instead. - *

- * - * @param resId - * the resource identifier of the drawable - * - * @attr ref android.R.styleable#ImageView_src - */ - public void setImageResource(int resId) { - if (mUri != null || mResource != resId) { - updateDrawable(null); - updateMovie(null); - mResource = resId; - mUri = null; - - final int oldWidth = mDrawableWidth; - final int oldHeight = mDrawableHeight; - - resolveUri(); - - if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { - requestLayout(); - } - invalidate(); - } - } - - /** - * Sets the content of this ImageView to the specified Uri. - * - *

- * This does Bitmap reading and decoding on the UI thread, which can cause a - * latency hiccup. If that's a concern, consider using - * {@link #setImageDrawable(android.graphics.drawable.Drawable)} or - * {@link #setImageBitmap(android.graphics.Bitmap)} and - * {@link android.graphics.BitmapFactory} instead. - *

- * - * @param uri - * The Uri of an image - */ - public void setImageURI(Uri uri) { - if (mResource != 0 - || (mUri != uri && (uri == null || mUri == null || !uri - .equals(mUri)))) { - updateDrawable(null); - updateMovie(null); - mResource = 0; - mUri = uri; - - final int oldWidth = mDrawableWidth; - final int oldHeight = mDrawableHeight; - - resolveUri(); - - if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { - requestLayout(); - } - invalidate(); - } - } - - /** - * Sets a drawable as the content of this ImageView. - * - * @param drawable - * The drawable to set - */ - public void setImageDrawable(Drawable drawable) { - if (mDrawable != drawable) { - mResource = 0; - mUri = null; - mMovie = null; - - final int oldWidth = mDrawableWidth; - final int oldHeight = mDrawableHeight; - - updateDrawable(drawable); - updateMovie(null); - - if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { - requestLayout(); - } - invalidate(); - } - } - - /** - * Sets a Movie as the content of this SuperImageView. - * - * @param mv - * The movie to set - */ - public void setImageMovie(Movie mv) { - if (mMovie != mv) { - mResource = 0; - mUri = null; - mDrawable = null; - - final int oldWidth = mDrawableWidth; - final int oldHeight = mDrawableHeight; - - updateDrawable(null); - updateMovie(mv); - - if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { - requestLayout(); - } - invalidate(); - } - } - - /** - * Sets a InputStream as the content of this SuperImageView. - * - * @param is - * The InputStream to set - */ - public void setImageInputStream(InputStream is) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int len; - while ((len = is.read(buffer)) > -1) { - baos.write(buffer, 0, len); - } - is.close(); - baos.flush(); - - byte[] bytes = baos.toByteArray(); - Movie m = Movie.decodeByteArray(bytes, 0, bytes.length); - if (m == null) { - Bitmap b = BitmapFactory - .decodeByteArray(bytes, 0, bytes.length); - setImageBitmap(b); - } else - setImageMovie(m); - - baos.close(); - } catch (IOException e) { - Log.e(TAG, "Set Image from InputStream error !"); - } - } - - /** - * Sets a Bitmap as the content of this ImageView. - * - * @param bm - * The bitmap to set - */ - public void setImageBitmap(Bitmap bm) { - // if this is used frequently, may handle bitmaps explicitly - // to reduce the intermediate drawable object - setImageDrawable(new BitmapDrawable(getContext().getResources(), bm)); - } - - public void setImageState(int[] state, boolean merge) { - mState = state; - mMergeState = merge; - if (mDrawable != null) { - refreshDrawableState(); - resizeFromResource(); - } - } - - @Override - public void setSelected(boolean selected) { - super.setSelected(selected); - resizeFromResource(); - } - - /** - * Sets the image level, when it is constructed from a - * {@link android.graphics.drawable.LevelListDrawable}. - * - * @param level - * The new level for the image. - */ - public void setImageLevel(int level) { - mLevel = level; - if (mDrawable != null) { - mDrawable.setLevel(level); - resizeFromResource(); - } - } - - /** - * Options for scaling the bounds of an image to the bounds of this view. - */ - public enum ScaleType { - /** - * Scale using the image matrix when drawing. The image matrix can be - * set using {@link SuperImageView#setImageMatrix(Matrix)}. From XML, - * use this syntax: android:scaleType="matrix". - */ - MATRIX(0), - /** - * Scale the image using {@link Matrix.ScaleToFit#FILL}. From XML, use - * this syntax: android:scaleType="fitXY". - */ - FIT_XY(1), - /** - * Scale the image using {@link Matrix.ScaleToFit#START}. From XML, use - * this syntax: android:scaleType="fitStart". - */ - FIT_START(2), - /** - * Scale the image using {@link Matrix.ScaleToFit#CENTER}. From XML, use - * this syntax: android:scaleType="fitCenter". - */ - FIT_CENTER(3), - /** - * Scale the image using {@link Matrix.ScaleToFit#END}. From XML, use - * this syntax: android:scaleType="fitEnd". - */ - FIT_END(4), - /** - * Center the image in the view, but perform no scaling. From XML, use - * this syntax: android:scaleType="center". - */ - CENTER(5), - /** - * Scale the image uniformly (maintain the image's aspect ratio) so that - * both dimensions (width and height) of the image will be equal to or - * larger than the corresponding dimension of the view (minus padding). - * The image is then centered in the view. From XML, use this syntax: - * android:scaleType="centerCrop". - */ - CENTER_CROP(6), - /** - * Scale the image uniformly (maintain the image's aspect ratio) so that - * both dimensions (width and height) of the image will be equal to or - * less than the corresponding dimension of the view (minus padding). - * The image is then centered in the view. From XML, use this syntax: - * android:scaleType="centerInside". - */ - CENTER_INSIDE(7); - - ScaleType(int ni) { - nativeInt = ni; - } - - final int nativeInt; - } - - /** - * Controls how the image should be resized or moved to match the size of - * this ImageView. - * - * @param scaleType - * The desired scaling mode. - * - * @attr ref android.R.styleable#ImageView_scaleType - */ - public void setScaleType(ScaleType scaleType) { - if (scaleType == null) { - throw new NullPointerException(); - } - - if (mScaleType != scaleType) { - mScaleType = scaleType; - - setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); - - requestLayout(); - invalidate(); - } - } - - /** - * Return the current scale type in use by this ImageView. - * - * @see SuperImageView.ScaleType - * - * @attr ref android.R.styleable#ImageView_scaleType - */ - public ScaleType getScaleType() { - return mScaleType; - } - - /** - * Return the view's optional matrix. This is applied to the view's drawable - * when it is drawn. If there is not matrix, this method will return an - * identity matrix. Do not change this matrix in place but make a copy. If - * you want a different matrix applied to the drawable, be sure to call - * setImageMatrix(). - */ - public Matrix getImageMatrix() { - if (mDrawMatrix == null) { - // return new Matrix(Matrix.IDENTITY_MATRIX); //TODO Fix here - return new Matrix(); - } - return mDrawMatrix; - } - - public void setImageMatrix(Matrix matrix) { - // collaps null and identity to just null - if (matrix != null && matrix.isIdentity()) { - matrix = null; - } - - // don't invalidate unless we're actually changing our matrix - if (matrix == null && !mMatrix.isIdentity() || matrix != null - && !mMatrix.equals(matrix)) { - mMatrix.set(matrix); - configureBounds(); - invalidate(); - } - } - - /** - * Return whether this ImageView crops to padding. - * - * @return whether this ImageView crops to padding - * - * @see #setCropToPadding(boolean) - * - * @attr ref android.R.styleable#ImageView_cropToPadding - */ - public boolean getCropToPadding() { - return mCropToPadding; - } - - /** - * Sets whether this ImageView will crop to padding. - * - * @param cropToPadding - * whether this ImageView will crop to padding - * - * @see #getCropToPadding() - * - * @attr ref android.R.styleable#ImageView_cropToPadding - */ - public void setCropToPadding(boolean cropToPadding) { - if (mCropToPadding != cropToPadding) { - mCropToPadding = cropToPadding; - requestLayout(); - invalidate(); - } - } - - private void resolveUri() { - if (mDrawable != null && mMovie != null) { - return; - } - - Resources rsrc = getResources(); - if (rsrc == null) { - return; - } - - Drawable d = null; - Movie m = null; - - if (mResource != 0) { - try { - InputStream is = rsrc.openRawResource(mResource); - m = Movie.decodeStream(is); - if (m == null) { - d = rsrc.getDrawable(mResource); - } - } catch (Exception e) { - Log.w("ImageView", "Unable to find resource: " + mResource, e); - // Don't try again. - mUri = null; - } - } else if (mUri != null) { - String scheme = mUri.getScheme(); - /* - * if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { // - * TODO Can't fix here try { // Load drawable through Resources, to - * get the source density information - * ContentResolver.OpenResourceIdResult r = - * getContext().getContentResolver().getResourceId(mUri); d = - * r.r.getDrawable(r.id); } catch (Exception e) { Log.w("ImageView", - * "Unable to open content: " + mUri, e); } } else - */if (ContentResolver.SCHEME_CONTENT.equals(scheme) - || ContentResolver.SCHEME_FILE.equals(scheme)) { - InputStream stream = null; - try { - stream = getContext().getContentResolver().openInputStream( - mUri); - m = Movie.decodeStream(stream); - if (m == null) { - stream = getContext().getContentResolver() - .openInputStream(mUri); - d = Drawable.createFromStream(stream, null); - } - } catch (Exception e) { - Log.w("ImageView", "Unable to open content: " + mUri, e); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - Log.w("ImageView", "Unable to close content: " - + mUri, e); - } - } - } - } else { - d = Drawable.createFromPath(mUri.toString()); - } - if (d == null && m == null) { - System.out.println("resolveUri failed on bad bitmap uri: " - + mUri); - // Don't try again. - mUri = null; - } - } else { - return; - } - - updateDrawable(d); - updateMovie(m); - } - - @Override - public int[] onCreateDrawableState(int extraSpace) { - if (mState == null) { - return super.onCreateDrawableState(extraSpace); - } else if (!mMergeState) { - return mState; - } else { - return mergeDrawableStates( - super.onCreateDrawableState(extraSpace + mState.length), - mState); - } - } - - private void updateDrawable(Drawable d) { - if (mDrawable != null) { - mDrawable.setCallback(null); - unscheduleDrawable(mDrawable); - } - mDrawable = d; - if (d != null) { - d.setCallback(this); - if (d.isStateful()) { - d.setState(getDrawableState()); - } - d.setLevel(mLevel); - // d.setLayoutDirection(getLayoutDirection()); // TODO Can't fix, reflection? - // here - d.setVisible(getVisibility() == VISIBLE, true); - mDrawableWidth = d.getIntrinsicWidth(); - mDrawableHeight = d.getIntrinsicHeight(); - applyColorMod(); - configureBounds(); - } else { - mDrawableWidth = mDrawableHeight = -1; - } - } - - private void updateMovie(Movie m) { - mMovie = m; - if (m != null) { - mMovieStart = 0; - mCurrentAnimationTime = 0; - mDrawableWidth = m.width(); - mDrawableHeight = m.height(); - applyColorMod(); - configureBounds(); - } - } - - private void resizeFromResource() { - Drawable d = mDrawable; - Movie m = mMovie; - if (d != null) { - int w = d.getIntrinsicWidth(); - if (w < 0) - w = mDrawableWidth; - int h = d.getIntrinsicHeight(); - if (h < 0) - h = mDrawableHeight; - if (w != mDrawableWidth || h != mDrawableHeight) { - mDrawableWidth = w; - mDrawableHeight = h; - requestLayout(); - } - } else if (m != null) { - int w = m.width(); - if (w < 0) - w = mDrawableWidth; - int h = m.height(); - if (h < 0) - h = mDrawableHeight; - if (w != mDrawableWidth || h != mDrawableHeight) { - mDrawableWidth = w; - mDrawableHeight = h; - requestLayout(); - } - } - } - - @SuppressLint("NewApi") - @Override - public void onRtlPropertiesChanged(int layoutDirection) { - if (Build.VERSION.SDK_INT < 17) { - return; - } - - super.onRtlPropertiesChanged(layoutDirection); - - if (mDrawable != null) { - // mDrawable.setLayoutDirection(layoutDirection); // TODO Can't fix, reflection? - // here - } - } - - private static final Matrix.ScaleToFit[] sS2FArray = { - Matrix.ScaleToFit.FILL, Matrix.ScaleToFit.START, - Matrix.ScaleToFit.CENTER, Matrix.ScaleToFit.END }; - - private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { - // ScaleToFit enum to their corresponding Matrix.ScaleToFit values - return sS2FArray[st.nativeInt - 1]; - } - - @SuppressLint("NewApi") - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - resolveUri(); - int w; - int h; - - // Desired aspect ratio of the view's contents (not including padding) - float desiredAspect = 0.0f; - - // We are allowed to change the view's width - boolean resizeWidth = false; - - // We are allowed to change the view's height - boolean resizeHeight = false; - - final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); - final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); - - if (mDrawable == null && mMovie == null) { - // If no drawable, its intrinsic size is 0. - mDrawableWidth = -1; - mDrawableHeight = -1; - w = h = 0; - } else { - w = mDrawableWidth; - h = mDrawableHeight; - if (w <= 0) - w = 1; - if (h <= 0) - h = 1; - - // We are supposed to adjust view bounds to match the aspect - // ratio of our drawable. See if that is possible. - if (mAdjustViewBounds) { - resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; - resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; - - desiredAspect = (float) w / (float) h; - } - } - - int pleft = getPaddingLeft(); - int pright = getPaddingRight(); - int ptop = getPaddingTop(); - int pbottom = getPaddingBottom(); - - int widthSize; - int heightSize; - - if (resizeWidth || resizeHeight) { - /* - * If we get here, it means we want to resize to match the drawables - * aspect ratio, and we have the freedom to change at least one - * dimension. - */ - - // Get the max possible width given our constraints - widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, - widthMeasureSpec); - - // Get the max possible height given our constraints - heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, - heightMeasureSpec); - - if (desiredAspect != 0.0f) { - // See what our actual aspect ratio is - float actualAspect = (float) (widthSize - pleft - pright) - / (heightSize - ptop - pbottom); - - if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { - - boolean done = false; - - // Try adjusting width to be proportional to height - if (resizeWidth) { - int newWidth = (int) (desiredAspect * (heightSize - - ptop - pbottom)) - + pleft + pright; - - // Allow the width to outgrow its original estimate if - // height is fixed. - if (!resizeHeight && !mAdjustViewBoundsCompat) { - widthSize = resolveAdjustedSize(newWidth, - mMaxWidth, widthMeasureSpec); - } - - if (newWidth <= widthSize) { - widthSize = newWidth; - done = true; - } - } - - // Try adjusting height to be proportional to width - if (!done && resizeHeight) { - int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) - + ptop + pbottom; - - // Allow the height to outgrow its original estimate if - // width is fixed. - if (!resizeWidth && !mAdjustViewBoundsCompat) { - heightSize = resolveAdjustedSize(newHeight, - mMaxHeight, heightMeasureSpec); - } - - if (newHeight <= heightSize) { - heightSize = newHeight; - } - } - } - } - } else { - /* - * We are either don't want to preserve the drawables aspect ratio, - * or we are not allowed to change view dimensions. Just measure in - * the normal way. - */ - w += pleft + pright; - h += ptop + pbottom; - - w = Math.max(w, getSuggestedMinimumWidth()); - h = Math.max(h, getSuggestedMinimumHeight()); - - if (Build.VERSION.SDK_INT < 11) { - widthSize = resolveSize(w, widthMeasureSpec); - heightSize = resolveSize(h, heightMeasureSpec); - } else { - widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); - heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); - } - } - - setMeasuredDimension(widthSize, heightSize); - } - - private int resolveAdjustedSize(int desiredSize, int maxSize, - int measureSpec) { - int result = desiredSize; - int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); - switch (specMode) { - case MeasureSpec.UNSPECIFIED: - /* - * Parent says we can be as big as we want. Just don't be larger - * than max size imposed on ourselves. - */ - result = Math.min(desiredSize, maxSize); - break; - case MeasureSpec.AT_MOST: - // Parent says we can be as big as we want, up to specSize. - // Don't be larger than specSize, and don't be larger than - // the max size imposed on ourselves. - result = Math.min(Math.min(desiredSize, specSize), maxSize); - break; - case MeasureSpec.EXACTLY: - // No choice. Do what we are told. - result = specSize; - break; - } - return result; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, - int bottom) { - configureBounds(); - } - - private void configureBounds() { - if (mDrawable == null && mMovie == null) { - return; - } - - int dwidth = mDrawableWidth; - int dheight = mDrawableHeight; - - int vwidth = getWidth() - getPaddingLeft() - getPaddingRight(); - int vheight = getHeight() - getPaddingTop() - getPaddingBottom(); - - boolean fits = (dwidth < 0 || vwidth == dwidth) - && (dheight < 0 || vheight == dheight); - - if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { - /* - * If the drawable has no intrinsic size, or we're told to - * scaletofit, then we just fill our entire view. - */ - if (mDrawable != null) { - mDrawable.setBounds(0, 0, vwidth, vheight); - mDrawMatrix = null; - } else if (dheight > 0 && dheight > 0) { - mDrawMatrix = new Matrix(); - mDrawMatrix.setScale((float) vwidth / dwidth, (float) vheight - / dheight); - } - - } else { - // We need to do the scaling ourself, so have the drawable - // use its native size. - if (mDrawable != null) - mDrawable.setBounds(0, 0, dwidth, dheight); - - if (ScaleType.MATRIX == mScaleType) { - // Use the specified matrix as-is. - if (mMatrix.isIdentity()) { - mDrawMatrix = null; - } else { - mDrawMatrix = mMatrix; - } - } else if (fits) { - // The bitmap fits exactly, no transform needed. - mDrawMatrix = null; - } else if (ScaleType.CENTER == mScaleType) { - // Center bitmap in view, no scaling. - mDrawMatrix = mMatrix; - mDrawMatrix.setTranslate( - (int) ((vwidth - dwidth) * 0.5f + 0.5f), - (int) ((vheight - dheight) * 0.5f + 0.5f)); - } else if (ScaleType.CENTER_CROP == mScaleType) { - mDrawMatrix = mMatrix; - - float scale; - float dx = 0, dy = 0; - - if (dwidth * vheight > vwidth * dheight) { - scale = (float) vheight / (float) dheight; - dx = (vwidth - dwidth * scale) * 0.5f; - } else { - scale = (float) vwidth / (float) dwidth; - dy = (vheight - dheight * scale) * 0.5f; - } - - mDrawMatrix.setScale(scale, scale); - mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); - } else if (ScaleType.CENTER_INSIDE == mScaleType) { - mDrawMatrix = mMatrix; - float scale; - float dx; - float dy; - - if (dwidth <= vwidth && dheight <= vheight) { - scale = 1.0f; - } else { - scale = Math.min((float) vwidth / (float) dwidth, - (float) vheight / (float) dheight); - } - - dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); - dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); - - mDrawMatrix.setScale(scale, scale); - mDrawMatrix.postTranslate(dx, dy); - } else { - // Generate the required transform. - mTempSrc.set(0, 0, dwidth, dheight); - mTempDst.set(0, 0, vwidth, vheight); - - mDrawMatrix = mMatrix; - mDrawMatrix.setRectToRect(mTempSrc, mTempDst, - scaleTypeToScaleToFit(mScaleType)); - } - } - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - Drawable d = mDrawable; - if (d != null && d.isStateful()) { - d.setState(getDrawableState()); - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (mDrawable == null && mMovie == null) { - return; // couldn't resolve the URI - } - - if (mDrawableWidth == 0 || mDrawableHeight == 0) { - return; // nothing to draw (empty bounds) - } - - int pLeft = getPaddingLeft(); - - if (mDrawMatrix == null && getPaddingTop() == 0 && pLeft == 0) { - if (mDrawable != null) - mDrawable.draw(canvas); - else - drawMovieFrame(canvas); - } else { - int saveCount = canvas.getSaveCount(); - canvas.save(); - - if (mCropToPadding) { - final int scrollX = getScrollX(); - final int scrollY = getScrollY(); - canvas.clipRect(scrollX + pLeft, scrollY + getPaddingTop(), - scrollX + getRight() - getLeft() - getPaddingRight(), scrollY - + getBottom() - getTop() - getPaddingBottom()); - } - - canvas.translate(pLeft, getPaddingTop()); - - if (mDrawMatrix != null) { - canvas.concat(mDrawMatrix); - } - if (mDrawable != null) - mDrawable.draw(canvas); - else - drawMovieFrame(canvas); - canvas.restoreToCount(saveCount); - } - } - - @SuppressLint("NewApi") - private void drawMovieFrame(Canvas canvas) { - // if (!mPaused) - updateAnimationTime(); - - mMovie.setTime(mCurrentAnimationTime); - mMovie.draw(canvas, 0, 0); - - // if (!mPaused) - if (Build.VERSION.SDK_INT < 16) { - invalidate(); - } else { - postInvalidateOnAnimation(); - } - } - - private void updateAnimationTime() { - long now = android.os.SystemClock.uptimeMillis(); - - if (mMovieStart == 0) { - mMovieStart = now; - } - - int dur = mMovie.duration(); - - if (dur == 0) { - dur = DEFAULT_MOVIEW_DURATION; - } - - mCurrentAnimationTime = (int) ((now - mMovieStart) % dur); - } - - /** - *

- * Return the offset of the widget's text baseline from the widget's top - * boundary. - *

- * - * @return the offset of the baseline within the widget's bounds or -1 if - * baseline alignment is not supported. - */ - @Override - @ViewDebug.ExportedProperty(category = "layout") - public int getBaseline() { - if (mBaselineAlignBottom) { - return getMeasuredHeight(); - } else { - return mBaseline; - } - } - - /** - *

- * Set the offset of the widget's text baseline from the widget's top - * boundary. This value is overridden by the - * {@link #setBaselineAlignBottom(boolean)} property. - *

- * - * @param baseline - * The baseline to use, or -1 if none is to be provided. - * - * @see #setBaseline(int) - * @attr ref android.R.styleable#ImageView_baseline - */ - public void setBaseline(int baseline) { - if (mBaseline != baseline) { - mBaseline = baseline; - requestLayout(); - } - } - - /** - * Set whether to set the baseline of this view to the bottom of the view. - * Setting this value overrides any calls to setBaseline. - * - * @param aligned - * If true, the image view will be baseline aligned with based on - * its bottom edge. - * - * @attr ref android.R.styleable#ImageView_baselineAlignBottom - */ - public void setBaselineAlignBottom(boolean aligned) { - if (mBaselineAlignBottom != aligned) { - mBaselineAlignBottom = aligned; - requestLayout(); - } - } - - /** - * Return whether this view's baseline will be considered the bottom of the - * view. - * - * @see #setBaselineAlignBottom(boolean) - */ - public boolean getBaselineAlignBottom() { - return mBaselineAlignBottom; - } - - /** - * Set a tinting option for the image. - * - * @param color - * Color tint to apply. - * @param mode - * How to apply the color. The standard mode is - * {@link PorterDuff.Mode#SRC_ATOP} - * - * @attr ref android.R.styleable#ImageView_tint - */ - public final void setColorFilter(int color, PorterDuff.Mode mode) { - setColorFilter(new PorterDuffColorFilter(color, mode)); - } - - /** - * Set a tinting option for the image. Assumes - * {@link PorterDuff.Mode#SRC_ATOP} blending mode. - * - * @param color - * Color tint to apply. - * @attr ref android.R.styleable#ImageView_tint - */ - public final void setColorFilter(int color) { - setColorFilter(color, PorterDuff.Mode.SRC_ATOP); - } - - public final void clearColorFilter() { - setColorFilter(null); - } - - /** - * @hide Candidate for future API inclusion - */ - public final void setXfermode(Xfermode mode) { - if (mXfermode != mode) { - mXfermode = mode; - mColorMod = true; - applyColorMod(); - invalidate(); - } - } - - /** - * Returns the active color filter for this ImageView. - * - * @return the active color filter for this ImageView - * - * @see #setColorFilter(android.graphics.ColorFilter) - */ - public ColorFilter getColorFilter() { - return mColorFilter; - } - - /** - * Apply an arbitrary colorfilter to the image. - * - * @param cf - * the colorfilter to apply (may be null) - * - * @see #getColorFilter() - */ - public void setColorFilter(ColorFilter cf) { - if (mColorFilter != cf) { - mColorFilter = cf; - mColorMod = true; - applyColorMod(); - invalidate(); - } - } - - /** - * Returns the alpha that will be applied to the drawable of this ImageView. - * - * @return the alpha that will be applied to the drawable of this ImageView - * - * @see #setImageAlpha(int) - */ - public int getImageAlpha() { - return mAlpha; - } - - /** - * Sets the alpha value that should be applied to the image. - * - * @param alpha - * the alpha value that should be applied to the image - * - * @see #getImageAlpha() - */ - public void setImageAlpha(int alpha) { - setAlpha(alpha); - } - - /** - * Sets the alpha value that should be applied to the image. - * - * @param alpha - * the alpha value that should be applied to the image - * - * @deprecated use #setImageAlpha(int) instead - */ - @Deprecated - public void setAlpha(int alpha) { - alpha &= 0xFF; // keep it legal - if (mAlpha != alpha) { - mAlpha = alpha; - mColorMod = true; - applyColorMod(); - invalidate(); - } - } - - private void applyColorMod() { - // Only mutate and apply when modifications have occurred. This should - // not reset the mColorMod flag, since these filters need to be - // re-applied if the Drawable is changed. - if (mDrawable != null && mColorMod) { - mDrawable = mDrawable.mutate(); - mDrawable.setColorFilter(mColorFilter); - // mDrawable.setXfermode(mXfermode); // Can't fix here - mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); - } - } - - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (mDrawable != null) { - mDrawable.setVisible(visibility == VISIBLE, false); - } - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mDrawable != null) { - mDrawable.setVisible(getVisibility() == VISIBLE, false); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (mDrawable != null) { - mDrawable.setVisible(false, false); - } - } - - @SuppressLint("NewApi") - @Override - public void onInitializeAccessibilityEvent(AccessibilityEvent event) { - if (Build.VERSION.SDK_INT < 14) { - return; - } - - super.onInitializeAccessibilityEvent(event); - event.setClassName(SuperImageView.class.getName()); - } - - @SuppressLint("NewApi") - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - if (Build.VERSION.SDK_INT < 14) { - return; - } - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(SuperImageView.class.getName()); - } +package com.hippo.ehviewer.view; + +import android.annotation.SuppressLint; +import android.content.ContentResolver; +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Movie; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.Xfermode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.util.AttributeSet; +import com.hippo.ehviewer.util.Log; +import android.view.View; +import android.view.ViewDebug; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.RemoteViews.RemoteView; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; + +import com.hippo.ehviewer.R; + +// TODO use native to improve gif + +/** + * Displays an arbitrary image, such as an icon. The ImageView class can load + * images from various sources (such as resources or content providers), takes + * care of computing its measurement from the image so that it can be used in + * any layout manager, and provides various display options such as scaling and + * tinting. + * + * @attr ref android.R.styleable#ImageView_adjustViewBounds + * @attr ref android.R.styleable#ImageView_src + * @attr ref android.R.styleable#ImageView_maxWidth + * @attr ref android.R.styleable#ImageView_maxHeight + * @attr ref android.R.styleable#ImageView_tint + * @attr ref android.R.styleable#ImageView_scaleType + * @attr ref android.R.styleable#ImageView_cropToPadding + */ +@RemoteView +public class SuperImageView extends View { + private static String TAG = "SuperImageView"; + // for gif + private Movie mMovie = null; + // Not 0 mean the same timer + private long mMovieStart = 1; + private int mCurrentAnimationTime = 0; + + private static final int DEFAULT_MOVIEW_DURATION = 1000; + + // settable by the client + private Uri mUri; + private int mResource = 0; + private Matrix mMatrix; + private ScaleType mScaleType; + private boolean mHaveFrame = false; + private boolean mAdjustViewBounds = false; + private int mMaxWidth = Integer.MAX_VALUE; + private int mMaxHeight = Integer.MAX_VALUE; + + // these are applied to the drawable + private ColorFilter mColorFilter; + private Xfermode mXfermode; + private int mAlpha = 255; + private int mViewAlphaScale = 256; + private boolean mColorMod = false; + + private Drawable mDrawable = null; + private int[] mState = null; + private boolean mMergeState = false; + private int mLevel = 0; + private int mDrawableWidth; + private int mDrawableHeight; + private Matrix mDrawMatrix = null; + + // Avoid allocations... + private RectF mTempSrc = new RectF(); + private RectF mTempDst = new RectF(); + + private boolean mCropToPadding; + + private int mBaseline = -1; + private boolean mBaselineAlignBottom = false; + + // AdjustViewBounds behavior will be in compatibility mode for older apps. + private boolean mAdjustViewBoundsCompat = false; + + private static final ScaleType[] sScaleTypeArray = { ScaleType.MATRIX, + ScaleType.FIT_XY, ScaleType.FIT_START, ScaleType.FIT_CENTER, + ScaleType.FIT_END, ScaleType.CENTER, ScaleType.CENTER_CROP, + ScaleType.CENTER_INSIDE }; + + public SuperImageView(Context context) { + super(context); + initImageView(); + } + + public SuperImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SuperImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initImageView(); + + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SuperImageView, defStyle, 0); + + mBaselineAlignBottom = a.getBoolean( + R.styleable.SuperImageView_baselineAlignBottom, false); + + mBaseline = a.getDimensionPixelSize( + R.styleable.SuperImageView_baseline, -1); + + setAdjustViewBounds(a.getBoolean( + R.styleable.SuperImageView_adjustViewBounds, false)); + + setMaxWidth(a.getDimensionPixelSize( + R.styleable.SuperImageView_maxWidth, Integer.MAX_VALUE)); + + setMaxHeight(a.getDimensionPixelSize( + R.styleable.SuperImageView_maxHeight, Integer.MAX_VALUE)); + + int index = a.getInt(R.styleable.SuperImageView_scaleType, -1); + if (index >= 0) { + setScaleType(sScaleTypeArray[index]); + } + + int tint = a.getInt(R.styleable.SuperImageView_tint, 0); + if (tint != 0) { + setColorFilter(tint); + } + + int alpha = a.getInt(R.styleable.SuperImageView_drawableAlpha, 255); + if (alpha != 255) { + setAlpha(alpha); + } + + mCropToPadding = a.getBoolean(R.styleable.SuperImageView_cropToPadding, + false); + + int resId = a.getResourceId(R.styleable.SuperImageView_src, -1); + if (resId == -1) { + Drawable d = a.getDrawable(R.styleable.SuperImageView_src); + if (d != null) { + setImageDrawable(d); + } + } else + this.setImageResource(resId); + + a.recycle(); + + // need inflate syntax/reader for matrix + } + + private void initImageView() { + mMatrix = new Matrix(); + mScaleType = ScaleType.FIT_CENTER; + mAdjustViewBoundsCompat = getContext().getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1; + } + + @Override + protected boolean verifyDrawable(Drawable dr) { + return mDrawable == dr || super.verifyDrawable(dr); + } + + protected boolean verifyMovie(Movie mv) { + return mMovie == mv; + } + + @SuppressLint("NewApi") + @Override + public void jumpDrawablesToCurrentState() { + if (Build.VERSION.SDK_INT < 11) { + return; + } + + super.jumpDrawablesToCurrentState(); + if (mDrawable != null) + mDrawable.jumpToCurrentState(); + } + + @Override + public void invalidateDrawable(Drawable dr) { + if (dr == mDrawable) { + /* + * we invalidate the whole view in this case because it's very hard + * to know where the drawable actually is. This is made complicated + * because of the offsets and transformations that can be applied. + * In theory we could get the drawable's bounds and run them through + * the transformation and offsets, but this is probably not worth + * the effort. + */ + invalidate(); + } else { + super.invalidateDrawable(dr); + } + } + + public void invalidateMovie(Movie mv) { + if (mv == mMovie) { + /* + * we invalidate the whole view in this case because it's very hard + * to know where the drawable actually is. This is made complicated + * because of the offsets and transformations that can be applied. + * In theory we could get the drawable's bounds and run them through + * the transformation and offsets, but this is probably not worth + * the effort. + */ + invalidate(); + } + } + + @Override + public boolean hasOverlappingRendering() { + return (getBackground() != null && getBackground().getCurrent() != null); + } + + @SuppressLint("NewApi") + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + if (Build.VERSION.SDK_INT < 14) { + return; + } + + super.onPopulateAccessibilityEvent(event); + CharSequence contentDescription = getContentDescription(); + if (!TextUtils.isEmpty(contentDescription)) { + event.getText().add(contentDescription); + } + } + + /** + * True when ImageView is adjusting its bounds to preserve the aspect ratio + * of its drawable + * + * @return whether to adjust the bounds of this view to presrve the original + * aspect ratio of the drawable + * + * @see #setAdjustViewBounds(boolean) + * + * @attr ref android.R.styleable#ImageView_adjustViewBounds + */ + public boolean getAdjustViewBounds() { + return mAdjustViewBounds; + } + + /** + * Set this to true if you want the ImageView to adjust its bounds to + * preserve the aspect ratio of its drawable. + * + *

+ * Note: If the application targets API level 17 or lower, + * adjustViewBounds will allow the drawable to shrink the view bounds, but + * not grow to fill available measured space in all cases. This is for + * compatibility with legacy {@link android.view.View.MeasureSpec + * MeasureSpec} and {@link android.widget.RelativeLayout RelativeLayout} + * behavior. + *

+ * + * @param adjustViewBounds + * Whether to adjust the bounds of this view to preserve the + * original aspect ratio of the drawable. + * + * @see #getAdjustViewBounds() + * + * @attr ref android.R.styleable#ImageView_adjustViewBounds + */ + public void setAdjustViewBounds(boolean adjustViewBounds) { + mAdjustViewBounds = adjustViewBounds; + if (adjustViewBounds) { + setScaleType(ScaleType.FIT_CENTER); + } + } + + /** + * The maximum width of this view. + * + * @return The maximum width of this view + * + * @see #setMaxWidth(int) + * + * @attr ref android.R.styleable#ImageView_maxWidth + */ + public int getMaxWidth() { + return mMaxWidth; + } + + /** + * An optional argument to supply a maximum width for this view. Only valid + * if {@link #setAdjustViewBounds(boolean)} has been set to true. To set an + * image to be a maximum of 100 x 100 while preserving the original aspect + * ratio, do the following: 1) set adjustViewBounds to true 2) set maxWidth + * and maxHeight to 100 3) set the height and width layout params to + * WRAP_CONTENT. + * + *

+ * Note that this view could be still smaller than 100 x 100 using this + * approach if the original image is small. To set an image to a fixed size, + * specify that size in the layout params and then use + * {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine + * how to fit the image within the bounds. + *

+ * + * @param maxWidth + * maximum width for this view + * + * @see #getMaxWidth() + * + * @attr ref android.R.styleable#ImageView_maxWidth + */ + public void setMaxWidth(int maxWidth) { + mMaxWidth = maxWidth; + } + + /** + * The maximum height of this view. + * + * @return The maximum height of this view + * + * @see #setMaxHeight(int) + * + * @attr ref android.R.styleable#ImageView_maxHeight + */ + public int getMaxHeight() { + return mMaxHeight; + } + + /** + * An optional argument to supply a maximum height for this view. Only valid + * if {@link #setAdjustViewBounds(boolean)} has been set to true. To set an + * image to be a maximum of 100 x 100 while preserving the original aspect + * ratio, do the following: 1) set adjustViewBounds to true 2) set maxWidth + * and maxHeight to 100 3) set the height and width layout params to + * WRAP_CONTENT. + * + *

+ * Note that this view could be still smaller than 100 x 100 using this + * approach if the original image is small. To set an image to a fixed size, + * specify that size in the layout params and then use + * {@link #setScaleType(android.widget.ImageView.ScaleType)} to determine + * how to fit the image within the bounds. + *

+ * + * @param maxHeight + * maximum height for this view + * + * @see #getMaxHeight() + * + * @attr ref android.R.styleable#ImageView_maxHeight + */ + public void setMaxHeight(int maxHeight) { + mMaxHeight = maxHeight; + } + + /** + * Return the view's drawable, or null if no drawable has been assigned. + */ + public Drawable getDrawable() { + return mDrawable; + } + + public int getResourceWidth() { + return mDrawableWidth; + } + + public int getResourceHeight() { + return mDrawableHeight; + } + + public Movie getMovie() { + return mMovie; + } + + public boolean isEmpty() { + if (mMovie == null && mDrawable == null) + return true; + else + return false; + } + + /** + * Sets a drawable as the content of this ImageView. + * + *

+ * This does Bitmap reading and decoding on the UI thread, which can cause a + * latency hiccup. If that's a concern, consider using + * {@link #setImageDrawable(android.graphics.drawable.Drawable)} or + * {@link #setImageBitmap(android.graphics.Bitmap)} and + * {@link android.graphics.BitmapFactory} instead. + *

+ * + * @param resId + * the resource identifier of the drawable + * + * @attr ref android.R.styleable#ImageView_src + */ + public void setImageResource(int resId) { + if (mUri != null || mResource != resId) { + updateDrawable(null); + updateMovie(null); + mResource = resId; + mUri = null; + + final int oldWidth = mDrawableWidth; + final int oldHeight = mDrawableHeight; + + resolveUri(); + + if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { + requestLayout(); + } + invalidate(); + } + } + + /** + * Sets the content of this ImageView to the specified Uri. + * + *

+ * This does Bitmap reading and decoding on the UI thread, which can cause a + * latency hiccup. If that's a concern, consider using + * {@link #setImageDrawable(android.graphics.drawable.Drawable)} or + * {@link #setImageBitmap(android.graphics.Bitmap)} and + * {@link android.graphics.BitmapFactory} instead. + *

+ * + * @param uri + * The Uri of an image + */ + public void setImageURI(Uri uri) { + if (mResource != 0 + || (mUri != uri && (uri == null || mUri == null || !uri + .equals(mUri)))) { + updateDrawable(null); + updateMovie(null); + mResource = 0; + mUri = uri; + + final int oldWidth = mDrawableWidth; + final int oldHeight = mDrawableHeight; + + resolveUri(); + + if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { + requestLayout(); + } + invalidate(); + } + } + + /** + * Sets a drawable as the content of this ImageView. + * + * @param drawable + * The drawable to set + */ + public void setImageDrawable(Drawable drawable) { + if (mDrawable != drawable) { + mResource = 0; + mUri = null; + mMovie = null; + + final int oldWidth = mDrawableWidth; + final int oldHeight = mDrawableHeight; + + updateDrawable(drawable); + updateMovie(null); + + if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { + requestLayout(); + } + invalidate(); + } + } + + /** + * Sets a Movie as the content of this SuperImageView. + * + * @param mv + * The movie to set + */ + public void setImageMovie(Movie mv) { + if (mMovie != mv) { + mResource = 0; + mUri = null; + mDrawable = null; + + final int oldWidth = mDrawableWidth; + final int oldHeight = mDrawableHeight; + + updateDrawable(null); + updateMovie(mv); + + if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { + requestLayout(); + } + invalidate(); + } + } + + /** + * Sets a InputStream as the content of this SuperImageView. + * + * @param is + * The InputStream to set + */ + public void setImageInputStream(InputStream is) { + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = is.read(buffer)) > -1) { + baos.write(buffer, 0, len); + } + is.close(); + baos.flush(); + + byte[] bytes = baos.toByteArray(); + Movie m = Movie.decodeByteArray(bytes, 0, bytes.length); + if (m == null) { + Bitmap b = BitmapFactory + .decodeByteArray(bytes, 0, bytes.length); + setImageBitmap(b); + } else + setImageMovie(m); + + baos.close(); + } catch (IOException e) { + Log.e(TAG, "Set Image from InputStream error !"); + } + } + + /** + * Sets a Bitmap as the content of this ImageView. + * + * @param bm + * The bitmap to set + */ + public void setImageBitmap(Bitmap bm) { + // if this is used frequently, may handle bitmaps explicitly + // to reduce the intermediate drawable object + setImageDrawable(new BitmapDrawable(getContext().getResources(), bm)); + } + + public void setImageState(int[] state, boolean merge) { + mState = state; + mMergeState = merge; + if (mDrawable != null) { + refreshDrawableState(); + resizeFromResource(); + } + } + + @Override + public void setSelected(boolean selected) { + super.setSelected(selected); + resizeFromResource(); + } + + /** + * Sets the image level, when it is constructed from a + * {@link android.graphics.drawable.LevelListDrawable}. + * + * @param level + * The new level for the image. + */ + public void setImageLevel(int level) { + mLevel = level; + if (mDrawable != null) { + mDrawable.setLevel(level); + resizeFromResource(); + } + } + + /** + * Options for scaling the bounds of an image to the bounds of this view. + */ + public enum ScaleType { + /** + * Scale using the image matrix when drawing. The image matrix can be + * set using {@link SuperImageView#setImageMatrix(Matrix)}. From XML, + * use this syntax: android:scaleType="matrix". + */ + MATRIX(0), + /** + * Scale the image using {@link Matrix.ScaleToFit#FILL}. From XML, use + * this syntax: android:scaleType="fitXY". + */ + FIT_XY(1), + /** + * Scale the image using {@link Matrix.ScaleToFit#START}. From XML, use + * this syntax: android:scaleType="fitStart". + */ + FIT_START(2), + /** + * Scale the image using {@link Matrix.ScaleToFit#CENTER}. From XML, use + * this syntax: android:scaleType="fitCenter". + */ + FIT_CENTER(3), + /** + * Scale the image using {@link Matrix.ScaleToFit#END}. From XML, use + * this syntax: android:scaleType="fitEnd". + */ + FIT_END(4), + /** + * Center the image in the view, but perform no scaling. From XML, use + * this syntax: android:scaleType="center". + */ + CENTER(5), + /** + * Scale the image uniformly (maintain the image's aspect ratio) so that + * both dimensions (width and height) of the image will be equal to or + * larger than the corresponding dimension of the view (minus padding). + * The image is then centered in the view. From XML, use this syntax: + * android:scaleType="centerCrop". + */ + CENTER_CROP(6), + /** + * Scale the image uniformly (maintain the image's aspect ratio) so that + * both dimensions (width and height) of the image will be equal to or + * less than the corresponding dimension of the view (minus padding). + * The image is then centered in the view. From XML, use this syntax: + * android:scaleType="centerInside". + */ + CENTER_INSIDE(7); + + ScaleType(int ni) { + nativeInt = ni; + } + + final int nativeInt; + } + + /** + * Controls how the image should be resized or moved to match the size of + * this ImageView. + * + * @param scaleType + * The desired scaling mode. + * + * @attr ref android.R.styleable#ImageView_scaleType + */ + public void setScaleType(ScaleType scaleType) { + if (scaleType == null) { + throw new NullPointerException(); + } + + if (mScaleType != scaleType) { + mScaleType = scaleType; + + setWillNotCacheDrawing(mScaleType == ScaleType.CENTER); + + requestLayout(); + invalidate(); + } + } + + /** + * Return the current scale type in use by this ImageView. + * + * @see SuperImageView.ScaleType + * + * @attr ref android.R.styleable#ImageView_scaleType + */ + public ScaleType getScaleType() { + return mScaleType; + } + + /** + * Return the view's optional matrix. This is applied to the view's drawable + * when it is drawn. If there is not matrix, this method will return an + * identity matrix. Do not change this matrix in place but make a copy. If + * you want a different matrix applied to the drawable, be sure to call + * setImageMatrix(). + */ + public Matrix getImageMatrix() { + if (mDrawMatrix == null) { + // return new Matrix(Matrix.IDENTITY_MATRIX); //TODO Fix here + return new Matrix(); + } + return mDrawMatrix; + } + + public void setImageMatrix(Matrix matrix) { + // collaps null and identity to just null + if (matrix != null && matrix.isIdentity()) { + matrix = null; + } + + // don't invalidate unless we're actually changing our matrix + if (matrix == null && !mMatrix.isIdentity() || matrix != null + && !mMatrix.equals(matrix)) { + mMatrix.set(matrix); + configureBounds(); + invalidate(); + } + } + + /** + * Return whether this ImageView crops to padding. + * + * @return whether this ImageView crops to padding + * + * @see #setCropToPadding(boolean) + * + * @attr ref android.R.styleable#ImageView_cropToPadding + */ + public boolean getCropToPadding() { + return mCropToPadding; + } + + /** + * Sets whether this ImageView will crop to padding. + * + * @param cropToPadding + * whether this ImageView will crop to padding + * + * @see #getCropToPadding() + * + * @attr ref android.R.styleable#ImageView_cropToPadding + */ + public void setCropToPadding(boolean cropToPadding) { + if (mCropToPadding != cropToPadding) { + mCropToPadding = cropToPadding; + requestLayout(); + invalidate(); + } + } + + private void resolveUri() { + if (mDrawable != null && mMovie != null) { + return; + } + + Resources rsrc = getResources(); + if (rsrc == null) { + return; + } + + Drawable d = null; + Movie m = null; + + if (mResource != 0) { + try { + InputStream is = rsrc.openRawResource(mResource); + m = Movie.decodeStream(is); + if (m == null) { + d = rsrc.getDrawable(mResource); + } + } catch (Exception e) { + Log.w("ImageView", "Unable to find resource: " + mResource, e); + // Don't try again. + mUri = null; + } + } else if (mUri != null) { + String scheme = mUri.getScheme(); + /* + * if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { // + * TODO Can't fix here try { // Load drawable through Resources, to + * get the source density information + * ContentResolver.OpenResourceIdResult r = + * getContext().getContentResolver().getResourceId(mUri); d = + * r.r.getDrawable(r.id); } catch (Exception e) { Log.w("ImageView", + * "Unable to open content: " + mUri, e); } } else + */if (ContentResolver.SCHEME_CONTENT.equals(scheme) + || ContentResolver.SCHEME_FILE.equals(scheme)) { + InputStream stream = null; + try { + stream = getContext().getContentResolver().openInputStream( + mUri); + m = Movie.decodeStream(stream); + if (m == null) { + stream = getContext().getContentResolver() + .openInputStream(mUri); + d = Drawable.createFromStream(stream, null); + } + } catch (Exception e) { + Log.w("ImageView", "Unable to open content: " + mUri, e); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + Log.w("ImageView", "Unable to close content: " + + mUri, e); + } + } + } + } else { + d = Drawable.createFromPath(mUri.toString()); + } + if (d == null && m == null) { + System.out.println("resolveUri failed on bad bitmap uri: " + + mUri); + // Don't try again. + mUri = null; + } + } else { + return; + } + + updateDrawable(d); + updateMovie(m); + } + + @Override + public int[] onCreateDrawableState(int extraSpace) { + if (mState == null) { + return super.onCreateDrawableState(extraSpace); + } else if (!mMergeState) { + return mState; + } else { + return mergeDrawableStates( + super.onCreateDrawableState(extraSpace + mState.length), + mState); + } + } + + private void updateDrawable(Drawable d) { + if (mDrawable != null) { + mDrawable.setCallback(null); + unscheduleDrawable(mDrawable); + } + mDrawable = d; + if (d != null) { + d.setCallback(this); + if (d.isStateful()) { + d.setState(getDrawableState()); + } + d.setLevel(mLevel); + // d.setLayoutDirection(getLayoutDirection()); // TODO Can't fix, reflection? + // here + d.setVisible(getVisibility() == VISIBLE, true); + mDrawableWidth = d.getIntrinsicWidth(); + mDrawableHeight = d.getIntrinsicHeight(); + applyColorMod(); + configureBounds(); + } else { + mDrawableWidth = mDrawableHeight = -1; + } + } + + private void updateMovie(Movie m) { + mMovie = m; + if (m != null) { + mMovieStart = 0; + mCurrentAnimationTime = 0; + mDrawableWidth = m.width(); + mDrawableHeight = m.height(); + applyColorMod(); + configureBounds(); + } + } + + private void resizeFromResource() { + Drawable d = mDrawable; + Movie m = mMovie; + if (d != null) { + int w = d.getIntrinsicWidth(); + if (w < 0) + w = mDrawableWidth; + int h = d.getIntrinsicHeight(); + if (h < 0) + h = mDrawableHeight; + if (w != mDrawableWidth || h != mDrawableHeight) { + mDrawableWidth = w; + mDrawableHeight = h; + requestLayout(); + } + } else if (m != null) { + int w = m.width(); + if (w < 0) + w = mDrawableWidth; + int h = m.height(); + if (h < 0) + h = mDrawableHeight; + if (w != mDrawableWidth || h != mDrawableHeight) { + mDrawableWidth = w; + mDrawableHeight = h; + requestLayout(); + } + } + } + + @SuppressLint("NewApi") + @Override + public void onRtlPropertiesChanged(int layoutDirection) { + if (Build.VERSION.SDK_INT < 17) { + return; + } + + super.onRtlPropertiesChanged(layoutDirection); + + if (mDrawable != null) { + // mDrawable.setLayoutDirection(layoutDirection); // TODO Can't fix, reflection? + // here + } + } + + private static final Matrix.ScaleToFit[] sS2FArray = { + Matrix.ScaleToFit.FILL, Matrix.ScaleToFit.START, + Matrix.ScaleToFit.CENTER, Matrix.ScaleToFit.END }; + + private static Matrix.ScaleToFit scaleTypeToScaleToFit(ScaleType st) { + // ScaleToFit enum to their corresponding Matrix.ScaleToFit values + return sS2FArray[st.nativeInt - 1]; + } + + @SuppressLint("NewApi") + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + resolveUri(); + int w; + int h; + + // Desired aspect ratio of the view's contents (not including padding) + float desiredAspect = 0.0f; + + // We are allowed to change the view's width + boolean resizeWidth = false; + + // We are allowed to change the view's height + boolean resizeHeight = false; + + final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + + if (mDrawable == null && mMovie == null) { + // If no drawable, its intrinsic size is 0. + mDrawableWidth = -1; + mDrawableHeight = -1; + w = h = 0; + } else { + w = mDrawableWidth; + h = mDrawableHeight; + if (w <= 0) + w = 1; + if (h <= 0) + h = 1; + + // We are supposed to adjust view bounds to match the aspect + // ratio of our drawable. See if that is possible. + if (mAdjustViewBounds) { + resizeWidth = widthSpecMode != MeasureSpec.EXACTLY; + resizeHeight = heightSpecMode != MeasureSpec.EXACTLY; + + desiredAspect = (float) w / (float) h; + } + } + + int pleft = getPaddingLeft(); + int pright = getPaddingRight(); + int ptop = getPaddingTop(); + int pbottom = getPaddingBottom(); + + int widthSize; + int heightSize; + + if (resizeWidth || resizeHeight) { + /* + * If we get here, it means we want to resize to match the drawables + * aspect ratio, and we have the freedom to change at least one + * dimension. + */ + + // Get the max possible width given our constraints + widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, + widthMeasureSpec); + + // Get the max possible height given our constraints + heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, + heightMeasureSpec); + + if (desiredAspect != 0.0f) { + // See what our actual aspect ratio is + float actualAspect = (float) (widthSize - pleft - pright) + / (heightSize - ptop - pbottom); + + if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { + + boolean done = false; + + // Try adjusting width to be proportional to height + if (resizeWidth) { + int newWidth = (int) (desiredAspect * (heightSize + - ptop - pbottom)) + + pleft + pright; + + // Allow the width to outgrow its original estimate if + // height is fixed. + if (!resizeHeight && !mAdjustViewBoundsCompat) { + widthSize = resolveAdjustedSize(newWidth, + mMaxWidth, widthMeasureSpec); + } + + if (newWidth <= widthSize) { + widthSize = newWidth; + done = true; + } + } + + // Try adjusting height to be proportional to width + if (!done && resizeHeight) { + int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + + ptop + pbottom; + + // Allow the height to outgrow its original estimate if + // width is fixed. + if (!resizeWidth && !mAdjustViewBoundsCompat) { + heightSize = resolveAdjustedSize(newHeight, + mMaxHeight, heightMeasureSpec); + } + + if (newHeight <= heightSize) { + heightSize = newHeight; + } + } + } + } + } else { + /* + * We are either don't want to preserve the drawables aspect ratio, + * or we are not allowed to change view dimensions. Just measure in + * the normal way. + */ + w += pleft + pright; + h += ptop + pbottom; + + w = Math.max(w, getSuggestedMinimumWidth()); + h = Math.max(h, getSuggestedMinimumHeight()); + + if (Build.VERSION.SDK_INT < 11) { + widthSize = resolveSize(w, widthMeasureSpec); + heightSize = resolveSize(h, heightMeasureSpec); + } else { + widthSize = resolveSizeAndState(w, widthMeasureSpec, 0); + heightSize = resolveSizeAndState(h, heightMeasureSpec, 0); + } + } + + setMeasuredDimension(widthSize, heightSize); + } + + private int resolveAdjustedSize(int desiredSize, int maxSize, + int measureSpec) { + int result = desiredSize; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + switch (specMode) { + case MeasureSpec.UNSPECIFIED: + /* + * Parent says we can be as big as we want. Just don't be larger + * than max size imposed on ourselves. + */ + result = Math.min(desiredSize, maxSize); + break; + case MeasureSpec.AT_MOST: + // Parent says we can be as big as we want, up to specSize. + // Don't be larger than specSize, and don't be larger than + // the max size imposed on ourselves. + result = Math.min(Math.min(desiredSize, specSize), maxSize); + break; + case MeasureSpec.EXACTLY: + // No choice. Do what we are told. + result = specSize; + break; + } + return result; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + configureBounds(); + } + + private void configureBounds() { + if (mDrawable == null && mMovie == null) { + return; + } + + int dwidth = mDrawableWidth; + int dheight = mDrawableHeight; + + int vwidth = getWidth() - getPaddingLeft() - getPaddingRight(); + int vheight = getHeight() - getPaddingTop() - getPaddingBottom(); + + boolean fits = (dwidth < 0 || vwidth == dwidth) + && (dheight < 0 || vheight == dheight); + + if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) { + /* + * If the drawable has no intrinsic size, or we're told to + * scaletofit, then we just fill our entire view. + */ + if (mDrawable != null) { + mDrawable.setBounds(0, 0, vwidth, vheight); + mDrawMatrix = null; + } else if (dheight > 0 && dheight > 0) { + mDrawMatrix = new Matrix(); + mDrawMatrix.setScale((float) vwidth / dwidth, (float) vheight + / dheight); + } + + } else { + // We need to do the scaling ourself, so have the drawable + // use its native size. + if (mDrawable != null) + mDrawable.setBounds(0, 0, dwidth, dheight); + + if (ScaleType.MATRIX == mScaleType) { + // Use the specified matrix as-is. + if (mMatrix.isIdentity()) { + mDrawMatrix = null; + } else { + mDrawMatrix = mMatrix; + } + } else if (fits) { + // The bitmap fits exactly, no transform needed. + mDrawMatrix = null; + } else if (ScaleType.CENTER == mScaleType) { + // Center bitmap in view, no scaling. + mDrawMatrix = mMatrix; + mDrawMatrix.setTranslate( + (int) ((vwidth - dwidth) * 0.5f + 0.5f), + (int) ((vheight - dheight) * 0.5f + 0.5f)); + } else if (ScaleType.CENTER_CROP == mScaleType) { + mDrawMatrix = mMatrix; + + float scale; + float dx = 0, dy = 0; + + if (dwidth * vheight > vwidth * dheight) { + scale = (float) vheight / (float) dheight; + dx = (vwidth - dwidth * scale) * 0.5f; + } else { + scale = (float) vwidth / (float) dwidth; + dy = (vheight - dheight * scale) * 0.5f; + } + + mDrawMatrix.setScale(scale, scale); + mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f)); + } else if (ScaleType.CENTER_INSIDE == mScaleType) { + mDrawMatrix = mMatrix; + float scale; + float dx; + float dy; + + if (dwidth <= vwidth && dheight <= vheight) { + scale = 1.0f; + } else { + scale = Math.min((float) vwidth / (float) dwidth, + (float) vheight / (float) dheight); + } + + dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f); + dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f); + + mDrawMatrix.setScale(scale, scale); + mDrawMatrix.postTranslate(dx, dy); + } else { + // Generate the required transform. + mTempSrc.set(0, 0, dwidth, dheight); + mTempDst.set(0, 0, vwidth, vheight); + + mDrawMatrix = mMatrix; + mDrawMatrix.setRectToRect(mTempSrc, mTempDst, + scaleTypeToScaleToFit(mScaleType)); + } + } + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + Drawable d = mDrawable; + if (d != null && d.isStateful()) { + d.setState(getDrawableState()); + } + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mDrawable == null && mMovie == null) { + return; // couldn't resolve the URI + } + + if (mDrawableWidth == 0 || mDrawableHeight == 0) { + return; // nothing to draw (empty bounds) + } + + int pLeft = getPaddingLeft(); + + if (mDrawMatrix == null && getPaddingTop() == 0 && pLeft == 0) { + if (mDrawable != null) + mDrawable.draw(canvas); + else + drawMovieFrame(canvas); + } else { + int saveCount = canvas.getSaveCount(); + canvas.save(); + + if (mCropToPadding) { + final int scrollX = getScrollX(); + final int scrollY = getScrollY(); + canvas.clipRect(scrollX + pLeft, scrollY + getPaddingTop(), + scrollX + getRight() - getLeft() - getPaddingRight(), scrollY + + getBottom() - getTop() - getPaddingBottom()); + } + + canvas.translate(pLeft, getPaddingTop()); + + if (mDrawMatrix != null) { + canvas.concat(mDrawMatrix); + } + if (mDrawable != null) + mDrawable.draw(canvas); + else + drawMovieFrame(canvas); + canvas.restoreToCount(saveCount); + } + } + + @SuppressLint("NewApi") + private void drawMovieFrame(Canvas canvas) { + // if (!mPaused) + updateAnimationTime(); + + mMovie.setTime(mCurrentAnimationTime); + mMovie.draw(canvas, 0, 0); + + // if (!mPaused) + if (Build.VERSION.SDK_INT < 16) { + invalidate(); + } else { + postInvalidateOnAnimation(); + } + } + + private void updateAnimationTime() { + long now = android.os.SystemClock.uptimeMillis(); + + if (mMovieStart == 0) { + mMovieStart = now; + } + + int dur = mMovie.duration(); + + if (dur == 0) { + dur = DEFAULT_MOVIEW_DURATION; + } + + mCurrentAnimationTime = (int) ((now - mMovieStart) % dur); + } + + /** + *

+ * Return the offset of the widget's text baseline from the widget's top + * boundary. + *

+ * + * @return the offset of the baseline within the widget's bounds or -1 if + * baseline alignment is not supported. + */ + @Override + @ViewDebug.ExportedProperty(category = "layout") + public int getBaseline() { + if (mBaselineAlignBottom) { + return getMeasuredHeight(); + } else { + return mBaseline; + } + } + + /** + *

+ * Set the offset of the widget's text baseline from the widget's top + * boundary. This value is overridden by the + * {@link #setBaselineAlignBottom(boolean)} property. + *

+ * + * @param baseline + * The baseline to use, or -1 if none is to be provided. + * + * @see #setBaseline(int) + * @attr ref android.R.styleable#ImageView_baseline + */ + public void setBaseline(int baseline) { + if (mBaseline != baseline) { + mBaseline = baseline; + requestLayout(); + } + } + + /** + * Set whether to set the baseline of this view to the bottom of the view. + * Setting this value overrides any calls to setBaseline. + * + * @param aligned + * If true, the image view will be baseline aligned with based on + * its bottom edge. + * + * @attr ref android.R.styleable#ImageView_baselineAlignBottom + */ + public void setBaselineAlignBottom(boolean aligned) { + if (mBaselineAlignBottom != aligned) { + mBaselineAlignBottom = aligned; + requestLayout(); + } + } + + /** + * Return whether this view's baseline will be considered the bottom of the + * view. + * + * @see #setBaselineAlignBottom(boolean) + */ + public boolean getBaselineAlignBottom() { + return mBaselineAlignBottom; + } + + /** + * Set a tinting option for the image. + * + * @param color + * Color tint to apply. + * @param mode + * How to apply the color. The standard mode is + * {@link PorterDuff.Mode#SRC_ATOP} + * + * @attr ref android.R.styleable#ImageView_tint + */ + public final void setColorFilter(int color, PorterDuff.Mode mode) { + setColorFilter(new PorterDuffColorFilter(color, mode)); + } + + /** + * Set a tinting option for the image. Assumes + * {@link PorterDuff.Mode#SRC_ATOP} blending mode. + * + * @param color + * Color tint to apply. + * @attr ref android.R.styleable#ImageView_tint + */ + public final void setColorFilter(int color) { + setColorFilter(color, PorterDuff.Mode.SRC_ATOP); + } + + public final void clearColorFilter() { + setColorFilter(null); + } + + /** + * @hide Candidate for future API inclusion + */ + public final void setXfermode(Xfermode mode) { + if (mXfermode != mode) { + mXfermode = mode; + mColorMod = true; + applyColorMod(); + invalidate(); + } + } + + /** + * Returns the active color filter for this ImageView. + * + * @return the active color filter for this ImageView + * + * @see #setColorFilter(android.graphics.ColorFilter) + */ + public ColorFilter getColorFilter() { + return mColorFilter; + } + + /** + * Apply an arbitrary colorfilter to the image. + * + * @param cf + * the colorfilter to apply (may be null) + * + * @see #getColorFilter() + */ + public void setColorFilter(ColorFilter cf) { + if (mColorFilter != cf) { + mColorFilter = cf; + mColorMod = true; + applyColorMod(); + invalidate(); + } + } + + /** + * Returns the alpha that will be applied to the drawable of this ImageView. + * + * @return the alpha that will be applied to the drawable of this ImageView + * + * @see #setImageAlpha(int) + */ + public int getImageAlpha() { + return mAlpha; + } + + /** + * Sets the alpha value that should be applied to the image. + * + * @param alpha + * the alpha value that should be applied to the image + * + * @see #getImageAlpha() + */ + public void setImageAlpha(int alpha) { + setAlpha(alpha); + } + + /** + * Sets the alpha value that should be applied to the image. + * + * @param alpha + * the alpha value that should be applied to the image + * + * @deprecated use #setImageAlpha(int) instead + */ + @Deprecated + public void setAlpha(int alpha) { + alpha &= 0xFF; // keep it legal + if (mAlpha != alpha) { + mAlpha = alpha; + mColorMod = true; + applyColorMod(); + invalidate(); + } + } + + private void applyColorMod() { + // Only mutate and apply when modifications have occurred. This should + // not reset the mColorMod flag, since these filters need to be + // re-applied if the Drawable is changed. + if (mDrawable != null && mColorMod) { + mDrawable = mDrawable.mutate(); + mDrawable.setColorFilter(mColorFilter); + // mDrawable.setXfermode(mXfermode); // Can't fix here + mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8); + } + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (mDrawable != null) { + mDrawable.setVisible(visibility == VISIBLE, false); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mDrawable != null) { + mDrawable.setVisible(getVisibility() == VISIBLE, false); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mDrawable != null) { + mDrawable.setVisible(false, false); + } + } + + @SuppressLint("NewApi") + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + if (Build.VERSION.SDK_INT < 14) { + return; + } + + super.onInitializeAccessibilityEvent(event); + event.setClassName(SuperImageView.class.getName()); + } + + @SuppressLint("NewApi") + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + if (Build.VERSION.SDK_INT < 14) { + return; + } + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(SuperImageView.class.getName()); + } } \ No newline at end of file diff --git a/src/com/hippo/ehviewer/widget/AutoWrapLayout.java b/src/com/hippo/ehviewer/widget/AutoWrapLayout.java index ab19575ae..3a2981732 100644 --- a/src/com/hippo/ehviewer/widget/AutoWrapLayout.java +++ b/src/com/hippo/ehviewer/widget/AutoWrapLayout.java @@ -10,7 +10,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.View; import android.view.ViewGroup; diff --git a/src/com/hippo/ehviewer/widget/DrawerLayout.java b/src/com/hippo/ehviewer/widget/DrawerLayout.java index c0cf6082e..cba72ceb7 100644 --- a/src/com/hippo/ehviewer/widget/DrawerLayout.java +++ b/src/com/hippo/ehviewer/widget/DrawerLayout.java @@ -35,7 +35,7 @@ import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewGroupCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.util.AttributeSet; -import android.util.Log; +import com.hippo.ehviewer.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; diff --git a/src/com/jakewharton/disklrucache/DiskLruCache.java b/src/com/jakewharton/disklrucache/DiskLruCache.java index d5f543005..4ae961c19 100644 --- a/src/com/jakewharton/disklrucache/DiskLruCache.java +++ b/src/com/jakewharton/disklrucache/DiskLruCache.java @@ -41,7 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; -import android.util.Log; +import com.hippo.ehviewer.util.Log; /** * A cache that uses a bounded amount of space on a filesystem. Each cache entry