1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.filtershow.ui; 18 19 import android.content.Intent; 20 import android.graphics.Bitmap; 21 import android.graphics.Rect; 22 import android.net.Uri; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.support.v4.app.DialogFragment; 26 import android.text.Editable; 27 import android.text.TextWatcher; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.EditText; 32 import android.widget.SeekBar; 33 import android.widget.TextView; 34 35 import com.android.gallery3d.R; 36 import com.android.gallery3d.filtershow.FilterShowActivity; 37 import com.android.gallery3d.filtershow.imageshow.MasterImage; 38 import com.android.gallery3d.filtershow.pipeline.ImagePreset; 39 import com.android.gallery3d.filtershow.pipeline.ProcessingService; 40 import com.android.gallery3d.filtershow.tools.SaveImage; 41 42 import java.io.ByteArrayOutputStream; 43 import java.io.File; 44 45 public class ExportDialog extends DialogFragment implements View.OnClickListener, 46 SeekBar.OnSeekBarChangeListener { 47 SeekBar mSeekBar; 48 TextView mSeekVal; 49 EditText mWidthText; 50 EditText mHeightText; 51 TextView mEstimatedSize; 52 int mQuality = 95; 53 int mExportWidth = 0; 54 int mExportHeight = 0; 55 Rect mOriginalBounds; 56 int mCompressedSize; 57 Rect mCompressedBounds; 58 float mExportCompressionMargin = 1.1f; 59 float mRatio; 60 String mSliderLabel; 61 boolean mEditing = false; 62 Handler mHandler; 63 int mUpdateDelay = 1000; 64 Runnable mUpdateRunnable = new Runnable() { 65 @Override 66 public void run() { 67 updateCompressionFactor(); 68 updateSize(); 69 } 70 }; 71 72 private class Watcher implements TextWatcher { 73 private EditText mEditText; 74 Watcher(EditText text) { 75 mEditText = text; 76 } 77 78 @Override 79 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 80 } 81 82 @Override 83 public void onTextChanged(CharSequence s, int start, int before, int count) { 84 } 85 86 @Override 87 public void afterTextChanged(Editable s) { 88 textChanged(mEditText); 89 } 90 } 91 92 @Override 93 public View onCreateView(LayoutInflater inflater, ViewGroup container, 94 Bundle savedInstanceState) { 95 mHandler = new Handler(getActivity().getMainLooper()); 96 97 View view = inflater.inflate(R.layout.filtershow_export_dialog, container); 98 mSeekBar = (SeekBar) view.findViewById(R.id.qualitySeekBar); 99 mSeekVal = (TextView) view.findViewById(R.id.qualityTextView); 100 mSliderLabel = getString(R.string.quality) + ": "; 101 mSeekBar.setProgress(mQuality); 102 mSeekVal.setText(mSliderLabel + mSeekBar.getProgress()); 103 mSeekBar.setOnSeekBarChangeListener(this); 104 mWidthText = (EditText) view.findViewById(R.id.editableWidth); 105 mHeightText = (EditText) view.findViewById(R.id.editableHeight); 106 mEstimatedSize = (TextView) view.findViewById(R.id.estimadedSize); 107 108 mOriginalBounds = MasterImage.getImage().getOriginalBounds(); 109 ImagePreset preset = MasterImage.getImage().getPreset(); 110 mOriginalBounds = preset.finalGeometryRect(mOriginalBounds.width(), 111 mOriginalBounds.height()); 112 mRatio = mOriginalBounds.width() / (float) mOriginalBounds.height(); 113 mWidthText.setText("" + mOriginalBounds.width()); 114 mHeightText.setText("" + mOriginalBounds.height()); 115 mExportWidth = mOriginalBounds.width(); 116 mExportHeight = mOriginalBounds.height(); 117 mWidthText.addTextChangedListener(new Watcher(mWidthText)); 118 mHeightText.addTextChangedListener(new Watcher(mHeightText)); 119 120 view.findViewById(R.id.cancel).setOnClickListener(this); 121 view.findViewById(R.id.done).setOnClickListener(this); 122 getDialog().setTitle(R.string.export_flattened); 123 124 updateCompressionFactor(); 125 updateSize(); 126 return view; 127 } 128 129 @Override 130 public void onStopTrackingTouch(SeekBar arg0) { 131 // Do nothing 132 } 133 134 @Override 135 public void onStartTrackingTouch(SeekBar arg0) { 136 // Do nothing 137 } 138 139 @Override 140 public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { 141 mSeekVal.setText(mSliderLabel + arg1); 142 mQuality = mSeekBar.getProgress(); 143 scheduleUpdateCompressionFactor(); 144 } 145 146 private void scheduleUpdateCompressionFactor() { 147 mHandler.removeCallbacks(mUpdateRunnable); 148 mHandler.postDelayed(mUpdateRunnable, mUpdateDelay); 149 } 150 151 @Override 152 public void onClick(View v) { 153 switch (v.getId()) { 154 case R.id.cancel: 155 dismiss(); 156 break; 157 case R.id.done: 158 FilterShowActivity activity = (FilterShowActivity) getActivity(); 159 Uri sourceUri = MasterImage.getImage().getUri(); 160 File dest = SaveImage.getNewFile(activity, activity.getSelectedImageUri()); 161 float scaleFactor = mExportWidth / (float) mOriginalBounds.width(); 162 Intent processIntent = ProcessingService.getSaveIntent(activity, MasterImage 163 .getImage().getPreset(), dest, activity.getSelectedImageUri(), sourceUri, 164 true, mSeekBar.getProgress(), scaleFactor, false); 165 activity.startService(processIntent); 166 dismiss(); 167 break; 168 } 169 } 170 171 public void updateCompressionFactor() { 172 Bitmap bitmap = MasterImage.getImage().getFilteredImage(); 173 if (bitmap == null) { 174 return; 175 } 176 ByteArrayOutputStream out = new ByteArrayOutputStream(); 177 bitmap.compress(Bitmap.CompressFormat.JPEG, mQuality, out); 178 mCompressedSize = out.size(); 179 mCompressedBounds = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 180 } 181 182 public void updateSize() { 183 if (mCompressedBounds == null) { 184 return; 185 } 186 // This is a rough estimate of the final save size. There's some loose correlation 187 // between a compressed jpeg and a larger version of it in function of the image 188 // area. Not a perfect estimate by far. 189 float originalArea = mCompressedBounds.width() * mCompressedBounds.height(); 190 float newArea = mExportWidth * mExportHeight; 191 float factor = originalArea / (float) mCompressedSize; 192 float compressedSize = newArea / factor; 193 compressedSize *= mExportCompressionMargin; 194 float size = compressedSize / 1024.f / 1024.f; 195 size = ((int) (size * 100)) / 100f; 196 String estimatedSize = "" + size + " Mb"; 197 mEstimatedSize.setText(estimatedSize); 198 } 199 200 private void textChanged(EditText text) { 201 if (mEditing) { 202 return; 203 } 204 mEditing = true; 205 int width = 0; 206 int height = 0; 207 if (text.getId() == R.id.editableWidth) { 208 if (mWidthText.getText() != null) { 209 String value = String.valueOf(mWidthText.getText()); 210 if (value.length() > 0) { 211 width = Integer.parseInt(value); 212 if (width > mOriginalBounds.width()) { 213 width = mOriginalBounds.width(); 214 mWidthText.setText("" + width); 215 } 216 height = (int) (width / mRatio); 217 } 218 mHeightText.setText("" + height); 219 } 220 } else if (text.getId() == R.id.editableHeight) { 221 if (mHeightText.getText() != null) { 222 String value = String.valueOf(mHeightText.getText()); 223 if (value.length() > 0) { 224 height = Integer.parseInt(value); 225 if (height > mOriginalBounds.height()) { 226 height = mOriginalBounds.height(); 227 mHeightText.setText("" + height); 228 } 229 width = (int) (height * mRatio); 230 } 231 mWidthText.setText("" + width); 232 } 233 } 234 mExportWidth = width; 235 mExportHeight = height; 236 updateSize(); 237 mEditing = false; 238 } 239 240 } 241