=== modified file 'CameraApp/advancedcamerasettings.cpp'
--- CameraApp/advancedcamerasettings.cpp	2015-01-08 13:44:13 +0000
+++ CameraApp/advancedcamerasettings.cpp	2015-10-14 13:58:46 +0000
@@ -27,6 +27,8 @@
 #include <QtMultimedia/QCameraFlashControl>
 #include <QtMultimedia/QCameraExposureControl>
 
+#include <cmath>
+
 // Definition of this enum value is duplicated in qtubuntu-camera
 static const QCameraExposure::ExposureMode ExposureHdr = static_cast<QCameraExposure::ExposureMode>(QCameraExposure::ExposureModeVendor + 1);
 
@@ -221,6 +223,12 @@
         QObject::connect(m_cameraControl,
                          SIGNAL(captureModeChanged(QCamera::CaptureModes)),
                          this, SIGNAL(resolutionChanged()));
+        QObject::connect(m_cameraControl,
+                         SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+                         this, SIGNAL(maximumResolutionChanged()));
+        QObject::connect(m_cameraControl,
+                         SIGNAL(captureModeChanged(QCamera::CaptureModes)),
+                         this, SIGNAL(fittingResolutionChanged()));
     }
 
     m_cameraFlashControl = flashControlFromCamera(m_camera);
@@ -236,6 +244,8 @@
     m_videoEncoderControl = videoEncoderControlFromCamera(m_camera);
 
     Q_EMIT resolutionChanged();
+    Q_EMIT maximumResolutionChanged();
+    Q_EMIT fittingResolutionChanged();
     Q_EMIT hasFlashChanged();
     Q_EMIT hasHdrChanged();
     Q_EMIT hdrEnabledChanged();
@@ -259,6 +269,8 @@
         }
         Q_EMIT activeCameraIndexChanged();
         Q_EMIT resolutionChanged();
+        Q_EMIT maximumResolutionChanged();
+        Q_EMIT fittingResolutionChanged();
         Q_EMIT hasFlashChanged();
         Q_EMIT videoSupportedResolutionsChanged();
     }
@@ -276,6 +288,83 @@
     return QSize();
 }
 
