Advertisement
Guest User

CaptureView.qml

a guest
Nov 2nd, 2017
188
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
QML 23.48 KB | None | 0 0
  1. import QtQuick 2.4
  2. import QtMultimedia 5.4
  3. import Sailfish.Silica 1.0
  4. import Sailfish.Media 1.0
  5. import com.jolla.camera 1.0
  6. import org.nemomobile.policy 1.0
  7. import org.nemomobile.ngf 1.0
  8. import org.nemomobile.dbus 2.0
  9. import QtSystemInfo 5.0
  10.  
  11. import "../settings"
  12.  
  13. FocusScope {
  14.     id: captureView
  15.  
  16.     property bool active
  17.     property bool windowVisible
  18.     property int orientation
  19.     property int effectiveIso: Settings.global.iso
  20.     property bool inButtonLayout: captureOverlay == null || captureOverlay.inButtonLayout
  21.  
  22.     readonly property int viewfinderOrientation: {
  23.         var rotation = 0
  24.         switch (captureView.orientation) {
  25.         case Orientation.Landscape: rotation = 90; break;
  26.         case Orientation.PortraitInverted: rotation = 180; break;
  27.         case Orientation.LandscapeInverted: rotation = 270; break;
  28.         }
  29.         return camera.position == Camera.FrontFace                                                                                                                                      
  30.                 ? (720 + camera.orientation - rotation) % 360                                                                                                                          
  31.                 : (720 + camera.orientation + rotation) % 360                                                                                                                          
  32.     }                                                                                                                                                                                  
  33.     property int captureOrientation                                                                                                                                                    
  34.     property int pageRotation
  35.  
  36.     property alias camera: camera
  37.     property QtObject viewfinder
  38.  
  39.     readonly property bool recording: active && camera.videoRecorder.recorderState == CameraRecorder.RecordingState
  40.  
  41.     property bool _complete
  42.     property bool _unload
  43.  
  44.     property bool touchFocusSupported: (camera.focus.focusMode == Camera.FocusAuto || camera.focus.focusMode == Camera.FocusContinuous)
  45.                                        && camera.captureMode != Camera.CaptureVideo
  46.  
  47.     property bool _captureOnFocus
  48.     property real _captureCountdown
  49.  
  50.     readonly property real _viewfinderPosition: orientation == Orientation.Portrait || orientation == Orientation.Landscape
  51.                                                 ? parent.x + x
  52.                                                 : -parent.x - x
  53.  
  54.     readonly property real viewfinderOffset: Math.min(0, isPortrait ? (focusArea.width - height)/2 : (focusArea.width - width)/2)
  55.  
  56.     readonly property bool isPortrait: orientation == Orientation.Portrait
  57.                 || orientation == Orientation.PortraitInverted
  58.     readonly property bool effectiveActive: ((activeFocus && active) || (windowVisible && recording)) && _applicationActive
  59.  
  60.     readonly property bool _canCapture: (camera.captureMode == Camera.CaptureStillImage && camera.imageCapture.ready)
  61.                 || (camera.captureMode == Camera.CaptureVideo && camera.videoRecorder.recorderStatus >= CameraRecorder.LoadedStatus)
  62.  
  63.     property bool captureButtonPressed: !!captureOverlay && captureOverlay.captureButtonPressed
  64.     readonly property bool _capturePending: volumeUp.pressed || volumeDown.pressed || captureButtonPressed
  65.  
  66.     property bool _captureQueued
  67.     property bool captureBusy
  68.     onCaptureBusyChanged: {
  69.         if (!captureBusy && _captureQueued) {
  70.             _captureQueued = false
  71.             camera.captureImage()
  72.         }
  73.     }
  74.  
  75.     readonly property bool _mirrorViewfinder: Settings.global.cameraDevice == "secondary"
  76.  
  77.     readonly property bool _applicationActive: Qt.application.state == Qt.ApplicationActive
  78.     on_ApplicationActiveChanged: if (_applicationActive) flashlightServiceProbe.checkFlashlightServiceStatus()
  79.  
  80.     readonly property string cameraDevice: Settings.cameraDevice
  81.  
  82.     property var captureOverlay: null
  83.  
  84.     signal recordingStopped(url url, string mimeType)
  85.     signal loaded
  86.     signal captured
  87.  
  88.     Item {
  89.         id: captureSnapshot
  90.         property alias sourceItem: captureSnapshotEffect.sourceItem
  91.         visible: false
  92.         anchors.verticalCenter: parent.verticalCenter
  93.         width: parent.width*captureSnapshotEffect.scale
  94.         height: parent.height*captureSnapshotEffect.scale
  95.         ShaderEffectSource {
  96.             id: captureSnapshotEffect
  97.             hideSource: false
  98.             live: false
  99.             scale: 0.4
  100.             anchors.centerIn: parent
  101.             width: isPortrait ? captureView.width : captureView.height
  102.             height: isPortrait ? captureView.height : captureView.width
  103.             rotation: -captureView.pageRotation
  104.         }
  105.     }
  106.  
  107.     function reload() {
  108.         if (captureView._complete) {
  109.             captureView._unload = true
  110.         }
  111.     }
  112.  
  113.     function setFocusPoint(point) {
  114.         focusTimer.restart()
  115.         camera.unlock()
  116.         camera.focus.customFocusPoint = point
  117.     }
  118.  
  119.     function _resetFocus() {
  120.         focusTimer.running = false
  121.         camera.unlock()
  122.     }
  123.  
  124.     function _triggerCapture() {
  125.         if (captureTimer.running) {
  126.             camera.unlock()
  127.             captureTimer.running = false
  128.         } else if (startRecordTimer.running) {
  129.             startRecordTimer.running = false
  130.         } else if (camera.videoRecorder.recorderState == CameraRecorder.RecordingState) {
  131.             camera.videoRecorder.stop()
  132.         } else if (Settings.mode.timer != 0) {
  133.             captureTimer.restart()
  134.         } else if (camera.captureMode == Camera.CaptureStillImage) {
  135.             camera.captureImage()
  136.         } else {
  137.             camera.record()
  138.         }
  139.     }
  140.  
  141.     onEffectiveIsoChanged: {
  142.         if (effectiveIso == 0) {
  143.             camera.exposure.setAutoIsoSensitivity()
  144.         } else {
  145.             camera.exposure.manualIso = Settings.global.iso
  146.         }
  147.     }
  148.  
  149.     on_CanCaptureChanged: {
  150.         if (!_canCapture) {
  151.             startRecordTimer.running = false
  152.         }
  153.     }
  154.  
  155.     Component.onCompleted: {
  156.         flashlightServiceProbe.checkFlashlightServiceStatus()
  157.         camera.deviceId = Settings.global.cameraDevice
  158.         loadOverlay()
  159.         _complete = true
  160.     }
  161.  
  162.     onCameraDeviceChanged: {
  163.         // We must call reload() first so camera reaches UnloadedState
  164.         // If we switch Camera::deviceId then camera will not start again
  165.         // which seems to be a bug in QtMultimedia
  166.         // Qt bug: https://bugreports.qt.io/browse/QTBUG-46995
  167.         reload()
  168.         _resetFocus()
  169.         camera.deviceId = Settings.cameraDevice
  170.         Settings.global.cameraDevice = Settings.cameraDevice
  171.     }
  172.  
  173.     onActiveChanged: {
  174.         if (!active) {
  175.             _resetFocus()
  176.         }
  177.     }
  178.  
  179.     Timer {
  180.         // prevent video recording continuing forever in the background
  181.         running: recording && !effectiveActive
  182.         interval: 60*1000
  183.         onTriggered: camera.videoRecorder.stop()
  184.     }
  185.  
  186.     Timer {
  187.         id: reloadTimer
  188.         interval: 100
  189.         running: captureView._unload && camera.cameraStatus == Camera.UnloadedStatus
  190.         onTriggered: {
  191.             captureView._unload = false
  192.         }
  193.     }
  194.  
  195.     Timer {
  196.         id: startFailedTimer
  197.         interval: 2000
  198.         onTriggered: {
  199.             if (camera.cameraStatus === Camera.StartingStatus) {
  200.                 captureView.reload()
  201.             }
  202.         }
  203.     }
  204.  
  205.     NonGraphicalFeedback {
  206.         id: shutterEvent
  207.         event: "camera_shutter"
  208.     }
  209.  
  210.     NonGraphicalFeedback {
  211.         id: recordStartEvent
  212.         event: "video_record_start"
  213.     }
  214.  
  215.     Timer {
  216.         id: startRecordTimer
  217.  
  218.         interval: 200
  219.         onTriggered: {
  220.             captureOverlay.writeMetaData()
  221.             camera.videoRecorder.record()
  222.             if (camera.videoRecorder.recorderState == CameraRecorder.RecordingState) {
  223.                 camera.videoRecorder.recorderStateChanged.connect(camera._finishRecording)
  224.                 extensions.disableNotifications(captureView, true)
  225.             }
  226.         }
  227.     }
  228.  
  229.     SequentialAnimation {
  230.         id: captureTimer
  231.  
  232.         NumberAnimation {
  233.             id: timerAnimation
  234.             duration: Settings.mode.timer * 1000
  235.             from: Settings.mode.timer
  236.             to: 0
  237.             easing.type: Easing.Linear
  238.             target: captureView
  239.             property: "_captureCountdown"
  240.         }
  241.         ScriptAction {
  242.             script: {
  243.                 if (camera.captureMode == Camera.CaptureStillImage) {
  244.                     camera.searchAndLock()
  245.                     camera.captureImage()
  246.                 } else {
  247.                     camera.record()
  248.                 }
  249.             }
  250.         }
  251.     }
  252.  
  253.     NonGraphicalFeedback {
  254.         id: recordStopEvent
  255.         event: "video_record_stop"
  256.     }
  257.  
  258.     onRecordingStopped: {
  259.         captureModel.appendCapture(
  260.                     url,
  261.                     mimeType,
  262.                     captureOrientation,
  263.                     camera.videoRecorder.duration / 1000,
  264.                     camera.videoRecorder.resolution)
  265.     }
  266.  
  267.     Camera {
  268.         id: camera
  269.  
  270.         function autoFocus() {
  271.             captureOverlay.close()
  272.             if (camera.captureMode == Camera.CaptureStillImage
  273.                     && focus.focusMode != Camera.FocusInfinity
  274.                     && focus.focusMode != Camera.FocusHyperfocal
  275.                     && camera.lockStatus == Camera.Unlocked
  276.                     && Settings.mode.timer == 0) {
  277.                 camera.searchAndLock()
  278.             }
  279.         }
  280.  
  281.         function captureImage() {
  282.             if (camera.lockStatus != Camera.Searching) {
  283.                 _completeCapture()
  284.             } else {
  285.                 captureView._captureOnFocus = true
  286.             }
  287.         }
  288.  
  289.         function record() {
  290.             videoRecorder.outputLocation = Settings.videoCapturePath("mp4")
  291.             startRecordTimer.running = true
  292.             recordStartEvent.play()
  293.         }
  294.  
  295.         function _completeCapture() {
  296.             if (captureBusy) {
  297.                 _captureQueued = true
  298.                 return
  299.             }
  300.  
  301.             captureBusy = true
  302.             captureOverlay.writeMetaData()
  303.  
  304.             shutterEvent.play()
  305.             captureAnimation.start()
  306.  
  307.             camera.imageCapture.captureToLocation(Settings.photoCapturePath('jpg'))
  308.  
  309.             if (focusTimer.running) {
  310.                 // Changing focus mode will reset focus point, make sure it stays same
  311.                 var focusPoint = Qt.point(camera.focus.customFocusPoint.x, camera.focus.customFocusPoint.y)
  312.                 focusTimer.restart()
  313.                 camera.focus.customFocusPoint = focusPoint
  314.             }
  315.         }
  316.  
  317.         function _finishRecording() {
  318.             if (videoRecorder.recorderState == CameraRecorder.StoppedState) {
  319.                 videoRecorder.recorderStateChanged.disconnect(_finishRecording)
  320.                 extensions.disableNotifications(captureView, false)
  321.                 var finalUrl = Settings.completeCapture(videoRecorder.outputLocation)
  322.                 if (finalUrl != "") {
  323.                     captureView.recordingStopped(finalUrl, videoRecorder.mediaContainer)
  324.                 }
  325.                 recordStopEvent.play()
  326.             }
  327.         }
  328.  
  329.         captureMode: Settings.mode.captureMode
  330.  
  331.         onCaptureModeChanged: captureView._resetFocus()
  332.  
  333.         cameraState: captureView._complete && captureView.effectiveActive && !captureView._unload
  334.                     ? Camera.ActiveState
  335.                     : Camera.UnloadedState
  336.  
  337.         onCameraStateChanged: {
  338.             if (cameraState == Camera.ActiveState && captureOverlay) {
  339.                 captureView.loaded()
  340.             }
  341.         }
  342.  
  343.         onCameraStatusChanged: {
  344.             if (camera.cameraStatus == Camera.StartingStatus) {
  345.                 startFailedTimer.restart()
  346.             } else {
  347.                 startFailedTimer.stop()
  348.             }
  349.         }
  350.  
  351.         imageCapture {
  352.             resolution: Settings.mode.imageResolution
  353.             onResolutionChanged: reload()
  354.  
  355.             onImageSaved: {
  356.                 camera.unlock()
  357.                 captureBusy = false
  358.  
  359.                 captureModel.appendCapture(
  360.                             path,
  361.                             "image/jpeg",
  362.                             captureOrientation,
  363.                             0,
  364.                             camera.imageCapture.resolution)
  365.             }
  366.             onCaptureFailed: {
  367.                 camera.unlock()
  368.                 captureBusy = false
  369.             }
  370.         }
  371.         videoRecorder {
  372.             resolution: Settings.mode.videoResolution
  373.             onResolutionChanged: reload()
  374.             frameRate: 30
  375.             audioChannels: 2
  376.             audioSampleRate: Settings.global.audioSampleRate
  377.             audioCodec: Settings.global.audioCodec
  378.             videoCodec: Settings.global.videoCodec
  379.             mediaContainer: Settings.global.mediaContainer
  380.  
  381.             videoEncodingMode: Settings.global.videoEncodingMode
  382.             videoBitRate: Settings.global.videoBitRate
  383.         }
  384.         focus {
  385.             focusMode: Settings.mode.focusDistanceValues.indexOf(Camera.FocusContinuous) >= 0
  386.                        ? Camera.FocusContinuous : Settings.mode.focusDistanceValues[0]
  387.             focusPointMode: focusTimer.running ? Camera.FocusPointCustom : Camera.FocusPointAuto
  388.         }
  389.         flash.mode: Settings.mode.flash
  390.         imageProcessing.whiteBalanceMode: Settings.global.whiteBalance
  391.  
  392.         exposure {
  393.             exposureMode: Settings.mode.exposureMode
  394.             exposureCompensation: Settings.global.exposureCompensation / 2.0
  395.             meteringMode: Settings.mode.meteringMode
  396.         }
  397.  
  398.         viewfinder {
  399.             resolution: Settings.mode.viewfinderResolution
  400.             minimumFrameRate: 30
  401.             maximumFrameRate: 30
  402.         }
  403.  
  404.         metaData {
  405.             orientation: captureView.captureOrientation
  406.         }
  407.  
  408.         onDeviceIdChanged: captureView.reload()
  409.         viewfinder.onResolutionChanged: captureView.reload()
  410.         focus.onFocusModeChanged: camera.unlock()
  411.  
  412.         onLockStatusChanged: {
  413.             if (lockStatus != Camera.Searching && captureView._captureOnFocus) {
  414.                 captureView._captureOnFocus = false
  415.                 camera._completeCapture()
  416.             }
  417.         }
  418.     }
  419.  
  420.     DeviceInfo {
  421.         Component.onCompleted: {
  422.             camera.metaData.cameraModel = model()
  423.             camera.metaData.cameraManufacturer = manufacturer()
  424.         }
  425.     }
  426.  
  427.     CameraExtensions {
  428.         id: extensions
  429.     }
  430.  
  431.     Binding {
  432.         target: captureView.viewfinder
  433.         property: "source"
  434.         value: camera
  435.     }
  436.  
  437.     Binding {
  438.         target: captureView.viewfinder
  439.         property: "mirror"
  440.         value: captureView._mirrorViewfinder
  441.     }
  442.  
  443.     SequentialAnimation {
  444.         id: captureAnimation
  445.  
  446.         PropertyAction {
  447.             target: captureSnapshot
  448.             property: "sourceItem"
  449.             value: viewfinder
  450.         }
  451.         ScriptAction {
  452.             script: captureSnapshotEffect.scheduleUpdate()
  453.         }
  454.         PropertyAction {
  455.             target: captureSnapshot
  456.             property: "x"
  457.             value: 0
  458.         }
  459.         PropertyAction {
  460.             target: captureSnapshot
  461.             property: "visible"
  462.             value: true
  463.         }
  464.         PropertyAction {
  465.             target: viewfinder
  466.             property: "opacity"
  467.             value: 0
  468.         }
  469.         PauseAnimation {
  470.             duration: 100
  471.         }
  472.         ParallelAnimation {
  473.             XAnimator {
  474.                 target: captureSnapshot
  475.                 from: 0
  476.                 to: captureView.isPortrait ? -captureView.height : -captureView.width
  477.                 duration: 300
  478.                 easing.type: Easing.InQuad
  479.             }
  480.             OpacityAnimator {
  481.                 target: viewfinder
  482.                 to: 1
  483.                 duration: 300
  484.             }
  485.         }
  486.         PropertyAction {
  487.             target: captureSnapshot
  488.             property: "visible"
  489.             value: false
  490.         }
  491.         PropertyAction {
  492.             target: captureSnapshot
  493.             property: "sourceItem"
  494.             value: null
  495.         }
  496.         ScriptAction {
  497.             script: captureView.captured()
  498.         }
  499.     }
  500.  
  501.     property Component overlayComponent
  502.     property var overlayIncubator
  503.  
  504.     function loadOverlay() {
  505.         overlayComponent = Qt.createComponent("CaptureOverlay.qml", Component.Asynchronous, captureView)
  506.         if (overlayComponent) {
  507.             if (overlayComponent.status === Component.Ready) {
  508.                 incubateOverlay()
  509.             } else if (overlayComponent.status === Component.Loading) {
  510.                 overlayComponent.statusChanged.connect(
  511.                     function(status) {
  512.                         if (overlayComponent) {
  513.                             if (status == Component.Ready) {
  514.                                 incubateOverlay()
  515.                             } else if (status == Component.Error) {
  516.                                 console.warn(overlayComponent.errorString())
  517.                             }
  518.                         }
  519.                     })
  520.             } else {
  521.                 console.log("Error loading capture overlay", overlayComponent.errorString())
  522.             }
  523.         }
  524.     }
  525.  
  526.     function incubateOverlay() {
  527.         overlayIncubator = overlayComponent.incubateObject(captureView, {
  528.                                                                       "captureView": captureView,
  529.                                                                       "camera": camera,
  530.                                                                       "focusArea": focusArea
  531.                                                                   }, Qt.Asynchronous)
  532.         overlayIncubator.onStatusChanged = function(status) {
  533.             if (status == Component.Ready) {
  534.                 captureOverlay = overlayIncubator.object
  535.                 overlayFadeIn.start()
  536.                 overlayIncubator = null
  537.                 if (camera.cameraState == Camera.ActiveState && captureOverlay) {
  538.                     captureView.loaded()
  539.                 }
  540.             } else if (status == Component.Error) {
  541.                 console.log("Failed to create capture overlay")
  542.                 overlayIncubator = null
  543.             }
  544.         }
  545.     }
  546.  
  547.     FadeAnimator {
  548.         id: overlayFadeIn
  549.         target: captureOverlay
  550.         to: 1.0
  551.         duration: 100
  552.     }
  553.  
  554.     Item {
  555.         id: focusArea
  556.  
  557.         width: Screen.width
  558.                * camera.viewfinder.resolution.width
  559.                / camera.viewfinder.resolution.height
  560.         height: Screen.width
  561.  
  562.         rotation: -captureView.viewfinderOrientation
  563.         anchors {
  564.             centerIn: parent
  565.             verticalCenterOffset: isPortrait ? viewfinderOffset : 0
  566.             horizontalCenterOffset: isPortrait ? 0 : viewfinderOffset
  567.         }
  568.         opacity: captureOverlay ? 1.0 - captureOverlay.settingsOpacity : 1.0
  569.  
  570.         Repeater {
  571.             model: camera.focus.focusZones
  572.             delegate: Item {
  573.                 x: focusArea.width * (captureView._mirrorViewfinder
  574.                                       ? 1 - area.x - area.width
  575.                                       : area.x)
  576.                 y: focusArea.height * area.y
  577.                 width: focusArea.width * area.width
  578.                 height: focusArea.height * area.height
  579.  
  580.                 visible: status != Camera.FocusAreaUnused && camera.focus.focusPointMode == Camera.FocusPointCustom
  581.  
  582.                 Rectangle {
  583.                     id: focusRectangle
  584.  
  585.                     width: Math.min(parent.width, parent.height)
  586.                     height: width
  587.                     anchors.centerIn: parent
  588.                     radius: width / 2
  589.                     border {
  590.                         width: Math.round(Theme.pixelRatio * 2)
  591.                         color: status == Camera.FocusAreaFocused ? Theme.highlightColor : "white"
  592.                     }
  593.                     color: "#00000000"
  594.                 }
  595.             }
  596.         }
  597.     }
  598.  
  599.     Timer {
  600.         id: focusTimer
  601.  
  602.         interval: 5000
  603.         onTriggered: captureView._resetFocus()
  604.     }
  605.  
  606.     MediaKey {
  607.         id: volumeUp
  608.         enabled: camera.imageCapture.ready
  609.                     && keysResource.acquired
  610.                     && camera.captureMode == Camera.CaptureStillImage
  611.                     && !captureButtonPressed
  612.                     && !captureView._captureOnFocus
  613.         key: Qt.Key_VolumeUp
  614.         onPressed: camera.autoFocus()
  615.         onReleased: {
  616.             if (enabled)
  617.                 captureView._triggerCapture()
  618.         }
  619.     }
  620.     MediaKey {
  621.         id: volumeDown
  622.         enabled: volumeUp.enabled
  623.         key: Qt.Key_VolumeDown
  624.         onPressed: camera.autoFocus()
  625.         onReleased: {
  626.             if (enabled)
  627.                 captureView._triggerCapture()
  628.         }
  629.     }
  630.  
  631.     Permissions {
  632.         enabled: captureView.activeFocus
  633.                     && camera.captureMode == Camera.CaptureStillImage
  634.                     && camera.cameraState == Camera.ActiveState
  635.         autoRelease: true
  636.         applicationClass: "camera"
  637.  
  638.         Resource {
  639.             id: keysResource
  640.             type: Resource.ScaleButton
  641.             optional: true
  642.         }
  643.     }
  644.  
  645.     DBusInterface {
  646.         id: flashlightServiceProbe
  647.         service: "org.freedesktop.DBus"
  648.         path: "/org/freedesktop/DBus"
  649.         iface: "org.freedesktop.DBus"
  650.         property bool flashlightServiceActive
  651.         onFlashlightServiceActiveChanged: {
  652.             if (flashlightServiceActive) {
  653.                 if (flashlightComponentLoader.sourceComponent == null || flashlightComponentLoader.sourceComponent == undefined) {
  654.                     flashlightComponentLoader.sourceComponent = flashlightComponent
  655.                 } else {
  656.                     flashlightComponentLoader.item.toggleFlashlight()
  657.                 }
  658.             }
  659.         }
  660.         function checkFlashlightServiceStatus() {
  661.             var probe = flashlightServiceProbe // cache id resolution to avoid context destruction issues
  662.             typedCall('NameHasOwner',
  663.                       { 'type': 's', 'value': 'com.jolla.settings.system.flashlight' },
  664.                         function(result) { probe.flashlightServiceActive = false; probe.flashlightServiceActive = result }, // twiddle so that the change-handler is invoked
  665.                         function() { probe.flashlightServiceActive = false; probe.flashlightServiceActive = true })         // assume true in failed case, to ensure we turn it off
  666.         }
  667.     }
  668.  
  669.     Loader { id: flashlightComponentLoader }
  670.  
  671.     Component {
  672.         id: flashlightComponent
  673.         DBusInterface {
  674.             id: flashlightDbus
  675.             bus: DBusInterface.SessionBus
  676.             service: "com.jolla.settings.system.flashlight"
  677.             path: "/com/jolla/settings/system/flashlight"
  678.             iface: "com.jolla.settings.system.flashlight"
  679.             Component.onCompleted: toggleFlashlight()
  680.             function toggleFlashlight() {
  681.                 var isOn = flashlightDbus.getProperty("flashlightOn")
  682.                 if (isOn) flashlightDbus.call("toggleFlashlight")
  683.             }
  684.         }
  685.     }
  686. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement