/** * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>) * <p/> * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version * <p/> * This program 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 Affero General Public License for more details * <p/> * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http:www.gnu.org/licenses/> * <p/> * Created on 16/1/15 3:36 PM */ package com.odoo.base.addons.ir.feature; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.ContentValues; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.OpenableColumns; import android.util.Base64; import android.webkit.MimeTypeMap; import android.widget.Toast; import com.odoo.App; import com.odoo.R; import com.odoo.base.addons.ir.IrAttachment; import com.odoo.core.orm.ODataRow; import com.odoo.core.orm.OValues; import com.odoo.core.orm.fields.OColumn; import com.odoo.core.support.OdooCompatActivity; import com.odoo.core.tools.permissions.DevicePermissionHelper; import com.odoo.core.utils.BitmapUtils; import com.odoo.core.utils.OAlert; import com.odoo.core.utils.OResource; import com.odoo.core.utils.OStorageUtils; import com.odoo.core.utils.notification.ONotificationBuilder; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.FileNameMap; import java.net.URLConnection; import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.makeText; public class OFileManager implements DialogInterface.OnClickListener { public static final String TAG = OFileManager.class.getSimpleName(); public static final int REQUEST_CAMERA = 111; public static final int REQUEST_IMAGE = 112; public static final int REQUEST_AUDIO = 113; public static final int REQUEST_FILE = 114; public static final int REQUEST_ALL_FILE = 115; private static final int SINGLE_ATTACHMENT_STREAM = 115; private static final long IMAGE_MAX_SIZE = 1000000; // 1 MB private OdooCompatActivity mActivity = null; private String[] mOptions = null; private RequestType requestType = null; private Uri newImageUri = null; private IrAttachment irAttachment = null; private App mApp; private DevicePermissionHelper devicePermissionHelper; public enum RequestType { CAPTURE_IMAGE, IMAGE, IMAGE_OR_CAPTURE_IMAGE, AUDIO, FILE, ALL_FILE_TYPE } public OFileManager(OdooCompatActivity activity) { mActivity = activity; irAttachment = new IrAttachment(mActivity, null); mApp = (App) mActivity.getApplicationContext(); devicePermissionHelper = new DevicePermissionHelper(activity); } public void downloadAttachment(final int attachment_id) { if (devicePermissionHelper.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _downloadAttachment(attachment_id); } else { devicePermissionHelper.requestToGrantPermission(new DevicePermissionHelper .PermissionGrantListener() { @Override public void onPermissionGranted() { _downloadAttachment(attachment_id); } @Override public void onPermissionDenied() { Toast.makeText(mActivity, R.string.toast_permission_download_storage, Toast.LENGTH_LONG).show(); } @Override public void onPermissionRationale() { Toast.makeText(mActivity, R.string.toast_permission_download_storage_help, Toast.LENGTH_LONG).show(); } }, Manifest.permission.WRITE_EXTERNAL_STORAGE); } } private void _downloadAttachment(int id) { ODataRow attachment = irAttachment.browse(id); if (attachment != null) { String uri = attachment.getString("file_uri"); if (uri.equals("false")) { // Downloading new file _download(attachment); } else { Uri file_uri = Uri.parse(uri); if (fileExists(file_uri)) { requestIntent(file_uri); } else if (atLeastKitKat()) { String file_path = getDocPath(file_uri); if (file_path != null) { file_uri = Uri.fromFile(new File(file_path)); requestIntent(file_uri); } else if (attachment.getInt("id") != 0) { // Downloading new file _download(attachment); } else { // Failed to get file OAlert.showAlert(mActivity, "Unable to find file !"); } } else if (fileExists(file_uri)) { requestIntent(file_uri); } else { // Failed to get file OAlert.showAlert(mActivity, "Unable to find file !"); } } } } private void _download(ODataRow attachment) { ONotificationBuilder builder = new ONotificationBuilder(mActivity, attachment.getInt(OColumn.ROW_ID)); builder.setTitle("Downloading " + attachment.getString("name")); builder.setText("Download in progress"); builder.setOngoing(true); builder.setAutoCancel(true); DownloadManager downloader = new DownloadManager(builder); downloader.execute(attachment); } private class DownloadManager extends AsyncTask<ODataRow, Void, ODataRow> { ONotificationBuilder notificationBuilder; public DownloadManager(ONotificationBuilder builder) { notificationBuilder = builder; } @Override protected void onPreExecute() { super.onPreExecute(); notificationBuilder.allowVibrate(false); notificationBuilder.withRingTone(false); notificationBuilder.setProgress(0, 0, true); notificationBuilder.build().show(); } @Override protected ODataRow doInBackground(ODataRow... params) { if (mApp.inNetwork()) { try { Thread.sleep(500); ODataRow attachment = params[0]; String base64 = irAttachment.getDatasFromServer(attachment.getInt(OColumn.ROW_ID)); if (!base64.equals("false")) { String file = createFile(attachment.getString("name"), Base64.decode(base64, 0) , attachment.getString("file_type")); Uri uri = Uri.fromFile(new File(file)); OValues values = new OValues(); values.put("file_uri", uri.toString()); irAttachment.update(attachment.getInt(OColumn.ROW_ID), values); return irAttachment.browse(attachment.getInt(OColumn.ROW_ID)); } } catch (Exception e) { e.printStackTrace(); } } return null; } @Override protected void onPostExecute(ODataRow row) { super.onPostExecute(row); if (row != null) { ONotificationBuilder.cancelNotification(mActivity, row.getInt(OColumn.ROW_ID)); ONotificationBuilder builder = new ONotificationBuilder(mActivity, row.getInt(OColumn.ROW_ID)); builder.allowVibrate(true); builder.withRingTone(true); builder.setTitle(row.getString("name")); builder.setText("Download Complete"); builder.setBigText("Download Complete"); if (row.getString("file_type").contains("image")) { Bitmap bmp = getBitmapFromURI(Uri.parse(row.getString("file_uri"))); builder.setBigPicture(bmp); } Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse(row.getString("file_uri")), row.getString("file_type")); builder.setResultIntent(intent); builder.build().show(); } else { ONotificationBuilder.cancelNotification(mActivity); } } } private String createFile(String name, byte[] fileAsBytes, String file_type) { InputStream is = new ByteArrayInputStream(fileAsBytes); String filename = name.replaceAll("[-+^:=, ]", "_"); String file_path = OStorageUtils.getDirectoryPath(file_type) + "/" + filename; try { FileOutputStream fos = new FileOutputStream(file_path); byte data[] = new byte[1024]; int count = 0; while ((count = is.read(data)) != -1) { fos.write(data, 0, count); } is.close(); fos.close(); } catch (Exception e) { e.printStackTrace(); } return file_path; } private void requestIntent(Uri uri) { Intent intent = new Intent(Intent.ACTION_VIEW); FileNameMap mime = URLConnection.getFileNameMap(); String mimeType = mime.getContentTypeFor(uri.getPath()); intent.setDataAndType(uri, mimeType); try { mActivity.startActivity(intent); } catch (ActivityNotFoundException e) { Toast.makeText(mActivity, OResource.string(mActivity, R.string.toast_no_activity_found_to_handle_file), Toast.LENGTH_LONG).show(); } } private boolean fileExists(Uri uri) { return new File(uri.getPath()).exists(); } public Bitmap getBitmapFromURI(Uri uri) { Bitmap bitmap; if (!fileExists(uri) && atLeastKitKat()) { String path = getDocPath(uri); bitmap = BitmapUtils.getBitmapImage(mActivity, BitmapUtils.uriToBase64(Uri.fromFile(new File(path)), mActivity.getContentResolver())); } else { bitmap = BitmapUtils.getBitmapImage(mActivity, BitmapUtils.uriToBase64(uri, mActivity.getContentResolver())); } return bitmap; } @TargetApi(Build.VERSION_CODES.KITKAT) public String getDocPath(Uri uri) { String wholeID = DocumentsContract.getDocumentId(uri); String id = wholeID.split(":")[1]; String[] column = {MediaStore.Images.Media.DATA}; String sel = MediaStore.Images.Media._ID + "=?"; Cursor cursor = mActivity.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{id}, null); String filePath = null; int columnIndex = cursor.getColumnIndex(column[0]); if (cursor.moveToFirst()) { filePath = cursor.getString(columnIndex); } cursor.close(); return filePath; } public boolean atLeastKitKat() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; } public void requestForFile(final RequestType type) { if (devicePermissionHelper.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { _requestForFile(type); } else { devicePermissionHelper.requestToGrantPermission(new DevicePermissionHelper .PermissionGrantListener() { @Override public void onPermissionGranted() { _requestForFile(type); } @Override public void onPermissionDenied() { Toast.makeText(mActivity, R.string.toast_permission_download_storage, Toast.LENGTH_LONG).show(); } @Override public void onPermissionRationale() { Toast.makeText(mActivity, R.string.toast_permission_download_storage_help, Toast.LENGTH_LONG).show(); } }, Manifest.permission.WRITE_EXTERNAL_STORAGE); } } public void _requestForFile(RequestType type) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_GET_CONTENT); switch (type) { case AUDIO: intent.setType("audio/*"); requestIntent(intent, REQUEST_AUDIO); break; case IMAGE: if (Build.VERSION.SDK_INT < 19) { intent = new Intent(); intent.setAction(Intent.ACTION_GET_CONTENT); } else { intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); } intent.setType("image/*"); requestIntent(intent, REQUEST_IMAGE); break; case CAPTURE_IMAGE: ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.TITLE, "Odoo Mobile Attachment"); values.put(MediaStore.Images.Media.DESCRIPTION, "Captured from Odoo Mobile App"); newImageUri = mActivity.getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, newImageUri); requestIntent(intent, REQUEST_CAMERA); break; case IMAGE_OR_CAPTURE_IMAGE: requestDialog(type); break; case FILE: intent.setType("application/*"); requestIntent(intent, REQUEST_FILE); break; case ALL_FILE_TYPE: intent.setType("*/*"); requestIntent(intent, REQUEST_ALL_FILE); break; } } public OValues getURIDetails(Uri uri) { OValues values = new OValues(); ContentResolver mCR = mActivity.getContentResolver(); if (uri.getScheme().equals("content")) { Cursor cr = mCR.query(uri, null, null, null, null); int nameIndex = cr.getColumnIndex(OpenableColumns.DISPLAY_NAME); int fileSize = cr.getColumnIndex(OpenableColumns.SIZE); if (cr.moveToFirst()) { values.put("name", cr.getString(nameIndex)); values.put("datas_fname", values.get("name")); values.put("file_size", Long.toString(cr.getLong(fileSize))); String path = getPath(uri); if (path != null) { values.put("file_size", new File(path).length() + ""); } } cr.close(); } if (uri.getScheme().equals("file")) { File file = new File(uri.toString()); values.put("name", file.getName()); values.put("datas_fname", values.get("name")); values.put("file_size", Long.toString(file.length())); } values.put("file_uri", uri.toString()); values.put("scheme", uri.getScheme()); MimeTypeMap mime = MimeTypeMap.getSingleton(); String type = mime.getMimeTypeFromExtension(mime .getExtensionFromMimeType(mCR.getType(uri))); values.put("file_type", (type == null) ? uri.getScheme() : type); values.put("type", type); return values; } public String getPath(Uri uri) { ContentResolver mCR = mActivity.getContentResolver(); String[] projection = {MediaStore.Images.Media.DATA}; Cursor cursor = mCR.query(uri, projection, null, null, null); if (cursor == null) return null; int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String s = cursor.getString(column_index); cursor.close(); return s; } public OValues handleResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case REQUEST_CAMERA: OValues values = getURIDetails(newImageUri); values.put("datas", BitmapUtils.uriToBase64(newImageUri, mActivity.getContentResolver(), true)); return values; case REQUEST_IMAGE: values = getURIDetails(data.getData()); values.put("datas", BitmapUtils.uriToBase64(data.getData(), mActivity.getContentResolver(), true)); return values; case REQUEST_ALL_FILE: default: return getURIDetails(data.getData()); } } return null; } private void requestIntent(Intent intent, int requestCode) { try { mActivity.startActivityForResult(intent, requestCode); } catch (ActivityNotFoundException e) { makeText(mActivity, "No Activity Found to handle request", LENGTH_SHORT).show(); } } private void requestDialog(RequestType type) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); switch (type) { case IMAGE_OR_CAPTURE_IMAGE: requestType = type; mOptions = new String[]{"Select Image", "Capture Image"}; break; } builder.setSingleChoiceItems(mOptions, -1, this); builder.create().show(); } @Override public void onClick(DialogInterface dialog, int which) { switch (requestType) { case IMAGE_OR_CAPTURE_IMAGE: requestForFile((which == 0) ? RequestType.IMAGE : RequestType.CAPTURE_IMAGE); break; } dialog.dismiss(); } }