mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1065485: Don't decode-to-encode default favicons. r=rnewman
--HG-- rename : mobile/android/base/resources/drawable-mdpi/bookmarkdefaults_favicon_addons.png => mobile/android/base/resources/raw/bookmarkdefaults_favicon_addons.png rename : mobile/android/base/resources/drawable-mdpi/bookmarkdefaults_favicon_marketplace.png => mobile/android/base/resources/raw/bookmarkdefaults_favicon_marketplace.png rename : mobile/android/base/resources/drawable-mdpi/bookmarkdefaults_favicon_support.png => mobile/android/base/resources/raw/bookmarkdefaults_favicon_support.png
This commit is contained in:
parent
d7c79fa1a7
commit
a1ac9b703d
@ -7,6 +7,9 @@ package org.mozilla.gecko.db;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.IllegalAccessException;
|
||||||
|
import java.lang.NoSuchFieldException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -53,12 +56,18 @@ import android.net.Uri;
|
|||||||
import android.provider.Browser;
|
import android.provider.Browser;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import org.mozilla.gecko.util.IOUtils;
|
||||||
|
|
||||||
|
import static org.mozilla.gecko.util.IOUtils.ConsumedInputStream;
|
||||||
|
import static org.mozilla.gecko.favicons.LoadFaviconTask.DEFAULT_FAVICON_BUFFER_SIZE;
|
||||||
|
|
||||||
public class LocalBrowserDB {
|
public class LocalBrowserDB {
|
||||||
// Calculate these once, at initialization. isLoggable is too expensive to
|
// Calculate these once, at initialization. isLoggable is too expensive to
|
||||||
// have in-line in each log call.
|
// have in-line in each log call.
|
||||||
private static final String LOGTAG = "GeckoLocalBrowserDB";
|
private static final String LOGTAG = "GeckoLocalBrowserDB";
|
||||||
private static final Integer FAVICON_ID_NOT_FOUND = Integer.MIN_VALUE;
|
|
||||||
|
// Sentinel value used to indicate a failure to locate an ID for a default favicon.
|
||||||
|
private static final int FAVICON_ID_NOT_FOUND = Integer.MIN_VALUE;
|
||||||
|
|
||||||
private static final boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG);
|
private static final boolean logDebug = Log.isLoggable(LOGTAG, Log.DEBUG);
|
||||||
protected static void debug(String message) {
|
protected static void debug(String message) {
|
||||||
@ -185,11 +194,20 @@ public class LocalBrowserDB {
|
|||||||
final ContentValues bookmarkValue = createBookmark(now, title, url, pos++, folderID);
|
final ContentValues bookmarkValue = createBookmark(now, title, url, pos++, folderID);
|
||||||
bookmarkValues.add(bookmarkValue);
|
bookmarkValues.add(bookmarkValue);
|
||||||
|
|
||||||
Bitmap icon = getDefaultFaviconFromPath(context, name);
|
ConsumedInputStream faviconStream = getDefaultFaviconFromDrawable(context, name);
|
||||||
if (icon == null) {
|
if (faviconStream == null) {
|
||||||
icon = getDefaultFaviconFromDrawable(context, name);
|
faviconStream = getDefaultFaviconFromPath(context, name);
|
||||||
}
|
}
|
||||||
if (icon == null) {
|
|
||||||
|
if (faviconStream == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the event that truncating the buffer fails, give up and move on.
|
||||||
|
byte[] icon;
|
||||||
|
try {
|
||||||
|
icon = faviconStream.getTruncatedData();
|
||||||
|
} catch (OutOfMemoryError e) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,11 +222,7 @@ public class LocalBrowserDB {
|
|||||||
bookmarkValue.put(Bookmarks.FAVICON_ID, faviconID);
|
bookmarkValue.put(Bookmarks.FAVICON_ID, faviconID);
|
||||||
faviconValues.add(iconValue);
|
faviconValues.add(iconValue);
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException e) {
|
||||||
Log.wtf(LOGTAG, "Reflection failure.", e);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
Log.wtf(LOGTAG, "Reflection failure.", e);
|
|
||||||
} catch (NoSuchFieldException e) {
|
|
||||||
Log.wtf(LOGTAG, "Reflection failure.", e);
|
Log.wtf(LOGTAG, "Reflection failure.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,13 +306,13 @@ public class LocalBrowserDB {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final String iconData = bookmark.getString("icon");
|
final String iconData = bookmark.getString("icon");
|
||||||
final Bitmap icon = BitmapUtils.getBitmapFromDataURI(iconData);
|
|
||||||
|
byte[] icon = BitmapUtils.getBytesFromDataURI(iconData);
|
||||||
if (icon == null) {
|
if (icon == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ContentValues iconValue = createFavicon(url, icon);
|
final ContentValues iconValue = createFavicon(url, icon);
|
||||||
|
|
||||||
if (iconValue == null) {
|
if (iconValue == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -361,21 +375,11 @@ public class LocalBrowserDB {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ContentValues createFavicon(final String url, final Bitmap icon) {
|
private static ContentValues createFavicon(final String url, final byte[] icon) {
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
ContentValues iconValues = new ContentValues();
|
ContentValues iconValues = new ContentValues();
|
||||||
iconValues.put(Favicons.PAGE_URL, url);
|
iconValues.put(Favicons.PAGE_URL, url);
|
||||||
|
iconValues.put(Favicons.DATA, icon);
|
||||||
|
|
||||||
byte[] data = null;
|
|
||||||
if (icon.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
|
|
||||||
data = stream.toByteArray();
|
|
||||||
} else {
|
|
||||||
Log.w(LOGTAG, "Favicon compression failed.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
iconValues.put(Favicons.DATA, data);
|
|
||||||
return iconValues;
|
return iconValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,45 +408,53 @@ public class LocalBrowserDB {
|
|||||||
return bookmark.getString(property);
|
return bookmark.getString(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Bitmap getDefaultFaviconFromPath(Context context, String name) {
|
private static int getFaviconId(String name) {
|
||||||
Class<?> stringClass = R.string.class;
|
|
||||||
try {
|
try {
|
||||||
// Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_*
|
Class<?> drawablesClass = R.raw.class;
|
||||||
Field faviconField = stringClass.getField(name.replace("_title_", "_favicon_"));
|
|
||||||
if (faviconField == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int faviconId = faviconField.getInt(null);
|
|
||||||
String path = context.getString(faviconId);
|
|
||||||
|
|
||||||
String apkPath = context.getPackageResourcePath();
|
// Look for a favicon with the id R.raw.bookmarkdefaults_favicon_*.
|
||||||
File apkFile = new File(apkPath);
|
Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_"));
|
||||||
String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path;
|
faviconField.setAccessible(true);
|
||||||
return GeckoJarReader.getBitmap(context.getResources(), bitmapPath);
|
|
||||||
} catch (java.lang.IllegalAccessException ex) {
|
return faviconField.getInt(null);
|
||||||
Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex);
|
} catch (IllegalAccessException | NoSuchFieldException ex) {
|
||||||
} catch (java.lang.NoSuchFieldException ex) {
|
Log.wtf(LOGTAG, "Reflection error fetching favicon: " + name, ex);
|
||||||
// If the field does not exist, that means we intend to load via a drawable.
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
Log.e(LOGTAG, "Failed to find favicon resource ID for " + name);
|
||||||
|
return FAVICON_ID_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Bitmap getDefaultFaviconFromDrawable(Context context, String name) {
|
/**
|
||||||
Class<?> drawablesClass = R.drawable.class;
|
* Load a favicon from the omnijar.
|
||||||
try {
|
* @return A ConsumedInputStream containing the bytes loaded from omnijar. This must be a format
|
||||||
// Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_*
|
* compatible with the favicon decoder (most probably a PNG or ICO file).
|
||||||
Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_"));
|
*/
|
||||||
if (faviconField == null) {
|
private static ConsumedInputStream getDefaultFaviconFromPath(Context context, String name) {
|
||||||
return null;
|
int faviconId = getFaviconId(name);
|
||||||
}
|
if (faviconId == FAVICON_ID_NOT_FOUND) {
|
||||||
int faviconId = faviconField.getInt(null);
|
return null;
|
||||||
return BitmapUtils.decodeResource(context, faviconId);
|
|
||||||
} catch (java.lang.IllegalAccessException ex) {
|
|
||||||
Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex);
|
|
||||||
} catch (java.lang.NoSuchFieldException ex) {
|
|
||||||
Log.wtf(LOGTAG, "No field, and presumably no drawable, for " + name);
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
String path = context.getString(faviconId);
|
||||||
|
|
||||||
|
String apkPath = context.getPackageResourcePath();
|
||||||
|
File apkFile = new File(apkPath);
|
||||||
|
String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path;
|
||||||
|
|
||||||
|
InputStream iStream = GeckoJarReader.getStream(bitmapPath);
|
||||||
|
|
||||||
|
return IOUtils.readFully(iStream, DEFAULT_FAVICON_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConsumedInputStream getDefaultFaviconFromDrawable(Context context, String name) {
|
||||||
|
int faviconId = getFaviconId(name);
|
||||||
|
if (faviconId == FAVICON_ID_NOT_FOUND) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream iStream = context.getResources().openRawResource(faviconId);
|
||||||
|
return IOUtils.readFully(iStream, DEFAULT_FAVICON_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate cached data
|
// Invalidate cached data
|
||||||
|
@ -20,6 +20,7 @@ import org.mozilla.gecko.db.BrowserDB;
|
|||||||
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
|
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
|
||||||
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
|
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
|
||||||
import org.mozilla.gecko.util.GeckoJarReader;
|
import org.mozilla.gecko.util.GeckoJarReader;
|
||||||
|
import org.mozilla.gecko.util.IOUtils;
|
||||||
import org.mozilla.gecko.util.ThreadUtils;
|
import org.mozilla.gecko.util.ThreadUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -32,6 +33,8 @@ import java.util.LinkedList;
|
|||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.mozilla.gecko.util.IOUtils.ConsumedInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing the asynchronous task to load a Favicon which is not currently in the in-memory
|
* Class representing the asynchronous task to load a Favicon which is not currently in the in-memory
|
||||||
* cache.
|
* cache.
|
||||||
@ -49,7 +52,7 @@ public class LoadFaviconTask {
|
|||||||
private static final int MAX_REDIRECTS_TO_FOLLOW = 5;
|
private static final int MAX_REDIRECTS_TO_FOLLOW = 5;
|
||||||
// The default size of the buffer to use for downloading Favicons in the event no size is given
|
// The default size of the buffer to use for downloading Favicons in the event no size is given
|
||||||
// by the server.
|
// by the server.
|
||||||
private static final int DEFAULT_FAVICON_BUFFER_SIZE = 25000;
|
public static final int DEFAULT_FAVICON_BUFFER_SIZE = 25000;
|
||||||
|
|
||||||
private static final AtomicInteger nextFaviconLoadId = new AtomicInteger(0);
|
private static final AtomicInteger nextFaviconLoadId = new AtomicInteger(0);
|
||||||
private final Context context;
|
private final Context context;
|
||||||
@ -281,42 +284,17 @@ public class LoadFaviconTask {
|
|||||||
bufferSize = DEFAULT_FAVICON_BUFFER_SIZE;
|
bufferSize = DEFAULT_FAVICON_BUFFER_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a buffer to hold the raw favicon data downloaded.
|
// Read the InputStream into a byte[].
|
||||||
byte[] buffer = new byte[bufferSize];
|
ConsumedInputStream result = IOUtils.readFully(entity.getContent(), bufferSize);
|
||||||
|
if (result == null) {
|
||||||
// The offset of the start of the buffer's free space.
|
return null;
|
||||||
int bPointer = 0;
|
|
||||||
|
|
||||||
// The quantity of bytes the last call to read yielded.
|
|
||||||
int lastRead = 0;
|
|
||||||
InputStream contentStream = entity.getContent();
|
|
||||||
try {
|
|
||||||
// Fully read the entity into the buffer - decoding of streams is not supported
|
|
||||||
// (and questionably pointful - what would one do with a half-decoded Favicon?)
|
|
||||||
while (lastRead != -1) {
|
|
||||||
// Read as many bytes as are currently available into the buffer.
|
|
||||||
lastRead = contentStream.read(buffer, bPointer, buffer.length - bPointer);
|
|
||||||
bPointer += lastRead;
|
|
||||||
|
|
||||||
// If buffer has overflowed, double its size and carry on.
|
|
||||||
if (bPointer == buffer.length) {
|
|
||||||
bufferSize *= 2;
|
|
||||||
byte[] newBuffer = new byte[bufferSize];
|
|
||||||
|
|
||||||
// Copy the contents of the old buffer into the new buffer.
|
|
||||||
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
contentStream.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Having downloaded the image, decode it.
|
// Having downloaded the image, decode it.
|
||||||
return FaviconDecoder.decodeFavicon(buffer, 0, bPointer + 1);
|
return FaviconDecoder.decodeFavicon(result.getData(), 0, result.consumedLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadFavicon tasks are performed on a unique background executor thread
|
// LoadFaviconTasks are performed on a unique background executor thread
|
||||||
// to avoid network blocking.
|
// to avoid network blocking.
|
||||||
public final void execute() {
|
public final void execute() {
|
||||||
try {
|
try {
|
||||||
|
@ -341,16 +341,33 @@ public final class BitmapUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
|
byte[] raw = getBytesFromDataURI(dataURI);
|
||||||
try {
|
if (raw == null || raw.length == 0) {
|
||||||
byte[] raw = Base64.decode(base64, Base64.DEFAULT);
|
return null;
|
||||||
return BitmapUtils.decodeByteArray(raw);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(LOGTAG, "exception decoding bitmap from data URI: " + dataURI, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return decodeByteArray(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a byte[] containing the bytes in a given base64 string, or null if this is not a valid
|
||||||
|
* base64 string.
|
||||||
|
*/
|
||||||
|
public static byte[] getBytesFromBase64(String base64) {
|
||||||
|
try {
|
||||||
|
return Base64.decode(base64, Base64.DEFAULT);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOGTAG, "exception decoding bitmap from data URI: " + base64, e);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] getBytesFromDataURI(String dataURI) {
|
||||||
|
final String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
|
||||||
|
return getBytesFromBase64(base64);
|
||||||
|
}
|
||||||
|
|
||||||
public static Bitmap getBitmapFromDrawable(Drawable drawable) {
|
public static Bitmap getBitmapFromDrawable(Drawable drawable) {
|
||||||
if (drawable instanceof BitmapDrawable) {
|
if (drawable instanceof BitmapDrawable) {
|
||||||
return ((BitmapDrawable) drawable).getBitmap();
|
return ((BitmapDrawable) drawable).getBitmap();
|
||||||
|
@ -60,6 +60,7 @@ gujar.sources += [
|
|||||||
'util/HardwareUtils.java',
|
'util/HardwareUtils.java',
|
||||||
'util/INIParser.java',
|
'util/INIParser.java',
|
||||||
'util/INISection.java',
|
'util/INISection.java',
|
||||||
|
'util/IOUtils.java',
|
||||||
'util/JSONUtils.java',
|
'util/JSONUtils.java',
|
||||||
'util/MenuUtils.java',
|
'util/MenuUtils.java',
|
||||||
'util/NativeEventListener.java',
|
'util/NativeEventListener.java',
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
111
mobile/android/base/util/IOUtils.java
Normal file
111
mobile/android/base/util/IOUtils.java
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.util;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static helper class containing useful methods for manipulating IO objects.
|
||||||
|
*/
|
||||||
|
public class IOUtils {
|
||||||
|
private static final String LOGTAG = "GeckoIOUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of consuming an input stream, holding the returned data as well
|
||||||
|
* as the length of the data returned.
|
||||||
|
* The byte[] is not guaranteed to be trimmed to the size of the data acquired from the stream:
|
||||||
|
* hence the need for the length field. This strategy avoids the need to copy the data into a
|
||||||
|
* trimmed buffer after consumption.
|
||||||
|
*/
|
||||||
|
public static class ConsumedInputStream {
|
||||||
|
public final int consumedLength;
|
||||||
|
// Only reassigned in getTruncatedData.
|
||||||
|
private byte[] consumedData;
|
||||||
|
|
||||||
|
public ConsumedInputStream(int consumedLength, byte[] consumedData) {
|
||||||
|
this.consumedLength = consumedLength;
|
||||||
|
this.consumedData = consumedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data trimmed to the length of the actual payload read, caching the result.
|
||||||
|
*/
|
||||||
|
public byte[] getTruncatedData() {
|
||||||
|
if (consumedData.length == consumedLength) {
|
||||||
|
return consumedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumedData = truncateBytes(consumedData, consumedLength);
|
||||||
|
return consumedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return consumedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully read an InputStream into a byte array.
|
||||||
|
* @param iStream the InputStream to consume.
|
||||||
|
* @param bufferSize The initial size of the buffer to allocate. It will be grown as
|
||||||
|
* needed, but if the caller knows something about the InputStream then
|
||||||
|
* passing a good value here can improve performance.
|
||||||
|
*/
|
||||||
|
public static ConsumedInputStream readFully(InputStream iStream, int bufferSize) {
|
||||||
|
// Allocate a buffer to hold the raw data downloaded.
|
||||||
|
byte[] buffer = new byte[bufferSize];
|
||||||
|
|
||||||
|
// The offset of the start of the buffer's free space.
|
||||||
|
int bPointer = 0;
|
||||||
|
|
||||||
|
// The quantity of bytes the last call to read yielded.
|
||||||
|
int lastRead = 0;
|
||||||
|
try {
|
||||||
|
// Fully read the data into the buffer.
|
||||||
|
while (lastRead != -1) {
|
||||||
|
// Read as many bytes as are currently available into the buffer.
|
||||||
|
lastRead = iStream.read(buffer, bPointer, buffer.length - bPointer);
|
||||||
|
bPointer += lastRead;
|
||||||
|
|
||||||
|
// If buffer has overflowed, double its size and carry on.
|
||||||
|
if (bPointer == buffer.length) {
|
||||||
|
bufferSize *= 2;
|
||||||
|
byte[] newBuffer = new byte[bufferSize];
|
||||||
|
|
||||||
|
// Copy the contents of the old buffer into the new buffer.
|
||||||
|
System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
|
||||||
|
buffer = newBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConsumedInputStream(bPointer + 1, buffer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOGTAG, "Error consuming input stream.", e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
iStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(LOGTAG, "Error closing input stream.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Truncate a given byte[] to a given length. Returns a new byte[] with the first length many
|
||||||
|
* bytes of the input.
|
||||||
|
*/
|
||||||
|
public static byte[] truncateBytes(byte[] bytes, int length) {
|
||||||
|
byte[] newBytes = new byte[length];
|
||||||
|
System.arraycopy(bytes, 0, newBytes, 0, length);
|
||||||
|
|
||||||
|
return newBytes;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user