/*************************************************************************
* Copyright (c) 2015 Lemberg Solutions
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**************************************************************************/
package com.ls.widgets.map.model;
import java.lang.ref.SoftReference;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable.Callback;
import android.graphics.drawable.TransitionDrawable;
import android.util.Log;
import android.view.View;
import com.ls.widgets.map.interfaces.TileManagerDelegate;
import com.ls.widgets.map.providers.TileProvider;
public class Cell implements Callback, TileManagerDelegate
{
// Model
private int zoomLevel;
private float width;
private float height;
private int col;
private int row;
// View
private Grid parent;
private View rootView;
private double scale;
private float density;
protected Drawable image;
private SoftReference<Drawable> imageCache;
private TileProvider tileProvider;
private Rect imageBounds;
private float x1;
private float y1;
// Controller
protected boolean invalidateRect;
private boolean isImageLoading;
private boolean loadImage;
private boolean isReady;
private String TAG = Cell.class.getSimpleName();
private boolean doNotProcessThisCell;
public Cell(Grid parent, TileProvider tileProvider, int zoomLevel, int col, int row, int tileSize, double scale)
{
doNotProcessThisCell = false;
this.width = tileSize;
this.height = tileSize;
this.col = col;
this.row = row;
this.scale = scale;
this.zoomLevel = zoomLevel;
invalidateRect = true;
this.parent = parent;
this.rootView = parent.getParentView();
this.tileProvider = tileProvider;
loadImage = true;
x1 = col * width;
y1 = row * height;
imageBounds = new Rect();
density = rootView.getResources().getDisplayMetrics().density;
isImageLoading = false;
isReady = false;
}
public void cacheImage(float dx, float dy)
{
if (image == null) {
loadImage(dx, dy, false);
}
}
public void draw (Canvas canvas, Paint paint, float dx, float dy)
{
try {
if (image == null)
loadImage(dx, dy, true);
if (image != null) {
if (invalidateRect) {
recalculateDrawableRect(dx, dy);
}
image.draw(canvas);
}
} catch (Exception e) {
e.printStackTrace();
Log.e("Cell", "Exception during cell painting. E: " + e);
}
}
public void setLoadImage(boolean loadImage)
{
this.loadImage = loadImage;
}
public void setScale(double scale)
{
this.scale = scale;
invalidateRect = true;
if (image != null) {
recalculateDrawableRect(0, 0);
}
}
public void freeResources()
{
imageCache = new SoftReference<Drawable>(image);
image = null;
invalidateRect = true;
}
private void loadImage(final float dx, final float dy, final boolean invalidateOnDownload)
{
if (doNotProcessThisCell)
return;
if (isImageLoading || !loadImage)
return;
if (imageCache != null) {
Drawable cached = imageCache.get();
if (cached != null) {
processNewTile(0, 0, cached, true);
return;
}
}
isImageLoading = true;
tileProvider.requestTile(zoomLevel, col, row, this);
}
private void processNewTile(final float dx, final float dy, Drawable drawable, boolean fromCache)
{
if (drawable != null) {
image = drawable;
if (!fromCache) {
Rect bounds = image.getBounds();
width = (float)Math.round(bounds.width());
height = (float)Math.round(bounds.height());
} else {
if (image instanceof TransitionDrawable) {
TransitionDrawable transDrawable = (TransitionDrawable) drawable;
BitmapDrawable tile = (BitmapDrawable) transDrawable.getDrawable(1);
width = tile.getBitmap().getWidth();
height = tile.getBitmap().getHeight();
}
}
recalculateDrawableRect(dx, dy);
if (image instanceof TransitionDrawable) {
((TransitionDrawable) image).setCallback(Cell.this);
((TransitionDrawable) image).startTransition(150);
rootView.postDelayed(new Runnable(){public void run(){onIsReady();}}, 150);
}
}
}
private void onIsReady()
{
isReady = true;
parent.onCellReady(this);
}
protected void recalculateDrawableRect(final float dx, final float dy)
{
imageBounds.left = (int)((x1) * scale);
imageBounds.top = (int)((y1) * scale);
imageBounds.right = (int)((x1 + width) * scale);
imageBounds.bottom = (int)((y1 + height) * scale);
image.setBounds(imageBounds);
invalidateRect = false;
}
@Override
public void invalidateDrawable(Drawable who)
{
Rect bounds = who.getBounds();
rootView.postInvalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when)
{
rootView.scheduleDrawable(who, what, when);
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what)
{
rootView.unscheduleDrawable(who, what);
}
public boolean isReady()
{
return isReady;
}
public void setTileProvider(TileProvider tileManager)
{
this.tileProvider = tileManager;
}
@Override
public void onTileReady(int zoomLevel, int col, int row, Drawable drawable) {
processNewTile(0, 0, drawable, false);
//rootView.postInvalidate(imageBounds.left, imageBounds.top, imageBounds.right, imageBounds.bottom);
isImageLoading = false;
}
@Override
public void onError(Exception e) {
isImageLoading = false;
doNotProcessThisCell = true;
onIsReady();
}
}