From bfab14264c6b8aee87d0383df8c00ca844c3bee4 Mon Sep 17 00:00:00 2001
From: jinwu <jinwu@codeaurora.org>
Date: Fri, 21 Sep 2018 18:33:37 +0800
Subject: [PATCH] SnapdraongCamera:add HEIF support

add option to allow user to save captured
image with HEIF format.

Change-Id: I267b783bf7e4f41f523ef2ac4fbb734657b8e827
CRs-Fixed: 2312764
---
 Android.mk                                    |  1 +
 res/values/camera2arrays.xml                  | 10 +++
 res/values/qcomstrings.xml                    |  5 ++
 res/xml/capture_preferences.xml               |  8 +++
 res/xml/setting_menu_preferences.xml          | 10 +++
 src/com/android/camera/CaptureModule.java     | 19 ++++++
 src/com/android/camera/MediaSaveService.java  | 68 +++++++++++++++++++
 src/com/android/camera/SettingsActivity.java  |  1 +
 src/com/android/camera/SettingsManager.java   | 11 +++
 src/com/android/camera/Storage.java           | 66 ++++++++++++++++--
 .../camera/imageprocessor/PostProcessor.java  | 16 ++++-
 11 files changed, 207 insertions(+), 8 deletions(-)

diff --git a/Android.mk b/Android.mk
index 557a04750..ba490b2e3 100755
--- a/Android.mk
+++ b/Android.mk
@@ -8,6 +8,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
 LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit
+LOCAL_STATIC_JAVA_LIBRARIES += androidx.heifwriter_heifwriter
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += $(call all-java-files-under, src_pd)
diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml
index e05303325..2916736cf 100755
--- a/res/values/camera2arrays.xml
+++ b/res/values/camera2arrays.xml
@@ -508,6 +508,16 @@
         <item>320x240</item>
     </string-array>
 
+    <string-array name="pref_camera2_picture_format_entries" translatable="false">
+        <item>@string/pref_camera2_picture_format_entry_0</item>
+        <item>@string/pref_camera2_picture_format_entry_1</item>
+    </string-array>
+
+    <string-array name="pref_camera2_picture_format_entryvalues" translatable="false">
+        <item>0</item>
+        <item>1</item>
+    </string-array>
+
     <!-- Camera Preferences focus mode dialog box entries -->
     <string-array name="pref_camera2_focusmode_entries" translatable="false">
         <item>@string/pref_camera_focusmode_entry_auto</item>
diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml
index bf6deb455..46cc2ab9d 100755
--- a/res/values/qcomstrings.xml
+++ b/res/values/qcomstrings.xml
@@ -1258,6 +1258,11 @@
     <string name="pref_camera2_video_hdr_entry_value_enable" translatable="false">1</string>
     <string name="pref_camera2_video_hdr_entry_value_disable" translatable="false">0</string>
 
+    <string name="pref_camera2_picture_format_title">Picture Format</string>
+    <string name="pref_camera2_picture_format_default">0</string>
+    <string name="pref_camera2_picture_format_entry_0">JPEG</string>
+    <string name="pref_camera2_picture_format_entry_1">HEIF</string>
+
     <string name="pref_camera2_deepportrait_entry_value_disable" translatable="false">off</string>
     <string name="pref_camera2_deepportrait_entry_value_enable" translatable="false">on</string>
     <string name="pref_camera_scenemode_entry_deepportrait" translatable="false">Deepportrait</string>
diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml
index bc74e280b..7575eb1a6 100755
--- a/res/xml/capture_preferences.xml
+++ b/res/xml/capture_preferences.xml
@@ -156,6 +156,14 @@
         camera:singleIcon="@drawable/ic_settings_picturesize"
         camera:title="@string/pref_camera_picturesize_title"/>
 
+    <IconListPreference
+        camera:defaultValue="@string/pref_camera2_picture_format_default"
+        camera:entries="@array/pref_camera2_picture_format_entries"
+        camera:entryValues="@array/pref_camera2_picture_format_entryvalues"
+        camera:key="pref_camera2_picture_format_key"
+        camera:singleIcon="@drawable/ic_settings_storage"
+        camera:title="@string/pref_camera2_picture_format_title"/>
+
     <IconListPreference
         camera:defaultValue="@string/pref_exposure_default"
         camera:key="pref_camera2_exposure_key"
diff --git a/res/xml/setting_menu_preferences.xml b/res/xml/setting_menu_preferences.xml
index da47590be..a9268e6cc 100755
--- a/res/xml/setting_menu_preferences.xml
+++ b/res/xml/setting_menu_preferences.xml
@@ -141,6 +141,16 @@
             android:layout="@layout/preference"
             android:title="@string/pref_camera2_shutter_sound_title" />
 
