/*
* Copyright 2015 Hippo Seven
*
* 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.hippo.nimingban.ui;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewPager;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import com.hippo.conaco.Conaco;
import com.hippo.conaco.DataContainer;
import com.hippo.conaco.ProgressNotify;
import com.hippo.drawable.ImageDrawable;
import com.hippo.drawable.ImageWrapper;
import com.hippo.io.UniFileInputStreamPipe;
import com.hippo.nimingban.ImageSearch;
import com.hippo.nimingban.NMBAppConfig;
import com.hippo.nimingban.NMBApplication;
import com.hippo.nimingban.R;
import com.hippo.nimingban.client.data.Site;
import com.hippo.nimingban.content.ImageProvider;
import com.hippo.nimingban.util.BitmapUtils;
import com.hippo.nimingban.util.OpenUrlHelper;
import com.hippo.nimingban.util.Settings;
import com.hippo.nimingban.widget.GalleryPage;
import com.hippo.unifile.UniFile;
import com.hippo.util.DrawableManager;
import com.hippo.widget.viewpager.PagerHolder;
import com.hippo.widget.viewpager.RecyclerPagerAdapter;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.ResourcesUtils;
import com.hippo.yorozuya.io.InputStreamPipe;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
// TODO show all image in post
public class GalleryActivity2 extends SwipeBackActivity {
public static final String[] IMAGE_EXTENSIONS = {
"jpg",
"jpeg",
"png",
"gif"
};
public static final String ACTION_SINGLE_IMAGE = "com.hippo.nimingban.ui.GalleryActivity2.action.SINGLE_IMAGE";
public static final String ACTION_MULTIMAGE = "com.hippo.nimingban.ui.GalleryActivity2.action.MULTIMAGE";
public static final String ACTION_IMAGE_FILE = "com.hippo.nimingban.ui.GalleryActivity2.action.IMAGE_FILE";
public static final String KEY_SITE = "site";
public static final String KEY_ID = "id";
public static final String KEY_KEY = "key";
public static final String KEY_IMAGE = "image";
public static final String KEY_UNI_FILE_URI = "uni_file_uri";
private ViewPager mViewPager;
private GalleryAdapter mGalleryAdapter;
private SaveTask mSaveTask;
private boolean handlerIntent(Intent intent) {
if (intent == null) {
return false;
}
String action = intent.getAction();
if (ACTION_SINGLE_IMAGE.equals(action)) {
int site = intent.getIntExtra(KEY_SITE, -1);
String id = intent.getStringExtra(KEY_ID);
String key = intent.getStringExtra(KEY_KEY);
String image = intent.getStringExtra(KEY_IMAGE);
if (Site.isValid(site) && id != null && key != null && image != null) {
mGalleryAdapter = new SingleImageAdapter(Site.fromId(site), id, key, image);
return true;
}
} else if (ACTION_IMAGE_FILE.equals(action)) {
Uri fileUri = intent.getParcelableExtra(KEY_UNI_FILE_URI);
UniFile file = UniFile.fromUri(this, fileUri);
if (file != null) {
mGalleryAdapter = new ImageFileAdapter(file);
return true;
}
}
return false;
}
@Override
protected int getLightThemeResId() {
return Settings.getColorStatusBar() ? R.style.SwipeActivity : R.style.SwipeActivity_NoStatus;
}
@Override
protected int getDarkThemeResId() {
return Settings.getColorStatusBar() ? R.style.SwipeActivity_Dark : R.style.SwipeActivity_Dark_NoStatus;
}
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!handlerIntent(getIntent())) {
finish();
return;
}
setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark));
ToolbarActivityHelper.setContentView(this, R.layout.activity_gallery_2);
setActionBarUpIndicator(DrawableManager.getDrawable(this, R.drawable.v_arrow_left_dark_x24));
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mViewPager.setAdapter(mGalleryAdapter);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewPager != null) {
// Unload all pager
for (int i = 0, n = mViewPager.getChildCount(); i < n; i++) {
View child = mViewPager.getChildAt(i);
if (child instanceof GalleryPage) {
((GalleryPage) child).unload();
}
}
}
if (mSaveTask != null) {
mSaveTask.onActivityDestory();
mSaveTask = null;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_gallery_2, menu);
if (mGalleryAdapter instanceof ImageFileAdapter) {
menu.removeItem(R.id.action_refresh);
menu.removeItem(R.id.action_search);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final int id = item.getItemId();
switch (id) {
case android.R.id.home:
finish();
return true;
case R.id.action_save:
if (mSaveTask == null) {
mGalleryAdapter.saveCurrentImage(false);
}
return true;
case R.id.action_share:
if (mSaveTask == null) {
mGalleryAdapter.saveCurrentImage(true);
}
return true;
case R.id.action_refresh:
if (mSaveTask == null) {
mGalleryAdapter.reloadCurrentImage();
}
return true;
case R.id.action_search_google:
case R.id.action_search_baidu:
case R.id.action_search_sogou:
case R.id.action_search_tineye:
case R.id.action_search_whatanime:
case R.id.action_search_saucenao:
case R.id.action_search_iqdb:
case R.id.action_search_iqdb_3d:
if (mGalleryAdapter instanceof SingleImageAdapter) {
String url = ImageSearch.getImageSearchUrl(id,
((SingleImageAdapter) mGalleryAdapter).getCurrentImageUrl());
if (url != null) {
OpenUrlHelper.openUrl(this, url, false);
}
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/**
* @param uri the save image file url, null for fail
* @param share true for share
*/
public void onSaveTaskOver(Uri uri, boolean share) {
if (mSaveTask != null) {
mSaveTask = null;
}
if (share) {
if (uri == null) {
Toast.makeText(this, R.string.cant_save_image, Toast.LENGTH_SHORT).show();
} else {
String mimeType = getContentResolver().getType(uri);
if (TextUtils.isEmpty(mimeType)) {
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
MimeTypeMap.getFileExtensionFromUrl(uri.toString()));
if (TextUtils.isEmpty(mimeType)) {
mimeType = "image/*";
}
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, ImageProvider.buildUri(uri.getLastPathSegment()));
intent.setType(mimeType);
startActivity(Intent.createChooser(intent, getString(R.string.share_image)));
}
} else {
Toast.makeText(this, uri != null ? R.string.save_successfully : R.string.save_failed, Toast.LENGTH_SHORT).show();
}
// Notify media scanner
if (uri != null) {
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
}
}
private class GalleryHolder extends PagerHolder {
public GalleryPage galleryPage;
public GalleryHolder(View itemView) {
super(itemView);
galleryPage = (GalleryPage) itemView;
}
}
private abstract class GalleryAdapter extends RecyclerPagerAdapter<GalleryHolder> {
@NonNull
@Override
public GalleryHolder createPagerHolder(ViewGroup container) {
return new GalleryHolder(GalleryActivity2.this.getLayoutInflater()
.inflate(R.layout.item_gallery, container, false));
}
public abstract void reloadCurrentImage();
public abstract void saveCurrentImage(boolean share);
}
private class ImageFileAdapter extends GalleryAdapter {
private UniFile mImageFile;
public ImageFileAdapter(UniFile imageFile) {
mImageFile = imageFile;
}
@Override
public void reloadCurrentImage() {
// Empty
}
@Override
public void saveCurrentImage(boolean share) {
GalleryHolder holder = getPagerHolder(0);
UniFile dir = Settings.getImageSaveLocation();
if (holder == null || !holder.galleryPage.isLoaded() || dir == null) {
onSaveTaskOver(null, share);
return;
}
mSaveTask = new ImageFileSaveTask(GalleryActivity2.this, mImageFile, dir, share);
mSaveTask.execute();
}
@Override
public void bindPagerHolder(GalleryHolder holder, int position) {
Object object = NMBApplication.getImageWrapperHelper(GalleryActivity2.this)
.decode(new UniFileInputStreamPipe(mImageFile));
if (object != null) {
ImageWrapper imageWrapper = (ImageWrapper) object;
imageWrapper.start();
Drawable drawable = new ImageDrawable(imageWrapper);
holder.galleryPage.showDrawable(drawable);
} else {
holder.galleryPage.showFailedText();
}
}
@Override
public void unbindPagerHolder(GalleryHolder holder, int position) {
holder.galleryPage.unload();
}
@Override
public int getCount() {
return 1;
}
}
private class SingleImageAdapter extends GalleryAdapter {
private Site mSite;
private String mId;
private String mKey;
private String mImage;
public SingleImageAdapter(Site site, String id, String key, String image) {
mSite = site;
mId = id;
mKey = key;
mImage = image;
}
@Override
public void bindPagerHolder(GalleryHolder holder, int position) {
String key;
DataContainer container;
UniFile dir = Settings.getImageSaveLocation();
if (Settings.getSaveImageAuto() && dir != null) {
key = null;
container = new UniFileDataContain(GalleryActivity2.this, dir,
mSite.getReadableName(GalleryActivity2.this) + "-" + mId);
} else {
key = mKey;
container = null;
}
holder.galleryPage.load(key, mImage, container);
}
@Override
public void unbindPagerHolder(GalleryHolder holder, int position) {
holder.galleryPage.unload();
}
@Override
public int getCount() {
return 1;
}
public String getCurrentImageUrl() {
return mImage;
}
@Override
public void reloadCurrentImage() {
GalleryHolder holder = getPagerHolder(0);
if (holder == null) {
return;
}
// Unload
holder.galleryPage.unload();
// Remove in cache
NMBApplication.getConaco(GalleryActivity2.this).getBeerBelly().remove(mImage);
// Remove all in save location
UniFile dir = Settings.getImageSaveLocation();
if (dir != null) {
String name = mSite.getReadableName(GalleryActivity2.this) + "-" + mId;
for (String extension : IMAGE_EXTENSIONS) {
UniFile file = dir.findFile(name + '.' + extension);
if (file != null) {
file.delete();
}
}
}
// Load
bindPagerHolder(holder, 0);
}
@Override
public void saveCurrentImage(boolean share) {
GalleryHolder holder = getPagerHolder(0);
UniFile dir = Settings.getImageSaveLocation();
if (holder == null || !holder.galleryPage.isLoaded() || dir == null) {
onSaveTaskOver(null, share);
return;
}
String name = mSite.getReadableName(GalleryActivity2.this) + "-" + mId;
mSaveTask = new SingleImageSaveTask(GalleryActivity2.this, dir, name, mKey, share);
mSaveTask.execute();
}
}
/*
private class MultimageAdapter extends GalleryAdapter {
private Site mSite;
@Override
public void reloadCurrentImage() {
}
@Override
public void saveCurrentImage(boolean share) {
}
@Override
public void bindPagerHolder(GalleryHolder holder, int position) {
}
@Override
public void unbindPagerHolder(GalleryHolder holder, int position) {
}
@Override
public int getCount() {
return 0;
}
}
*/
private static UniFile findFileForName(UniFile dir, String name, String[] extensions, String[] resultExtension) {
for (String extension : extensions) {
UniFile file = dir.findFile(name + '.' + extension);
if (file != null) {
if (resultExtension != null && resultExtension.length > 0) {
resultExtension[0] = extension;
}
return file;
}
}
return null;
}
/**
* Let conaco store image to image save location directly
*/
private static class UniFileDataContain implements DataContainer {
private Context mContext;
private UniFile mDir;
private String mName;
@Nullable
private String mFilename;
public UniFileDataContain(Context context, @NonNull UniFile dir, String name) {
mContext = context.getApplicationContext();
mDir = dir;
mName = name;
}
@Override
public void onUrlMoved(String requestUrl, String responseUrl) {
}
@Override
public boolean save(InputStream is, long length, String mediaType, ProgressNotify notify) {
// Get extension and filename
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mediaType);
if (!TextUtils.isEmpty(extension)) {
mFilename = mName + '.' + extension;
} else {
mFilename = mName;
}
UniFile file = mDir.createFile(mFilename);
if (file == null) {
return false;
}
OutputStream os = null;
try {
os = file.openOutputStream();
final byte buffer[] = new byte[1024 * 4];
long receivedSize = 0;
int bytesRead;
while((bytesRead = is.read(buffer)) !=-1) {
os.write(buffer, 0, bytesRead);
receivedSize += bytesRead;
if (length > 0) {
notify.notifyProgress((long) bytesRead, receivedSize, length);
}
}
os.flush();
IOUtils.closeQuietly(os);
// Notify media scanner
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, file.getUri()));
return true;
} catch (IOException e) {
IOUtils.closeQuietly(os);
file.delete();
return false;
}
}
@Override
public InputStreamPipe get() {
if (mFilename != null) {
UniFile file = mDir.findFile(mFilename);
if (file.exists()) {
return new UniFileInputStreamPipe(file);
}
}
String[] extension = new String[1];
UniFile file = findFileForName(mDir, mName, IMAGE_EXTENSIONS, extension);
if (file != null) {
return new UniFileInputStreamPipe(file);
}
return null;
}
@Override
public void remove() {
// Empty
}
}
private static abstract class SaveTask extends AsyncTask<Void, Void, Uri> {
public abstract void onActivityDestory();
}
private static class ImageFileSaveTask extends SaveTask {
private Context mContext;
private UniFile mFrom;
private UniFile mSaveDir;
private boolean mShare;
public ImageFileSaveTask(Context context, UniFile from, UniFile saveDir, boolean share) {
mContext = context;
mFrom = from;
mSaveDir = saveDir;
mShare = share;
}
public UniFile createSaveFile(String mineType) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mineType);
String displayName;
if (TextUtils.isEmpty(extension)) {
displayName = mFrom.getName();
} else {
displayName = mFrom.getName() + '.' + extension;
}
return mSaveDir.createFile(displayName);
}
private static String getFilePathForUri(Uri uri) {
if (UniFile.isFileUri(uri)) {
return uri.getPath();
} else {
return null;
}
}
/**
* @return Null for failed
*/
@Override
protected Uri doInBackground(Void... params) {
UniFile to = null;
// Get from MineType and get out save file
InputStream is = null;
try {
is = mFrom.openInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
if (options.outMimeType != null) {
to = createSaveFile(options.outMimeType);
}
} catch (IOException e) {
return null;
} finally {
IOUtils.closeQuietly(is);
}
// Check save file null
if (to == null) {
return null;
}
// Is from and to is the same ?
Uri fromUri = mFrom.getUri();
Uri toUri = to.getUri();
if (fromUri.equals(toUri)) {
return fromUri;
}
String fromPath = getFilePathForUri(fromUri);
String toPath = getFilePathForUri(toUri);
if (fromPath != null && fromPath.equals(toPath)) {
return fromUri;
}
boolean ok;
OutputStream os = null;
try {
is = mFrom.openInputStream();
os = to.openOutputStream();
IOUtils.copy(is, os);
ok = true;
} catch (IOException e) {
ok = false;
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
if (!ok) {
to.delete();
return null;
}
return to.getUri();
}
@Override
protected void onPostExecute(Uri uri) {
if (mContext instanceof GalleryActivity2) {
((GalleryActivity2) mContext).onSaveTaskOver(uri, mShare);
}
}
@Override
public void onActivityDestory() {
mContext = mContext.getApplicationContext();
}
}
private static class SingleImageSaveTask extends SaveTask {
private Context mContext;
private UniFile mSaveDir;
private String mName;
private String mKey;
private boolean mShare;
public SingleImageSaveTask(Context context, UniFile saveDir, String name, String key, boolean share) {
mContext = context;
mSaveDir = saveDir;
mName = name;
mKey = key;
mShare = share;
}
public UniFile createSaveFile(String mineType) {
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mineType);
String displayName;
if (TextUtils.isEmpty(extension)) {
displayName = mName;
} else {
displayName = mName + '.' + extension;
}
return mSaveDir.createFile(displayName);
}
@Override
protected Uri doInBackground(Void... params) {
// First try to find file in dir
UniFile file = findFileForName(mSaveDir, mName, IMAGE_EXTENSIONS, null);
if (file != null) {
// Check is it a image
Bitmap bitmap = BitmapUtils.decodeStream(new UniFileInputStreamPipe(file), 100, 100, -1, true, false, null);
if (bitmap != null) {
// It is image
bitmap.recycle();
return file.getUri();
} else {
file.delete();
}
}
// Save image to temp file
File temp = NMBAppConfig.createTempFile();
if (temp == null) {
return null;
}
boolean ok = true;
OutputStream os = null;
try {
os = new FileOutputStream(temp);
Conaco conaco = NMBApplication.getConaco(mContext);
ok = conaco.getBeerBelly().pullFromDiskCache(mKey, os);
} catch (IOException e) {
ok = false;
} finally {
IOUtils.closeQuietly(os);
}
if (!ok) {
temp.delete();
return null;
}
UniFile to = null;
// Get extension and create save file
InputStream is = null;
try {
is = new FileInputStream(temp);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
if (options.outMimeType != null) {
to = createSaveFile(options.outMimeType);
}
} catch (IOException e) {
return null;
} finally {
IOUtils.closeQuietly(is);
}
// Check save file null
if (to == null) {
temp.delete();
return null;
}
// Copy temp to save file
try {
is = new FileInputStream(temp);
os = to.openOutputStream();
IOUtils.copy(is, os);
ok = true;
} catch (IOException e) {
ok = false;
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
// Clean up
temp.delete();
if (ok) {
return to.getUri();
} else {
to.delete();
return null;
}
}
@Override
protected void onPostExecute(Uri uri) {
if (mContext instanceof GalleryActivity2) {
((GalleryActivity2) mContext).onSaveTaskOver(uri, mShare);
}
}
@Override
public void onActivityDestory() {
mContext = mContext.getApplicationContext();
}
}
}