mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 12:53:55 +01:00
227 lines
9.1 KiB
Java
227 lines
9.1 KiB
Java
/**
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package fr.free.nrw.commons;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.drawable.BitmapDrawable;
|
|
import android.text.TextUtils;
|
|
import android.util.AttributeSet;
|
|
import android.view.View;
|
|
import android.widget.ImageView;
|
|
|
|
import com.android.volley.VolleyError;
|
|
import com.android.volley.toolbox.ImageLoader;
|
|
import com.android.volley.toolbox.ImageLoader.ImageContainer;
|
|
import com.android.volley.toolbox.ImageLoader.ImageListener;
|
|
|
|
import fr.free.nrw.commons.contributions.Contribution;
|
|
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
|
|
|
public class MediaWikiImageView extends ImageView {
|
|
|
|
private Media mMedia;
|
|
|
|
private ImageLoader mImageLoader;
|
|
|
|
private ImageContainer mImageContainer;
|
|
|
|
private View loadingView;
|
|
|
|
private boolean isThumbnail;
|
|
|
|
public MediaWikiImageView(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public MediaWikiImageView(Context context, AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
TypedArray actualAttrs = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MediaWikiImageView, 0, 0);
|
|
isThumbnail = actualAttrs.getBoolean(0, false);
|
|
actualAttrs.recycle();
|
|
}
|
|
|
|
public MediaWikiImageView(Context context, AttributeSet attrs, int defStyle) {
|
|
super(context, attrs, defStyle);
|
|
}
|
|
|
|
public void setMedia(Media media, ImageLoader imageLoader) {
|
|
this.mMedia = media;
|
|
mImageLoader = imageLoader;
|
|
loadImageIfNecessary(false);
|
|
}
|
|
|
|
public void setLoadingView(View loadingView) {
|
|
this.loadingView = loadingView;
|
|
}
|
|
|
|
public View getLoadingView() {
|
|
return loadingView;
|
|
}
|
|
|
|
private void loadImageIfNecessary(final boolean isInLayoutPass) {
|
|
loadImageIfNecessary(isInLayoutPass, false);
|
|
}
|
|
|
|
private void loadImageIfNecessary(final boolean isInLayoutPass, final boolean tryOriginal) {
|
|
int width = getWidth();
|
|
int height = getHeight();
|
|
|
|
// if the view's bounds aren't known yet, hold off on loading the image.
|
|
if (width == 0 && height == 0) {
|
|
return;
|
|
}
|
|
|
|
if(mMedia == null) {
|
|
return;
|
|
}
|
|
|
|
// Do not count for density when loading thumbnails.
|
|
// FIXME: Use another 'algorithm' that doesn't punish low res devices
|
|
if(isThumbnail) {
|
|
float dpFactor = Math.max(getResources().getDisplayMetrics().density, 1.0f);
|
|
width = (int) (width / dpFactor);
|
|
height = (int) (height / dpFactor);
|
|
}
|
|
|
|
final String mUrl;
|
|
if(tryOriginal) {
|
|
mUrl = mMedia.getImageUrl();
|
|
} else {
|
|
// Round it to the nearest 320
|
|
// Possible a similar size image has already been generated.
|
|
// Reduces Server cache fragmentation, also increases chance of cache hit
|
|
// If width is less than 320, we round up to 320
|
|
int bucketedWidth = width <= 320 ? 320 : Math.round((float)width / 320.0f) * 320;
|
|
if(mMedia.getWidth() != 0 && mMedia.getWidth() < bucketedWidth) {
|
|
// If we know that the width of the image is lesser than the required width
|
|
// We don't even try to load the thumbnai, go directly to the source
|
|
loadImageIfNecessary(isInLayoutPass, true);
|
|
return;
|
|
} else {
|
|
mUrl = mMedia.getThumbnailUrl(bucketedWidth);
|
|
}
|
|
}
|
|
|
|
// if the URL to be loaded in this view is empty, cancel any old requests and clear the
|
|
// currently loaded image.
|
|
if (TextUtils.isEmpty(mUrl)) {
|
|
if (mImageContainer != null) {
|
|
mImageContainer.cancelRequest();
|
|
mImageContainer = null;
|
|
}
|
|
setImageBitmap(null);
|
|
return;
|
|
}
|
|
|
|
// Don't repeat work. Prevents onLayout cascades
|
|
// We ignore it if the image request was for either the current URL of for the full URL
|
|
// Since the full URL is always the second, and
|
|
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
|
|
if (mImageContainer.getRequestUrl().equals(mMedia.getImageUrl()) || mImageContainer.getRequestUrl().equals(mUrl)) {
|
|
return;
|
|
} else {
|
|
// if there is a pre-existing request, cancel it if it's fetching a different URL.
|
|
mImageContainer.cancelRequest();
|
|
BitmapDrawable actualDrawable = (BitmapDrawable)getDrawable();
|
|
if(actualDrawable != null && actualDrawable.getBitmap() != null) {
|
|
setImageBitmap(null);
|
|
if(loadingView != null) {
|
|
loadingView.setVisibility(View.VISIBLE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The pre-existing content of this view didn't match the current URL. Load the new image
|
|
// from the network.
|
|
ImageContainer newContainer = mImageLoader.get(mUrl,
|
|
new ImageListener() {
|
|
@Override
|
|
public void onErrorResponse(final VolleyError error) {
|
|
if(!tryOriginal) {
|
|
post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
loadImageIfNecessary(false, true);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onResponse(final ImageContainer response, boolean isImmediate) {
|
|
// If this was an immediate response that was delivered inside of a layout
|
|
// pass do not set the image immediately as it will trigger a requestLayout
|
|
// inside of a layout. Instead, defer setting the image by posting back to
|
|
// the main thread.
|
|
if (isImmediate && isInLayoutPass) {
|
|
post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
onResponse(response, false);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (response.getBitmap() != null) {
|
|
setImageBitmap(response.getBitmap());
|
|
if(tryOriginal && mMedia instanceof Contribution && (response.getBitmap().getWidth() > mMedia.getWidth() || response.getBitmap().getHeight() > mMedia.getHeight())) {
|
|
// If there is no width information for this image, save it. This speeds up image loading massively for smaller images
|
|
mMedia.setHeight(response.getBitmap().getHeight());
|
|
mMedia.setWidth(response.getBitmap().getWidth());
|
|
((Contribution)mMedia).setContentProviderClient(MediaWikiImageView.this.getContext().getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY));
|
|
((Contribution)mMedia).save();
|
|
}
|
|
if(loadingView != null) {
|
|
loadingView.setVisibility(View.GONE);
|
|
}
|
|
} else {
|
|
// I'm not really sure where this would hit but not onError
|
|
}
|
|
}
|
|
});
|
|
|
|
// update the ImageContainer to be the new bitmap container.
|
|
mImageContainer = newContainer;
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
super.onLayout(changed, left, top, right, bottom);
|
|
loadImageIfNecessary(true);
|
|
}
|
|
|
|
@Override
|
|
protected void onDetachedFromWindow() {
|
|
if (mImageContainer != null) {
|
|
// If the view was bound to an image request, cancel it and clear
|
|
// out the image from the view.
|
|
mImageContainer.cancelRequest();
|
|
setImageBitmap(null);
|
|
// also clear out the container so we can reload the image if necessary.
|
|
mImageContainer = null;
|
|
}
|
|
super.onDetachedFromWindow();
|
|
}
|
|
|
|
@Override
|
|
protected void drawableStateChanged() {
|
|
super.drawableStateChanged();
|
|
invalidate();
|
|
}
|
|
}
|