/******************************************************************************* * This file is part of RedReader. * * RedReader is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * RedReader is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RedReader. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package org.quantumbadger.redreader.activities; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.util.Base64OutputStream; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import org.quantumbadger.redreader.R; import org.quantumbadger.redreader.account.RedditAccountManager; import org.quantumbadger.redreader.cache.CacheManager; import org.quantumbadger.redreader.cache.CacheRequest; import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyAlways; import org.quantumbadger.redreader.common.AndroidApi; import org.quantumbadger.redreader.common.Constants; import org.quantumbadger.redreader.common.General; import org.quantumbadger.redreader.common.PrefsUtility; import org.quantumbadger.redreader.common.RRError; import org.quantumbadger.redreader.http.HTTPBackend; import org.quantumbadger.redreader.image.ThumbnailScaler; import org.quantumbadger.redreader.jsonwrap.JsonBufferedObject; import org.quantumbadger.redreader.jsonwrap.JsonValue; import org.quantumbadger.redreader.views.LoadingSpinnerView; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.UUID; public class ImgurUploadActivity extends BaseActivity { private static final int REQUEST_CODE = 1; private TextView mTextView; private ImageView mThumbnailView; private String mBase64Data; private Button mUploadButton; private View mLoadingOverlay; @Override protected void onCreate(Bundle savedInstanceState) { PrefsUtility.applyTheme(this); super.onCreate(savedInstanceState); setTitle(R.string.upload_to_imgur); final FrameLayout outerLayout = new FrameLayout(this); final LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); mTextView = new TextView(this); mTextView.setText(R.string.no_file_selected); layout.addView(mTextView); General.setAllMarginsDp(this, mTextView, 10); mUploadButton = new Button(this); mUploadButton.setText(R.string.button_upload); mUploadButton.setEnabled(false); layout.addView(mUploadButton); updateUploadButtonVisibility(); final Button browseButton = new Button(this); browseButton.setText(R.string.button_browse); layout.addView(browseButton); mThumbnailView = new ImageView(this); layout.addView(mThumbnailView); General.setAllMarginsDp(this, mThumbnailView, 20); browseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, REQUEST_CODE); } }); mUploadButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { if(mBase64Data != null) { uploadImage(); } else { General.quickToast(ImgurUploadActivity.this, R.string.no_file_selected); } } }); final ScrollView sv = new ScrollView(this); sv.addView(layout); outerLayout.addView(sv); { mLoadingOverlay = new LoadingSpinnerView(this); outerLayout.addView(mLoadingOverlay); mLoadingOverlay.setBackgroundColor(Color.argb(220, 50, 50, 50)); final ViewGroup.LayoutParams layoutParams = mLoadingOverlay.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; mLoadingOverlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { // Do nothing } }); mLoadingOverlay.setVisibility(View.GONE); } setBaseActivityContentView(outerLayout); General.setAllMarginsDp(this, layout, 20); } private void showLoadingOverlay() { mLoadingOverlay.setVisibility(View.VISIBLE); } private void hideLoadingOverlay() { mLoadingOverlay.setVisibility(View.GONE); } private void updateUploadButtonVisibility() { mUploadButton.setVisibility( mBase64Data != null ? View.VISIBLE : View.GONE); } @Override protected void onActivityResult(final int requestCode, final int result, final Intent data) { if(data == null || data.getData() == null) { return; } if(requestCode != REQUEST_CODE) { return; } if(result != RESULT_OK) { return; } onImageSelected(data.getData()); } private void onImageSelected(final Uri uri) { showLoadingOverlay(); new Thread("Image selected thread") { @Override public void run() { try { final ParcelFileDescriptor file = getContentResolver().openFileDescriptor(uri, "r"); final long statSize = file.getStatSize(); if(statSize >= 10 * 1000 * 1000) { // Use base 10 just to be safe... General.showResultDialog(ImgurUploadActivity.this, new RRError( getString(R.string.error_file_too_big_title), getString( R.string.error_file_too_big_message, statSize / 1024 + "kB", "10MB"))); return; } final int thumbnailSizePx = General.dpToPixels(ImgurUploadActivity.this, 200); final Bitmap rawBitmap = BitmapFactory.decodeFileDescriptor(file.getFileDescriptor()); final Bitmap thumbnailBitmap = ThumbnailScaler.scaleNoCrop(rawBitmap, thumbnailSizePx); rawBitmap.recycle(); final InputStream inputStream = getContentResolver().openInputStream(uri); final ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); final Base64OutputStream output = new Base64OutputStream(byteOutput, 0); try { General.copyStream(inputStream, output); output.flush(); } catch(IOException e) { throw new RuntimeException(e); } final String base64String = new String(byteOutput.toByteArray()); AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { mBase64Data = base64String; mUploadButton.setEnabled(true); mThumbnailView.setImageBitmap(thumbnailBitmap); mTextView.setText(getString( R.string.image_selected_summary, rawBitmap.getWidth(), rawBitmap.getHeight(), statSize / 1024 + "kB")); hideLoadingOverlay(); updateUploadButtonVisibility(); } }); } catch(Exception e) { General.showResultDialog(ImgurUploadActivity.this, new RRError( getString(R.string.error_file_open_failed_title), getString(R.string.error_file_open_failed_message), e)); AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { mBase64Data = null; mUploadButton.setEnabled(false); mThumbnailView.setImageBitmap(null); mTextView.setText(R.string.no_file_selected); hideLoadingOverlay(); updateUploadButtonVisibility(); } }); } } }.start(); } private void uploadImage() { showLoadingOverlay(); final ArrayList<HTTPBackend.PostField> postFields = new ArrayList<>(1); postFields.add(new HTTPBackend.PostField("image", mBase64Data)); CacheManager.getInstance(this).makeRequest(new CacheRequest( General.uriFromString("https://api.imgur.com/3/image"), RedditAccountManager.getInstance(this).getDefaultAccount(), null, Constants.Priority.API_ACTION, 0, DownloadStrategyAlways.INSTANCE, Constants.FileType.NOCACHE, CacheRequest.DOWNLOAD_QUEUE_IMGUR_API, true, postFields, false, false, this) { @Override protected void onCallbackException(final Throwable t) { BugReportActivity.handleGlobalError(ImgurUploadActivity.this, t); } @Override protected void onDownloadNecessary() { } @Override protected void onDownloadStarted() { } @Override protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer httpStatus, final String readableMessage) { General.showResultDialog(ImgurUploadActivity.this, General.getGeneralErrorForFailure( ImgurUploadActivity.this, type, t, httpStatus, url.toString())); AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { hideLoadingOverlay(); } }); } @Override public void onJsonParseStarted(final JsonValue result, final long timestamp, final UUID session, final boolean fromCache) { try { result.join(); } catch(final InterruptedException e) { throw new RuntimeException(e); } final Uri imageUri; try { final JsonBufferedObject root = result.asObject(); if(root == null) { throw new RuntimeException("Response root object is null"); } final Boolean success = root.getBoolean("success"); if(!Boolean.TRUE.equals(success)) { onFailure(CacheRequest.REQUEST_FAILURE_UPLOAD_FAIL_IMGUR, null, null, null); return; } final String id = root.getObject("data").getString("id"); imageUri = Uri.parse("https://imgur.com/" + id); } catch(final Throwable t) { onFailure(CacheRequest.REQUEST_FAILURE_PARSE_IMGUR, t, null, t.toString()); return; } AndroidApi.UI_THREAD_HANDLER.post(new Runnable() { @Override public void run() { final Intent resultIntent = new Intent(); resultIntent.setData(imageUri); setResult(0, resultIntent); finish(); } }); } @Override protected void onProgress(final boolean authorizationInProgress, final long bytesRead, final long totalBytes) { } @Override protected void onSuccess( final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final boolean fromCache, final String mimetype) { } }); } @Override public void onBackPressed() { if(General.onBackPressed()) super.onBackPressed(); } }