1 package jp.sourceforge.gokigen.memoma.io;
3 import static jp.sourceforge.gokigen.memoma.Main.APP_NAMESPACE;
6 import java.io.OutputStream;
7 import java.text.SimpleDateFormat;
8 import java.util.Calendar;
9 import java.util.Enumeration;
10 import java.util.Locale;
12 import android.app.ProgressDialog;
13 import android.content.ContentResolver;
14 import android.content.ContentValues;
15 import android.content.Context;
16 import android.database.Cursor;
17 import android.database.DatabaseUtils;
18 import android.graphics.Bitmap;
19 import android.graphics.Canvas;
20 import android.graphics.Rect;
21 import android.graphics.RectF;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.os.Build;
25 import android.os.Environment;
26 import android.provider.MediaStore;
27 import android.util.Log;
28 import android.view.Display;
30 import androidx.appcompat.app.AppCompatActivity;
32 import jp.sourceforge.gokigen.memoma.R;
33 import jp.sourceforge.gokigen.memoma.drawers.MeMoMaCanvasDrawer;
34 import jp.sourceforge.gokigen.memoma.holders.MeMoMaObjectHolder;
35 import jp.sourceforge.gokigen.memoma.holders.PositionObject;
38 * データをファイルに保存するとき用 アクセスラッパ (非同期処理を実行)
39 * Viewの情報を画像形式(png形式)で保存する。
40 * どのViewを保存するのかは、ICaptureExporter.getCaptureTargetView()クラスを使って教えてもらう。
42 * String : 実行時に渡すクラス(Param) : ファイル名をもらう
43 * Integer : 途中経過を伝えるクラス(Progress) : 今回は使っていない
44 * String : 処理結果を伝えるクラス(Result) : 結果を応答する。
46 public class ObjectLayoutCaptureExporter extends AsyncTask<String, Integer, String>
48 private final String TAG = toString();
49 private static final boolean dumpLog = false;
51 private static final int OUTPUT_EXPORT_SHARE_ID = 1000;
52 private static final int OUTPUT_MARGIN = 8;
53 private static final int OUTPUT_MARGIN_TOP = 50;
55 private static final int MINIMUM_WIDTH = 800;
56 private static final int MINIMUM_HEIGHT = 600;
58 private final ICaptureLayoutExporter receiver;
60 private final MeMoMaObjectHolder objectHolder;
61 private final MeMoMaCanvasDrawer canvasDrawer;
62 private final ProgressDialog savingDialog;
63 private float offsetX = 0.0f;
64 private float offsetY = 0.0f;
65 private int displayWidth;
66 private int displayHeight;
68 private final Context context;
70 private Uri exportedUri = null;
75 ObjectLayoutCaptureExporter(AppCompatActivity context, MeMoMaObjectHolder holder, MeMoMaCanvasDrawer drawer, ICaptureLayoutExporter resultReceiver)
77 this.context = context;
78 receiver = resultReceiver;
79 objectHolder = holder;
80 canvasDrawer = drawer;
83 Display display = context.getWindowManager().getDefaultDisplay();
84 displayWidth = display.getWidth();
85 displayHeight = display.getHeight();
87 // プログレスダイアログ(「保存中...」)を表示する。
88 savingDialog = new ProgressDialog(context);
89 savingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
90 savingDialog.setMessage(context.getString(R.string.dataSaving));
91 savingDialog.setIndeterminate(true);
92 savingDialog.setCancelable(false);
95 // ファイルをバックアップするディレクトリを作成する
96 File dir = new File(context.getFilesDir() + "/exported");
99 Log.v(TAG, "mkdir is failed.");
107 protected void onPreExecute()
113 * ビットマップデータを(PNG形式で)保管する。
115 private String exportToFile(String baseName, Bitmap targetImage)
117 String resultMessage = "";
120 String outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getPath() + "/" + APP_NAMESPACE + "/";
121 ContentResolver resolver = context.getContentResolver();
122 String fileName = baseName + ".png";
125 ContentValues values = new ContentValues();
126 values.put(MediaStore.Images.Media.TITLE, fileName);
127 values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
128 values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
129 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
131 values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + APP_NAMESPACE);
132 values.put(MediaStore.Images.Media.IS_PENDING, true);
133 extStorageUri = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
137 File path = new File(outputDir);
138 values.put(MediaStore.Images.Media.DATA, path.getAbsolutePath() + File.separator + fileName);
139 extStorageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
141 Log.v(TAG, "---------- " + baseName + ".png " + values);
142 Uri imageUri = resolver.insert(extStorageUri, values);
143 if (imageUri != null)
145 ////////////////////////////////////////////////////////////////
150 Cursor cursor = resolver.query(imageUri, null, null, null, null);
151 DatabaseUtils.dumpCursor(cursor);
157 resultMessage = e.getMessage();
160 ////////////////////////////////////////////////////////////////
162 OutputStream outputStream = resolver.openOutputStream(imageUri, "wa");
163 if (outputStream != null)
165 targetImage.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
166 outputStream.flush();
167 outputStream.close();
169 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
171 values.put(MediaStore.Images.Media.IS_PENDING, false);
172 resolver.update(imageUri, values, null, null);
177 Log.v(TAG, " cannot get imageUri...");
179 exportedUri = imageUri;
184 resultMessage = t.getMessage();
187 return (resultMessage);
191 * キャンバスの大きさがどれくらい必要か、チェックする。
193 private Rect checkCanvasSize()
195 Rect canvasSize = new Rect();
198 Enumeration<Integer> keys = objectHolder.getObjectKeys();
199 while (keys.hasMoreElements())
201 Integer key = keys.nextElement();
202 PositionObject pos = objectHolder.getPosition(key);
203 RectF posRect = pos.getRect();
204 if (canvasSize.left > posRect.left)
206 canvasSize.left = (int) posRect.left;
208 if (canvasSize.right < posRect.right)
210 canvasSize.right = (int) posRect.right;
212 if (canvasSize.top > posRect.top)
214 canvasSize.top = (int) posRect.top;
216 if (canvasSize.bottom < posRect.bottom)
218 canvasSize.bottom = (int) posRect.bottom;
223 canvasSize.left = canvasSize.left - OUTPUT_MARGIN;
224 canvasSize.right = canvasSize.right + OUTPUT_MARGIN;
225 canvasSize.top = canvasSize.top - OUTPUT_MARGIN_TOP;
226 canvasSize.bottom = canvasSize.bottom + OUTPUT_MARGIN;
230 if (displayWidth < MINIMUM_WIDTH)
232 displayWidth = MINIMUM_WIDTH;
234 if (displayHeight < MINIMUM_HEIGHT)
236 displayHeight = MINIMUM_HEIGHT;
239 // 出力の最小サイズを(表示画面サイズに)設定
240 if (canvasSize.width() < displayWidth)
242 canvasSize.right = canvasSize.left + displayWidth;
244 if (canvasSize.height() < displayHeight)
246 canvasSize.bottom = canvasSize.top + displayHeight;
249 // 画像位置(キャンバス位置)の調整。。。
250 offsetX = 0.0f - canvasSize.left - (OUTPUT_MARGIN);
251 offsetY = 0.0f - canvasSize.top - (OUTPUT_MARGIN);
253 // 出力する画像データのサイズを表示する
254 Log.v(TAG, "ObjectLayoutCaptureExporter::checkCanvasSize() w:" + canvasSize.width() + " , h:" + canvasSize.height() + " offset :(" + offsetX + "," + offsetY + ")");
260 * (バックグラウンドで実行する(このメソッドは、UIスレッドと別のところで実行する))
263 protected String doInBackground(String... datas)
268 Rect canvasSize = checkCanvasSize();
269 Bitmap targetBitmap = Bitmap.createBitmap(canvasSize.width(), canvasSize.height(), Bitmap.Config.RGB_565);
270 Canvas targetCanvas = new Canvas(targetBitmap);
272 // オブジェクトをビットマップの中に書き込む
273 canvasDrawer.drawOnBitmapCanvas(targetCanvas, offsetX, offsetY);
275 String fileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Calendar.getInstance().getTime()) + "_" + datas[0];
278 result = exportToFile(fileName, targetBitmap);
293 protected void onProgressUpdate(Integer... values)
303 protected void onPostExecute(String result)
307 if (receiver != null)
309 receiver.onCaptureLayoutExportedResult(exportedUri, result, OUTPUT_EXPORT_SHARE_ID);
314 Log.v(TAG, "ViewCaptureExporter::onPostExecute() : " + ex.getMessage());
315 ex.printStackTrace();
318 if (savingDialog != null)
320 savingDialog.dismiss();
327 public interface ICaptureLayoutExporter
330 void onCaptureLayoutExportedResult(Uri exportedUri, String detail, int id);