+QSize AdvancedCameraSettings::maximumResolution() const
+{
+    if (m_imageEncoderControl) {
+        QList<QSize> sizes = m_imageEncoderControl->supportedResolutions(
+                                       m_imageEncoderControl->imageSettings());
+
+        QSize maximumSize;
+        long maximumPixels = 0;
+
+        QList<QSize>::const_iterator it = sizes.begin();
+        while (it != sizes.end()) {
+            const long pixels = ((long)((*it).width())) * ((long)((*it).height()));
+            if (pixels > maximumPixels) {
+                maximumSize = *it;
+                maximumPixels = pixels;
+            }
+            ++it;
+        }
+
+        return maximumSize;
+    }
+
+    return QSize();
+}
+
+QSize AdvancedCameraSettings::fittingResolution() const
+{
+    QList<float> prioritizedAspectRatios;
+    const float backAspectRatios[4] = { 16.0f/9.0f, 3.0f/2.0f, 4.0f/3.0f, 5.0f/4.0f };
+    for (int i=0; i<4; ++i) {
+        if (!prioritizedAspectRatios.contains(backAspectRatios[i])) {
+            prioritizedAspectRatios.append(backAspectRatios[i]);
+        }
+    }
+
+    if (m_imageEncoderControl) {
+        QList<QSize> sizes = m_imageEncoderControl->supportedResolutions(
+                                       m_imageEncoderControl->imageSettings());
+
+        QSize optimalSize;
+        long optimalPixels = 0;
+
+        if (!sizes.empty()) {
+            float aspectRatio;
+
+            // Loop over all reported camera resolutions until we find the highest
+            // one that matches the current prioritized aspect ratio. If it doesn't
+            // find one on the current aspect ration, it selects the next ratio and
+            // tries again.
+            QList<float>::const_iterator ratioIt = prioritizedAspectRatios.begin();
+            while (ratioIt != prioritizedAspectRatios.end()) {
+                // Don't update the aspect ratio when using this function for finding
+                // the optimal thumbnail size as it will affect the preview window size
+                aspectRatio = (*ratioIt);
+
+                QList<QSize>::const_iterator it = sizes.begin();
+                while (it != sizes.end()) {
+                    const float ratio = (float)(*it).width() / (float)(*it).height();
+                    const long pixels = ((long)((*it).width())) * ((long)((*it).height()));
+                    const float EPSILON = 0.02;
+                    if (fabs(ratio - aspectRatio) < EPSILON && pixels > optimalPixels) {
+                        optimalSize = *it;
+                        optimalPixels = pixels;
+                    }
+                    ++it;
+                }
+                if (optimalPixels > 0) break;
+                ++ratioIt;
+            }
+        }
+
+        return optimalSize;
+    }
+
+    return QSize();
+}
+
 QStringList AdvancedCameraSettings::videoSupportedResolutions() const
 {
     if (m_videoEncoderControl) {

=== modified file 'CameraApp/advancedcamerasettings.h'
--- CameraApp/advancedcamerasettings.h	2014-12-08 19:12:52 +0000
+++ CameraApp/advancedcamerasettings.h	2015-10-14 13:58:46 +0000
@@ -40,6 +40,8 @@
     Q_PROPERTY (int activeCameraIndex READ activeCameraIndex WRITE setActiveCameraIndex
                 NOTIFY activeCameraIndexChanged)
     Q_PROPERTY (QSize resolution READ resolution NOTIFY resolutionChanged)
+    Q_PROPERTY (QSize maximumResolution READ maximumResolution NOTIFY maximumResolutionChanged)
+    Q_PROPERTY (QSize fittingResolution READ fittingResolution NOTIFY fittingResolutionChanged)
     Q_PROPERTY (QStringList videoSupportedResolutions READ videoSupportedResolutions NOTIFY videoSupportedResolutionsChanged)
     Q_PROPERTY (bool hasFlash READ hasFlash NOTIFY hasFlashChanged)
     Q_PROPERTY (bool hdrEnabled READ hdrEnabled WRITE setHdrEnabled NOTIFY hdrEnabledChanged)
@@ -53,6 +55,8 @@
     void setCamera(QObject* camera);
     void setActiveCameraIndex(int index);
     QSize resolution() const;
+    QSize maximumResolution() const;
+    QSize fittingResolution() const;
     QStringList videoSupportedResolutions() const;
     bool hasFlash() const;
     bool hasHdr() const;
@@ -66,6 +70,8 @@
     void cameraChanged();
     void activeCameraIndexChanged();
     void resolutionChanged();
+    void maximumResolutionChanged();
+    void fittingResolutionChanged();
     void hasFlashChanged();
     void hasHdrChanged();
     void hdrEnabledChanged();

=== modified file 'ViewFinderOverlay.qml'
--- ViewFinderOverlay.qml	2015-10-02 19:51:31 +0000
+++ ViewFinderOverlay.qml	2015-10-14 13:58:46 +0000
@@ -50,6 +50,7 @@
         property bool gridEnabled: false
         property bool preferRemovableStorage: false
         property string videoResolution: "1920x1080"
+        property string photoResolution
 
         onFlashModeChanged: if (flashMode != Camera.FlashOff) hdrEnabled = false;
         onHdrEnabledChanged: if (hdrEnabled) flashMode = Camera.FlashOff
@@ -87,12 +88,67 @@
         value: settings.videoResolution
     }
 
+    Binding {
+        target: camera.imageCapture
+        property: "resolution"
+        value: settings.photoResolution
+    }
+
+    Connections {
+        target: camera.imageCapture
+        onResolutionChanged: {
+            // FIXME: this is a necessary workaround because:
+            // - Neither camera.viewfinder.resolution nor camera.advanced.resolution
+            //   emit a changed signal when the underlying AalViewfinderSettingsControl's
+            //   resolution changes
+            // - we know that qtubuntu-camera changes the resolution of the
+            //   viewfinder automatically when the capture resolution is set
+            // - we need camera.viewfinder.resolution to hold the right
+            //   value
+            camera.viewfinder.resolution = camera.advanced.resolution;
+        }
+    }
+
     function resolutionToLabel(resolution) {
         // takes in a resolution string (e.g. "1920x1080") and returns a nicer
         // form of it for display in the UI: "1080p"
         return resolution.split("x").pop() + "p";
     }
 
+    function sizeToString(size) {
+        return size.width + "x" + size.height;
+    }
+
+    function stringToSize(resolution) {
+        var r = resolution.split("x");
+        return Qt.size(r[0], r[1]);
+    }
+
+    function sizeToAspectRatio(size) {
+        var ratio = Math.max(size.width, size.height) / Math.min(size.width, size.height);
+        var maxDenominator = 12;
+        var epsilon;
+        var numerator;
+        var denominator;
+        var bestDenominator;
+        var bestEpsilon = 10000;
+        for (denominator = 2; denominator <= maxDenominator; denominator++) {
+            numerator = ratio * denominator;
+            epsilon = Math.abs(Math.round(numerator) - numerator);
+            if (epsilon < bestEpsilon) {
+                bestEpsilon = epsilon;
+                bestDenominator = denominator;
+            }
+        }
+        numerator = Math.round(ratio * bestDenominator);
+        return "%1:%2".arg(numerator).arg(bestDenominator);
+    }
+
+    function sizeToMegapixels(size) {
+        var megapixels = (size.width * size.height) / 1000000;
+        return parseFloat(megapixels.toFixed(1))
+    }
+
     function updateVideoResolutionOptions() {
         // Clear and refill videoResolutionOptionsModel with available resolutions
         // Try to only display well known resolutions: 1080p, 720p and 480p
@@ -110,25 +166,63 @@
         }
 
         // If resolution setting chosen is not supported select the highest available resolution
-        if (supported.indexOf(settings.videoResolution) == -1) {
+        if (supported.length > 0 && supported.indexOf(settings.videoResolution) == -1) {
             settings.videoResolution = supported[supported.length - 1];
         }
     }
 
+    function updatePhotoResolutionOptions() {
+        // Clear and refill photoResolutionOptionsModel with available resolutions
+        photoResolutionOptionsModel.clear();
+
+        var optionMaximum = {"icon": "",
+                             "label": "%1 (%2MP)".arg(sizeToAspectRatio(camera.advanced.maximumResolution))
+                                                 .arg(sizeToMegapixels(camera.advanced.maximumResolution)),
+                             "value": sizeToString(camera.advanced.maximumResolution)};
+
+        var optionFitting = {"icon": "",
+                             "label": "%1 (%2MP)".arg(sizeToAspectRatio(camera.advanced.fittingResolution))
+                                                 .arg(sizeToMegapixels(camera.advanced.fittingResolution)),
+                             "value": sizeToString(camera.advanced.fittingResolution)};
+
+        if (camera.advanced.fittingResolution != camera.advanced.maximumResolution) {
+            photoResolutionOptionsModel.insert(0, optionMaximum);
+            photoResolutionOptionsModel.insert(1, optionFitting);
+        }
+
+        // If resolution setting chosen is not supported select the fitting resolution
+        if (settings.photoResolution != optionFitting.value &&
+            settings.photoResolution != optionMaximum.value) {
+            settings.photoResolution = optionFitting.value;
+        }
+    }
+
     Component.onCompleted: {
+        camera.cameraState = Camera.LoadedState;
+        camera.imageCapture.resolution = settings.photoResolution;
+        // FIXME: see workaround setting camera.viewfinder.resolution above
+        camera.viewfinder.resolution = camera.advanced.resolution;
         updateVideoResolutionOptions();
+        updatePhotoResolutionOptions();
+        camera.cameraState = Camera.ActiveState;
     }
 
     Connections {
         target: camera.advanced
         onVideoSupportedResolutionsChanged: updateVideoResolutionOptions();
+        onFittingResolutionChanged: updatePhotoResolutionOptions();
+        onMaximumResolutionChanged: updatePhotoResolutionOptions();
     }
 
     Connections {
         target: camera.advanced
         onActiveCameraIndexChanged: {
             updateVideoResolutionOptions();
+            updatePhotoResolutionOptions();
             camera.videoRecorder.resolution = settings.videoResolution;
+            camera.imageCapture.resolution = settings.photoResolution;
+            // FIXME: see workaround setting camera.viewfinder.resolution above
+            camera.viewfinder.resolution = camera.advanced.resolution;
         }
     }
 
@@ -404,6 +498,18 @@
                     property bool available: true
                     property bool visible: camera.captureMode == Camera.CaptureVideo
                     property bool showInIndicators: false
+                },
+                ListModel {
+                    id: photoResolutionOptionsModel
+
+                    property string settingsProperty: "photoResolution"
+                    property string icon: ""
+                    property string label: sizeToAspectRatio(stringToSize(settings.photoResolution))
+                    property bool isToggle: false
+                    property int selectedIndex: bottomEdge.indexForValue(photoResolutionOptionsModel, settings.photoResolution)
+                    property bool available: true
+                    property bool visible: camera.captureMode == Camera.CaptureStillImage && count > 1
+                    property bool showInIndicators: false
                 }
             ]
 

