I'm using Dropbox Android SDK for getting photos and show it into a GridView.
Currently, I'm using Picasso for adapter with another images resources like GPhotos, Facebook... too.
How can I get downloadable link from Dropbox's file? Or How to use Picasso load Dropbox's image?
This is the way I get the Dropbox image:
List<Entry> listEntry = mApi.search("/photos", ".jpg", 0, false);
int i = 0;
for (Entry entry : listEntry) {
if (this.isRemoving()) { // Check if fragment is being removed
return;
}
if (!entry.isDir) { // Check if this entry is dir or file
i++;
Log.e(TAG, entry + " --- " + entry.fileName() + " --- "
+ entry.parentPath() + " --- " + entry.modified);
final DropboxLink shareLink = mApi.share(entry.parentPath()
+ entry.fileName());
url = shareLink.url;
Log.v(TAG, "shareLink: " + shareLink.url);
// The file name I get is ImageName.JPG and path is /Photos
// With share() the url I get looks like https://db.tt/xxxxxxxx
}
}
I can't offer insight about using Picasso, but if you can accept the file data directly, using getFile or getThumbnail instead of share would be better.
If you do need a URL that Picasso can directly download from, you should use the media method instead.
Thanks #Greg for suggestion, i have a little notice for using Picasso with Dropbox SDK.
When use media i have to use option TRUE in SSL for success loading image from Picasso, if FALSE Picasso cannot load links.
There is a sample from dropbox about this, try to check this link dropbox sample
there is 2 files that you need to see PicassoClient.java And FileThumbnailRequestHandler.java
here's the code :
PicassoClient Class :
public class PicassoClient {
private static Picasso sPicasso;
public static void init(Context context, DbxClientV2 dbxClient) {
// Configure picasso to know about special thumbnail requests
sPicasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(context))
.addRequestHandler(new FileThumbnailRequestHandler(dbxClient))
.build();
}
public static Picasso getPicasso() {
return sPicasso;
}
}
FileThumbnailRequestHandler class :
public class FileThumbnailRequestHandler extends RequestHandler {
private static final String SCHEME = "dropbox";
private static final String HOST = "dropbox";
private final DbxClientV2 mDbxClient;
public FileThumbnailRequestHandler(DbxClientV2 dbxClient) {
mDbxClient = dbxClient;
}
/**
* Builds a {#link Uri} for a Dropbox file thumbnail suitable for handling by this handler
*/
public static Uri buildPicassoUri(FileMetadata file) {
return new Uri.Builder()
.scheme(SCHEME)
.authority(HOST)
.path(file.getPathLower()).build();
}
#Override
public boolean canHandleRequest(Request data) {
return SCHEME.equals(data.uri.getScheme()) && HOST.equals(data.uri.getHost());
}
#Override
public Result load(Request request, int networkPolicy) throws IOException {
try {
DbxDownloader<FileMetadata> downloader =
mDbxClient.files().getThumbnailBuilder(request.uri.getPath())
.withFormat(ThumbnailFormat.JPEG)
.withSize(ThumbnailSize.W1024H768)
.start();
return new Result(downloader.getInputStream(), Picasso.LoadedFrom.NETWORK);
} catch (DbxException e) {
throw new IOException(e);
}
}
}
You just need to import those 2 classes that i mentioned above then get the sPicasso object then you're ready to use it . :)
Related
I'm trying to preview (thumbnail) PDF documents that are remotely, using the Glide library from bumptech, version 4.8.0.
To achieve this, following the excellent tutorial Writing a custom ModelLoader, I've written a custom ModelLoader, a custom DataFetcher for the buildLoadData method; added the AppGlideModule, implemented ModelLoaderFactory and registered my ModelLoader.
Inside the DataFetcher I've added some logic to process the following two cases:
The content is an image. Works like a charm!
The content is a PDF document. W/Glide: Load failed for https://www.testserver.net/folder/sample.pdf with size [522x600]
class com.bumptech.glide.load.engine.GlideException: Failed to load resource
One approach has been to download the PDF file locally, and then render it (this DOES work), but it adds a considerable delay when having to download a file from a url and copy it locally; on the other hand, it doesn't take advantage of Glide's use of the cache.
Should I add another extra ModelLoader to use OkHttp3 instead of Volley (default)?
Any ideas? Thanks in advance!
public final class MyModelLoader implements ModelLoader<File, InputStream> {
private final Context context;
public MyModelLoader(Context context) {
this.context = context;
}
#NonNull
#Override
public ModelLoader.LoadData<InputStream> buildLoadData(#NonNull File model, int width, int height, #NonNull Options options) {
return new ModelLoader.LoadData<>(new ObjectKey(model), new MyDataFetcher(context, model));
}
#Override
public boolean handles(#NonNull File file) {
return true;
}
}
public class MyDataFetcher implements DataFetcher<InputStream> {
#SuppressWarnings("FieldCanBeLocal")
private final Context context;
private final File file;
private InputStream inputStream;
public MyDataFetcher(Context context, File file) {
this.context = context;
this.file = file;
}
#Override
public void loadData(#NonNull Priority priority, #NonNull DataCallback<? super InputStream> callback) {
try {
if (isPdf(file)) {
//We have a PDF document in "file" -- fail (if document is remote)
try {
//render first page of document PDF to bitmap, and pass to method 'onDataReady' as a InputStream
PdfRenderer pdfRenderer = new PdfRenderer(ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
PdfRenderer.Page page = pdfRenderer.openPage(0);
int width = 2048;
int height = (page.getHeight() * (width / page.getWidth()));
Bitmap pageBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
page.render(pageBitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
pageBitmap.compress(Bitmap.CompressFormat.PNG, 0, outputStream);
ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray());
callback.onDataReady(stream);
} catch (IOException ignored) {}
} else {
//We have an image in "file" -- OK
FileInputStream fileInputStream = new FileInputStream(file);
callback.onDataReady(fileInputStream);
}
} catch (IOException ignored) {}
}
// checks for file content
public boolean isPdf(File f) throws IOException {
URLConnection connection = f.toURL().openConnection();
String mimeType = connection.getContentType();
return mimeType.equals("application/pdf");
}
#Override
public void cleanup() {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ignored) {}
}
}
#Override
public void cancel() {
//empty
}
#NonNull
#Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
#NonNull
#Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}
public class MyModelLoaderFactory implements ModelLoaderFactory<File, InputStream> {
private final Context context;
public MyModelLoaderFactory(Context context) {
this.context = context;
}
#NonNull
#Override
public ModelLoader<File, InputStream> build(#NonNull MultiModelLoaderFactory multiFactory) {
return new MyModelLoader(context);
}
#Override
public void teardown() {
//empty
}
}
#GlideModule public class MyAppGlideModule extends AppGlideModule {
#Override
public void registerComponents(#NonNull Context context, #NonNull Glide glide, Registry registry) {
registry.prepend(File.class, InputStream.class, new MyModelLoaderFactory(context));
}
}
Finally, after all of the above, the call is of the form:
GlideApp.with(image.getContext()).load("resource_url").into(image);
Where "resouce_url" could be: https://www.testserver.net/folder/sample.pdf, eg.
You can show image and video thumbnails using Glide library if you want to show a pdf thumbnail you need to use the library for it e.g. Android PdfViewer. Then instead of using ImageView use PdfView and load only the first page instead of all e.g. .pages(0). That's it.
In your xml/layout
<com.github.barteksc.pdfviewer.PDFView
android:id="#+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
In your class
pdfView.fromBytes(bytes)
.pages(0) //show only first page
.spacing(0)
.swipeHorizontal(false)
.enableSwipe(false)
.load();
Well, I've finally found a method to solve the problem, from another perspective.
Instead of preprocessing the pdf on client-side, using a Glide ModelLoader, I've come up with an outlier but effective subterfuge: do it on server-side.
By means of the php Imagick extension, I've modified the server api so that it automatically generates a thumbnail on the server (in the "upload.php" module), same path where the pdf is saved. Thus, assuming that we have the pdf already loaded, we do the following:
// Create thumbnail, if pdf
if ($ext == 'pdf') {
$im = new imagick($base_path.$next_id["next"].".".$ext.'[0]');
$im->setImageFormat('jpg');
header('Content-Type: image/jpeg');
file_put_contents($base_path.$next_id["next"]."_thumbnail.jpg", $im);
}
(with the help of this link on using Imagick to convert pdf to jpg: How to convert pdf to a preview image in PHP).
On the other hand, when a record is deleted, the attachments that it may have associated must also be deleted, if any. This makes it necessary to also delete the thumbnail, in the same act, as shown below:
// Remove uploaded file from server
unlink($base_path.$id.".".$ext);
// If pdf, we also have to remove the thumbnail
if ($ext == 'pdf') {
unlink($base_path.$id."_thumbnail.jpg");
}
Now we have a set of files, some jpg/png and another pdf; but this is indifferent for Glide, which will only show jpg/png images, without any problem, even if they are remotely; and of course very quickly. The code on client-side is:
/* Have a pdf file, eg. "sample.pdf", Glide will load a file
with this name: "sample_thumbnail.jpg",
that contains first page of pdf file (preview)
(A single tap on one element will download the file
and launch an intent to display it)
*/
if (item.getType().equals("jpg") || item.getType().equals("jpeg") || item.getType().equals("png")) {
Glide.with(image.getContext()).load(item.getPath()).diskCacheStrategy(DiskCacheStrategy.RESOURCE).into(image);
} else if (item.getType().equals("pdf")) {
Glide.with(image.getContext()).load(getName(item.getPath()) + "_thumbnail.jpg").diskCacheStrategy(DiskCacheStrategy.RESOURCE).into(image);
} else {
throw new Exception("File type not supported");
}
Although maybe not everyone can have Imagick on the server, in my case this solution has worked wonderfully.
I have a dash manifest which needs to be loaded in ExoPlayer, which is also DRM protected.
I can't seem to find any example as to how to achieve that other than using JWPlayer Android
which did not work. executeProvisionRequest or executeKeyRequest was never called on loading my content.
Is there a simpler way to do this with ExoPlayer?
PlaylistItem content = new PlaylistItem.Builder()
.file("MY_MANIFEST_FILE_PATH")
.mediaDrmCallback(new WidevineMediaDrmCallback())
.build();
mPlayerView.load(content);
public class WidevineMediaDrmCallback implements MediaDrmCallback {
#Override
public byte[] executeProvisionRequest(UUID uuid, ExoMediaDrm.ProvisionRequest provisionRequest) throws Exception {
String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
return Util.executePost(url, null, null);
}
#Override
public byte[] executeKeyRequest(UUID uuid, ExoMediaDrm.KeyRequest request) throws Exception {
String url = request.getLicenseServerUrl();
if (TextUtils.isEmpty(url)) {
url = defaultUri;
}
return Util.executePost(url, request.getData(), null);
}
}
So I'm currently investigating the possibility to replace a lot of custom image loading AsyncTasks in a project, with the Picasso library and it seems promising. However there is one issue that I'm not completely sure how to solve using Picasso.
In this case we are downloading Album Art for music tracks, however when our ListView is to be shown we only have the track ids. So first we have to lookup an Album Art URL based on the track id and then load it into an ImageView. Currently we have an AsyncTask where we in doInBackground() first lookup the image URL and then load a Bitmap from it, which is then pasted to onPostExecute.
Is there any way to make this pre-lookup using Picasso or will we have to wrap the Picasso call in a AsyncTask that first perform the lookup (and feels like it kinda defeats the purpose).
Update: How it works now:
private class AlbumArtTask extends AsyncTask<String, Bitmap, Bitmap> {
#Override
protected Bitmap doInBackground(String... strings) {
final String id = strings[0];
// This part is what I'm asking for, basically to be able to make an Async
// lookup of the url that will later be used by Picasso
final Uri uri = getAlbumArtUriFromTrackId(id);
// Creating the Bitmap and return it to be used in an ImageView
// This part is handled by Picasso
return bitmap = createBitmapFromUri(uri);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
// load image into ImageView
}
}
Update 2: Made up example of what I'm after
Picasso.with(mContext)
.load(new UriProvider() {
// Get the actual image URI here
public Uri getImageUri() {
return getAlbumArtUriFromTrackId(id);
}
})
// And load it here
.into(mImageView);
I had to solve the same problem for my app. I used OkHTTP interceptor to redirect the request.
This is my picassa declaration:
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new PhotoInterceptor());
Picasso picasso = new Picasso.Builder(context)
.downloader(new OkHttpDownloader(client))
.build();
This is a basic implementation of the interceptor:
public class PhotoInterceptor implements Interceptor {
Gson gson = new Gson();
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (response.isSuccessful()) {
Photo photo = gson.fromJson(response.body().charStream(), Photo.class);
if (photo!=null) {
request = request.newBuilder()
.url(photo.getPhoto_file_url())
.build();
response = chain.proceed(request);
}
}
return response;
}
}
You have to load images from your Album Art URL using Piccaso while your listView is created. Then on Detail screen of Track you have to call your actual image url to set it in imageview.
Edited
private class AlbumArtTask extends AsyncTask<String, Bitmap, Uri> {
ImageView mImageView;
Context mContext;
public AlbumArtTask(Context context,ImageView imageView){
this.mImageView=imageView;
this.mContext=context;
}
#Override
protected Uri doInBackground(String... strings) {
final String id = strings[0];
// This part is what I'm asking for, basically to be able to make an Async
// lookup of the url that will later be used by Picasso
final Uri uri = getAlbumArtUriFromTrackId(id);
// Creating the Bitmap and return it to be used in an ImageView
// This part is handled by Picasso
return uri;
}
#Override
protected void onPostExecute(Uri uri) {
// You have to pass imageview in constructor.
// load image into ImageView
Picasso.with(mContext).load(uri)/* And load it here*/.into(mImageView);
}
}
This question was asked here: Universal-Image-Loader: wrong Bitmaps are attached to ImageView
I am using the the latest, 1.9.3. I implemented the solution by having this in my application class:
#Override
public void onCreate() {
super.onCreate();
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder().resetViewBeforeLoading(true).build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
.defaultDisplayImageOptions(defaultOptions)
.build();
ImageLoader.getInstance().init(config);
}
Per Android-Universal-Image-Loader doesn't keep loaded images on scroll in gridview, I load the image like so in my adapter:
ImageAware imageAware = new ImageViewAware(viewHolder.profileIV, false);
ImageLoader.getInstance().displayImage(imgUrl, imageAware);
It still doesn't work; I'm wondering if it's because I have to make a call to get the image url since the api that supplied the model data did not include an image url.
So in my adapter's getView(), before I use imageloader with the image url, I do another asynch call to get the image url, like so:
APIclient.getImageJson(getContext(), googleUrl, new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONObject imgJson) {
try {
JSONObject responseDataValue = imgJson.getJSONObject("responseData");
JSONArray resultsValue = responseDataValue.getJSONArray("results");
JSONObject result = resultsValue.getJSONObject(0);
String imgUrl = result.getString("url");
ImageAware imageAware = new ImageViewAware(viewHolder.profileIV, false);
ImageLoader.getInstance().displayImage(imgUrl, imageAware);
//ImageLoader.getInstance().displayImage(imgUrl, viewHolder.profileIV);
}
catch(Exception e) {
e.printStackTrace();
}
}
google url here looks like: https://ajax.googleapis.com/ajax/services/search/images?rsz=1&start=1&v=1.0&q=%22barack%20obama%22
each row would have a different url since the names are different. I don't know if the problem is still a listview recycer problem that wasn't fixed in the universal image loader library or if the culprit lies in the additional network call. How can I make the thumbnails consistent with the data next to it?
I think it's because of async call APIclient.getImageJson(...) in getView(). It's unknown time when onSuccess() callback is fired so you can call ImageLoader.getInstance().displayImage(...) for already recycled ImageView.
I can propose you join these 2 async operations (get JSON by APIclient, load image by ImageLoader) following way. Implement own ImageDoanloader which will process google JSON URLs (https://ajax.googleapis.com/ajax/services/search/images?rsz=1&start=1&v=1.0&q=%22barack%20obama%22), extract image URL and load image.
Let's introduce our own URI scheme - "json". So we know that incoming URIs like "json://..." are correspond to JSON link.
Prepare own downloader:
public class JsonImageDownloader extends BaseImageDownloader {
public static final String SCHEME_JSON = "json";
public static final String SCHEME_JSON_PREFIX = SCHEME_JSON + "://";
public JsonImageDownloader(Context context) {
super(context);
}
public JsonImageDownloader(Context context, int connectTimeout, int readTimeout) {
super(context, connectTimeout, readTimeout);
}
#Override
public InputStream getStream(String uri, Object extra) throws IOException {
if (uri.startsWith(SCHEME_JSON_PREFIX)) {
String jsonUri = uri.substring(SCHEME_JSON_PREFIX.length());
JSONObject imgJson = APIclient.getImageJson(context, jsonUri); // sync JSON loading
try {
JSONObject responseDataValue = imgJson.getJSONObject("responseData");
JSONArray resultsValue = responseDataValue.getJSONArray("results");
JSONObject result = resultsValue.getJSONObject(0);
String imgUrl = result.getString("url");
return super.getStream(imgUrl, extra);
} catch (JSONException e) {
throw new RuntimeException(e);
}
} else {
return super.getStream(uri, extra);
}
}
}
Set it into configuration
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.imageDownloader(new JsonImageDownloader(context))
....
.build();
Use ImageLoader in getView(...) without using APIclient:
ImageAware imageAware = new ImageViewAware(viewHolder.profileIV, false);
ImageLoader.getInstance().displayImage("json://" + googleUrl, imageAware);
I'm trying to upload multiple images to a PHP server along with a few other parameters (strings), using the POST method. I'm using Multipart method. I added 4 libraries prescribed in various solutions (apachemime, httpclient, httpmime, httpcore), but my SDK doesn't recognise MultipartEntity, displaying the error: 'cannot be resolved to a type'. I've also tried MultipartEntityBuilder, but even that's showing the same error. I basically want to upload a Bitmap Arraylist to the server & show a progress bar simultaneously.
You should post some code with the issue so we can take a look and help you.
But if you want to try something a little bit simple you can use this library, AsyncHttpClient: http://loopj.com/android-async-http/
Using this library you could post multiple files like this:
private static AsyncHttpClient clientHttp = new AsyncHttpClient();
...
RequestParams params = new RequestParams();
final String TAG_FILE = "archivo1";
final String TAG_FILE_1 = "archivo2";
final String TAG_FILE_2 = "archivo3";
final String PATH_FILE_1 = ApplicationContext.getInstance().getFilesDir().getPath() + "/" + "file1.jpg";
final String PATH_FILE_2 = ApplicationContext.getInstance().getFilesDir().getPath() + "/" + "file2.jpg";
final String PATH_FILE_3 = ApplicationContext.getInstance().getFilesDir().getPath() + "/" + "file3.jpg";
try {
params.put(TAG_FILE, PATH_FILE_1);
params.put(TAG_FILE_1, PATH_FILE_2);
params.put(TAG_FILE_2, PATH_FILE_3);
params.put(TAG_PARAM, "SOME TEXT");
}
catch(FileNotFoundException e) {
//Manage your exception
}
final int DEFAULT_TIMEOUT = 30 * 1000;
clientHttp.setTimeout(DEFAULT_TIMEOUT);
clientHttp.post("http://somereceiver.php", params, new JsonHttpResponseHandler() {
#Override
public void onSuccess(JSONObject response) {
//Do your code on success
}
#Override
public void onStart() {
// Show your progress bar
}
#Override
public void onFinish() {
// Hide your progress bar
super.onFinish();
if(PATH_FILE_1 != null) {
File tem = new File(PATH_FILE_1);
if(tem.exists()) tem.delete();
}
if(PATH_FILE_2 != null) {
File tem = new File(PATH_FILE_2);
if(tem.exists()) tem.delete();
}
if(PATH_FILE_3 != null) {
File tem = new File(PATH_FILE_3);
if(tem.exists()) tem.delete();
}
}
});
You could also use the generic response type if you dont need json, so you get a string.
Hope this helps.