+        <ListPreference
+            android:defaultValue="@string/pref_camera2_picture_format_default"
+            android:entries="@array/pref_camera2_picture_format_entries"
+            android:entryValues="@array/pref_camera2_picture_format_entryvalues"
+            android:icon="@drawable/ic_settings_storage"
+            android:key="pref_camera2_picture_format_key"
+            android:layout="@layout/preference"
+            android:summary="%s"
+            android:title="@string/pref_camera2_picture_format_title"/>
+
         <SwitchPreference
             android:defaultValue="false"
             android:key="pref_camera2_mono_only_key"
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index 7bfee3041..0c93f9b73 100755
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -31,6 +31,7 @@ import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -2422,6 +2423,24 @@ public class CaptureModule implements CameraModule, PhotoController,
 
                                     byte[] bytes = getJpegData(image);
 
+                                    if (mSettingsManager.getSavePictureFormat() ==
+                                            SettingsManager.HEIF_FORMAT) {
+                                        String value = mSettingsManager.getValue(
+                                                SettingsManager.KEY_JPEG_QUALITY);
+                                        int qualityNumber = getQualityNumber(value);
+                                        mActivity.getMediaSaveService().addHEIFImageFromJpeg(bytes,
+                                                title,date,null,image.getWidth(),image.getHeight(),
+                                                0,null,mContentResolver,
+                                                mOnMediaSavedListener,qualityNumber,"heif");
+                                        image.close();
+                                        if (mLongshotActive) {
+                                            mLastJpegData = bytes;
+                                        } else {
+                                            mActivity.updateThumbnail(bytes);
+                                        }
+                                        return;
+                                    }
+
                                     if (image.getFormat() == ImageFormat.RAW10) {
                                         mActivity.getMediaSaveService().addRawImage(bytes, title,
                                                 "raw");
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
index 217f44f27..8394162db 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -163,6 +163,23 @@ public class MediaSaveService extends Service {
         t.execute();
     }
 
+    public void addHEIFImageFromJpeg(byte[] data, String title, long date, Location loc,
+                             int width, int height, int orientation, ExifInterface exif,
+                             ContentResolver resolver, OnMediaSavedListener listener,
+                             int qualitiy, String pictureFormat) {
+        if (isQueueFull()) {
+            Log.e(TAG, "Cannot add image when the queue is full");
+            return;
+        }
+        HEIFImageSaveTask t = new HEIFImageSaveTask(data, title, date, loc, width, height, orientation,
+                exif, resolver, listener, qualitiy, pictureFormat);
+        mMemoryUse += data.length;
+        if (isQueueFull()) {
+            onQueueFull();
+        }
+        t.execute();
+    }
+
     public void addClearsightImage(byte[] clearsight, GImage bayer, GDepth.DepthMap depthMap,
                                    String title, long date, Location loc, int width, int height,
                                    int orientation, ExifInterface exif,
@@ -328,6 +345,57 @@ public class MediaSaveService extends Service {
         }
     }
 
+    private class HEIFImageSaveTask extends AsyncTask<Void, Void, Uri> {
+        private byte[] data;
+        private String title;
+        private long date;
+        private Location loc;
+        private int width, height;
+        private int orientation;
+        private ExifInterface exif;
+        private ContentResolver resolver;
+        private OnMediaSavedListener listener;
+        private int quality;
+        private String pictureFormat;
+
+        public HEIFImageSaveTask(byte[] data, String title, long date, Location loc,
+                                 int width, int height, int orientation, ExifInterface exif,
+                                 ContentResolver resolver, OnMediaSavedListener listener,
+                                 int quality,String pictureFormat) {
+            this.data = data;
+            this.title = title;
+            this.date = date;
+            this.loc = loc;
+            this.width = width;
+            this.height = height;
+            this.orientation = orientation;
+            this.exif = exif;
+            this.resolver = resolver;
+            this.listener = listener;
+            this.quality = quality;
+            this.pictureFormat = pictureFormat;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            super.onPreExecute();
+        }
+
+        @Override
+        protected Uri doInBackground(Void... params) {
+            return Storage.addHeifImage(
+                    resolver,title,date,loc,orientation,exif,data,
+                    width,height,quality,pictureFormat);
+        }
+
+        @Override
+        protected void onPostExecute(Uri uri) {
+            boolean previouslyFull = isQueueFull();
+            mMemoryUse -= data.length;
+            if (isQueueFull() != previouslyFull) onQueueAvailable();
+        }
+    }
+
     private class ImageSaveTask extends AsyncTask <Void, Void, Uri> {
         private byte[] data;
         private String title;
diff --git a/src/com/android/camera/SettingsActivity.java b/src/com/android/camera/SettingsActivity.java
index e413090a4..e0330063a 100755
--- a/src/com/android/camera/SettingsActivity.java
+++ b/src/com/android/camera/SettingsActivity.java
@@ -452,6 +452,7 @@ public class SettingsActivity extends PreferenceActivity {
 
     private void initializePreferences() {
         updatePreference(SettingsManager.KEY_PICTURE_SIZE);
+        updatePreference(SettingsManager.KEY_PICTURE_FORMAT);
         updatePreference(SettingsManager.KEY_VIDEO_QUALITY);
         updatePreference(SettingsManager.KEY_EXPOSURE);
         updatePreference(SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE);
diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java
index 7640cb53b..4346f2599 100755
--- a/src/com/android/camera/SettingsManager.java
+++ b/src/com/android/camera/SettingsManager.java
@@ -102,6 +102,8 @@ public class SettingsManager implements ListMenu.SettingsListener {
     public static final int SCENE_MODE_PROMODE_INT = SCENE_MODE_CUSTOM_START + 9;
     public static final int SCENE_MODE_DEEPZOOM_INT = SCENE_MODE_CUSTOM_START + 10;
 	public static final int SCENE_MODE_DEEPPORTRAIT_INT = SCENE_MODE_CUSTOM_START + 11;
+    public static final int JPEG_FORMAT = 0;
+    public static final int HEIF_FORMAT = 1;
     public static final String SCENE_MODE_DUAL_STRING = "100";
     public static final String SCENE_MODE_SUNSET_STRING = "10";
     public static final String SCENE_MODE_LANDSCAPE_STRING = "4";
@@ -124,6 +126,7 @@ public class SettingsManager implements ListMenu.SettingsListener {
     public static final String KEY_CAMERA_ID = "pref_camera2_id_key";
     public static final String KEY_SWITCH_CAMERA = "pref_camera2_switch_camera_key";
     public static final String KEY_PICTURE_SIZE = "pref_camera2_picturesize_key";
+    public static final String KEY_PICTURE_FORMAT = "pref_camera2_picture_format_key";
     public static final String KEY_ISO = "pref_camera2_iso_key";
     public static final String KEY_EXPOSURE = "pref_camera2_exposure_key";
     public static final String KEY_TIMER = "pref_camera2_timer_key";
@@ -1693,11 +1696,19 @@ public class SettingsManager implements ListMenu.SettingsListener {
         return num_val;
     }
 
+
+
     public boolean isCamera2HDRSupport(){
         String value = getValue(KEY_HDR);
         return value != null && value.equals("enable");
     }
 
+    public int getSavePictureFormat() {
+        String value = getValue(SettingsManager.KEY_PICTURE_FORMAT);
+        if (value == null) return 0;
+        return Integer.valueOf(value);
+    }
+
     public boolean isZSLInHALEnabled(){
         String value = getValue(KEY_ZSL);
         String halZSLValue = mContext.getString(R.string.pref_camera2_zsl_entryvalue_hal_zsl);
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index 13f65e14c..a71a88a47 100755
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -18,14 +18,18 @@ package com.android.camera;
 
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 
 import android.annotation.TargetApi;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.location.Location;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
+import android.os.Handler;
 import android.os.StatFs;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Images.ImageColumns;
@@ -35,6 +39,8 @@ import android.util.Log;
 import com.android.camera.data.LocalData;
 import com.android.camera.exif.ExifInterface;
 import com.android.camera.util.ApiHelper;
+import androidx.heifwriter.HeifWriter;
+import android.graphics.ImageFormat;
 
 public class Storage {
     private static final String TAG = "CameraStorage";
@@ -135,13 +141,24 @@ public class Storage {
         values.put(ImageColumns.TITLE, title);
         if (mimeType.equalsIgnoreCase("jpeg") ||
             mimeType.equalsIgnoreCase("image/jpeg") ||
+                mimeType.equalsIgnoreCase("heif") ||
             mimeType == null) {
-            values.put(ImageColumns.DISPLAY_NAME, title + ".jpg");
+
+            if (mimeType.equalsIgnoreCase("heif")){
+                values.put(ImageColumns.DISPLAY_NAME, title + ".heic");
+            } else {
+                values.put(ImageColumns.DISPLAY_NAME, title + ".jpg");
+            }
+
         } else {
             values.put(ImageColumns.DISPLAY_NAME, title + ".raw");
         }
         values.put(ImageColumns.DATE_TAKEN, date);
-        values.put(ImageColumns.MIME_TYPE, "image/jpeg");
+        if (mimeType.equalsIgnoreCase("heif")) {
+            values.put(ImageColumns.MIME_TYPE, "image/heif");
+        } else {
+            values.put(ImageColumns.MIME_TYPE, "image/jpeg");
+        }
         // Clockwise rotation in degrees. 0, 90, 180, or 270.
         values.put(ImageColumns.ORIENTATION, orientation);
         values.put(ImageColumns.DATA, path);
@@ -180,6 +197,40 @@ public class Storage {
         return size;
     }
 
+    public static Uri addHeifImage(ContentResolver resolver, String title, long date,
+                                   Location location, int orientation, ExifInterface exif, byte[] data, int width,
+                                   int height, int quality, String mimeType) {
+        String path = generateFilepath(title, mimeType);
+        Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
+        if (bitmap != null) {
+            try {
+                HeifWriter.Builder builder =
+                        new HeifWriter.Builder(path,width, height,HeifWriter.INPUT_MODE_BITMAP);
+                builder.setQuality(quality);
+                builder.setMaxImages(1);
+                builder.setPrimaryIndex(0);
+                builder.setRotation(orientation);
+                HeifWriter heifWriter = builder.build();
+                heifWriter.start();
+                heifWriter.addBitmap(bitmap);
+                heifWriter.stop(3000);
+                heifWriter.close();
+            } catch (IOException|IllegalStateException e) {
+                e.printStackTrace();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            bitmap.recycle();
+        }
+        File f = new File(path);
+        int size = 0;
+        if (f.exists() && f.isFile()) {
+            size = (int) f.length();
+        }
+        return addImage(resolver, title, date, location, orientation,
+                size, path, width, height, mimeType);
+    }
+
     // Overwrites the file and updates the MediaStore, or inserts the image if
     // one does not already exist.
     public static void updateImage(Uri imageUri, ContentResolver resolver, String title, long date,
@@ -224,11 +275,16 @@ public class Storage {
     }
 
     public static String generateFilepath(String title, String pictureFormat) {
-        if (pictureFormat == null || pictureFormat.equalsIgnoreCase("jpeg")) {
+        if (pictureFormat == null || pictureFormat.equalsIgnoreCase("jpeg")
+                || pictureFormat.equalsIgnoreCase("heif")) {
+            String suffix = ".jpg";
+            if (pictureFormat.equalsIgnoreCase("heif")) {
+                suffix = ".heic";
+            }
             if (isSaveSDCard() && SDCard.instance().isWriteable()) {
-                return SDCard.instance().getDirectory() + '/' + title + ".jpg";
+                return SDCard.instance().getDirectory() + '/' + title + suffix;
             } else {
-                return DIRECTORY + '/' + title + ".jpg";
+                return DIRECTORY + '/' + title + suffix;
             }
         } else {
             return RAW_DIRECTORY + '/' + title + ".raw";
diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java
index c227f32b2..f91f6b031 100755
--- a/src/com/android/camera/imageprocessor/PostProcessor.java
+++ b/src/com/android/camera/imageprocessor/PostProcessor.java
@@ -1278,9 +1278,19 @@ public class PostProcessor{
                             mController.showCapturedReview(bytes, orientation);
                         }
                     } else {
-                        mActivity.getMediaSaveService().addImage(
-                                bytes, title, date, null, image.getCropRect().width(), image.getCropRect().height(),
-                                orientation, null, mController.getMediaSavedListener(), mActivity.getContentResolver(), "jpeg");
+                        if(SettingsManager.getInstance().getSavePictureFormat() ==
+                                SettingsManager.HEIF_FORMAT) {
+                            String value = SettingsManager.getInstance().getValue(
+                                    SettingsManager.KEY_JPEG_QUALITY);
+                            int qualityNumber = CaptureModule.getQualityNumber(value);
+                            mActivity.getMediaSaveService().addHEIFImageFromJpeg(bytes,title,date,null,
+                                    image.getWidth(),image.getHeight(),orientation,null,mActivity.getContentResolver(),
+                                    mController.getMediaSavedListener(),qualityNumber,"heif");
+                        } else {
+                            mActivity.getMediaSaveService().addImage(
+                                    bytes, title, date, null, image.getCropRect().width(), image.getCropRect().height(),
+                                    orientation, null, mController.getMediaSavedListener(), mActivity.getContentResolver(), "jpeg");
+                        }
                         mController.updateThumbnailJpegData(bytes);
                         image.close();
                     }
-- 
GitLab