=== modified file 'ViewFinderView.qml'
--- ViewFinderView.qml	2015-07-03 11:03:42 +0000
+++ ViewFinderView.qml	2015-10-14 13:58:46 +0000
@@ -35,6 +35,7 @@
     Camera {
         id: camera
         captureMode: Camera.CaptureStillImage
+        cameraState: Camera.UnloadedState
         StateSaver.properties: "captureMode"
 
         function manualFocus(x, y) {
@@ -66,10 +67,6 @@
             StateSaver.properties: "activeCameraIndex"
         }
 
-        Component.onCompleted: {
-            camera.start();
-        }
-        
         /* Use only digital zoom for now as it's what phone cameras mostly use.
                TODO: if optical zoom is available, maximumZoom should be the combined
                range of optical and digital zoom and currentZoom should adjust the two
@@ -120,12 +117,14 @@
         target: Qt.application
         onActiveChanged: {
             if (Qt.application.active) {
-                camera.start()
+                if (camera.cameraState == Camera.LoadedState) {
+                    camera.cameraState = Camera.ActiveState;
+                }
             } else if (!application.desktopMode) {
                 if (camera.videoRecorder.recorderState == CameraRecorder.RecordingState) {
                     camera.videoRecorder.stop();
                 }
-                camera.stop()
+                camera.cameraState = Camera.LoadedState;
             }
         }
     }
@@ -239,16 +238,16 @@
                 axis.x: 0; axis.y: 1; axis.z: 0
                 angle: application.desktopMode ? 180 : 0
             }
-
-            ViewFinderGeometry {
-                id: viewFinderGeometry
-                anchors.centerIn: parent
-
-                cameraResolution: camera.advanced.resolution
-                viewFinderHeight: viewFinder.height
-                viewFinderWidth: viewFinder.width
-                viewFinderOrientation: viewFinder.orientation
-            }
+        }
+
+        ViewFinderGeometry {
+            id: viewFinderGeometry
+            anchors.centerIn: parent
+
+            cameraResolution: camera.viewfinder.resolution
+            viewFinderHeight: viewFinder.height
+            viewFinderWidth: viewFinder.width
+            viewFinderOrientation: viewFinder.orientation
         }
 
         Item {

