diff --git a/changes b/changes
index 5ce8ee768c72b8eab396089f58712a3a69773113..fbf2375b23552a1aa7c834f68caea3eb5ed9f3c6 100644
--- a/changes
+++ b/changes
@@ -1,4 +1,5 @@
     ANDROID
+     - handle pinch gesture to zoom score in/out
      - application can restart itself to restore initial settings
 
 1.2A.6
diff --git a/src/libs/main/score/tmainscore.cpp b/src/libs/main/score/tmainscore.cpp
index a210a378e9008fed7865b06fc6070015e11adfad..4de08247aba4d20b9fe8eceafaf4150e969110b3 100644
--- a/src/libs/main/score/tmainscore.cpp
+++ b/src/libs/main/score/tmainscore.cpp
@@ -90,6 +90,13 @@ TmainScore::TmainScore(QMainWindow* mw, QWidget* parent) :
 	createNoteName();
 	isExamExecuting(false); // initialize some connections
   setNote(0, Tnote()); // To display fake empty note properly
+
+  connect(this, &TmultiScore::pinchZoom, [=](int dir) {
+    if (dir > 0)
+      m_acts->zoomIn()->trigger();
+    else
+      m_acts->zoomOut()->trigger();
+  });
 }
 
 
@@ -714,16 +721,30 @@ void TmainScore::showNamesSlot() {
 }
 
 
+/**
+ * @p zoomScoreSlot() is invoked by zoom in/out actions
+ * triggered from menu or through pinch zoom signal,
+ * and represents user scale factor.
+ * By default preferred scale of a staff is:
+ * - half of screen height on desktop
+ * - 6 * finger size on mobile,
+ * but not greater than score widget height - to ensure display entire staff
+ * On bigger tablet screens maximum height is bounded by 8 * finger size (mobile)
+ * or screen height / 1.5 (desktop).
+ * Minimal size is bounded by user scale factor but under mobile,
+ * also by minimal clef width that can be smaller than half of finger
+ * to avoid covering clef by menu button
+ */
 void TmainScore::zoomScoreSlot() {
-	qreal newScale = Tcore::gl()->S->scoreScale;
-	if (sender() == m_acts->zoomOut()) {
-			newScale = qMin(Tcore::gl()->S->scoreScale + 0.25, 2.0);
-	} else {
-			newScale = qMax(Tcore::gl()->S->scoreScale - 0.25, 1.0);
-	}
-	if (newScale != Tcore::gl()->S->scoreScale) {
-		Tcore::gl()->S->scoreScale = newScale;
-		setScoreScale(newScale);
+  qreal newScale = Tcore::gl()->S->scoreScale;
+  if (sender() == m_acts->zoomOut())
+    newScale = qMin(Tcore::gl()->S->scoreScale + 0.2, 3.0);
+  else
+    newScale = qMax(Tcore::gl()->S->scoreScale - 0.2, 0.4);
+
+  if (newScale != Tcore::gl()->S->scoreScale) {
+    setScoreScale(newScale);
+    Tcore::gl()->S->scoreScale = scoreScale();
     if (m_questMark) {
       m_questMark->setPos(0, 0); // reset position to enable positioning again
       setQuestionMarkPos();
diff --git a/src/libs/score/tmultiscore.cpp b/src/libs/score/tmultiscore.cpp
index 3b2b7ef87ecf890908b984749e9fcfc5ddfbca38..e94d0c71a356666f84cb0839e4160e34734154a8 100644
--- a/src/libs/score/tmultiscore.cpp
+++ b/src/libs/score/tmultiscore.cpp
@@ -29,7 +29,12 @@
 #endif
 #include <QtWidgets/QtWidgets>
 
-
+/** Definition of maximum staff height depends on platform  */
+#if defined (Q_OS_ANDROID)
+  #define MAX_STAFF_HEIGHT qMin(height(), Tmtr::fingerPixels() * 8)
+#else
+  #define MAX_STAFF_HEIGHT qMin(qreal(height()), qMin(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 1.5)
+#endif
 
 #define SENDER_TO_STAFF static_cast<TscoreStaff*>(sender())
 
@@ -179,7 +184,26 @@ void TmultiScore::setScoreDisabled(bool disabled) {
 }
 
 
+/**
+ * @p m_scale represents scale sets by user
+ * - this value can be ignored:
+ *   -- if scale is too big to display entire staff
+ *   -- or when it is too small and clef goes under mobile menu
+ * By default @p m_scale = 1.0 - bigger values make score smaller
+ * smaller values increase the score
+ */
 void TmultiScore::setScoreScale(qreal sc) {
+  qreal scoreFactor = transform().m11() * getScaleFactor(height(), sc);
+  if (staff()->height() * scoreFactor > MAX_STAFF_HEIGHT) {
+    qDebug() << "Staff height out of score! Scaling ignored";
+    return;
+  }
+#if defined (Q_OS_ANDROID)
+  if (CLEF_WIDTH * scoreFactor < Tmtr::fingerPixels() * 0.5) {
+    qDebug() << "Score will be too small! Scaling ignored!";
+    return;
+  }
+#endif
   if (sc != m_scale) {
     m_scale = sc;
     resizeEvent(0);
@@ -303,6 +327,15 @@ void TmultiScore::ensureNoteIsVisible() {
 //####################################################################################################
 //###################################   PROTECTED   ##################################################
 //####################################################################################################
+qreal TmultiScore::getScaleFactor(int minH, qreal sc) {
+#if defined (Q_OS_ANDROID)
+    qreal hh = qMin(qreal(minH), Tmtr::fingerPixels() * 6.0);
+#else
+    qreal hh = qMin(minH, qMin<int>(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 2);
+#endif
+    return ((hh / (staff()->height() + 0.4)) / transform().m11()) / sc;
+
+}
 
 void TmultiScore::resizeEvent(QResizeEvent* event) {
   int hh = height(), ww = width();
@@ -324,12 +357,8 @@ void TmultiScore::resizeEvent(QResizeEvent* event) {
     qreal staffOff = 0.0;
     if (staff()->isPianoStaff())
       staffOff = 1.1;
-#if defined (Q_OS_ANDROID)
-    hh = qMin<int>(hh, Tmtr::fingerPixels() * 6);
-#else
-    hh = qMin<int>(hh, qMin<int>(qApp->desktop()->screenGeometry().width(), qApp->desktop()->screenGeometry().height()) / 2);
-#endif
-    qreal factor = (((qreal)hh / (staff()->height() + 0.4)) / transform().m11()) / m_scale;
+
+    qreal factor = getScaleFactor(hh, m_scale);
     scoreScene()->prepareToChangeRect();
     scale(factor, factor);
     int stavesNumber; // how many staves are needed
diff --git a/src/libs/score/tmultiscore.h b/src/libs/score/tmultiscore.h
index 0d1a7024a0dab408a3e7ea5a9bd279e01205a13b..b5fa2cb38bab24ecc46ba28b84f008f8981ffbc1 100644
--- a/src/libs/score/tmultiscore.h
+++ b/src/libs/score/tmultiscore.h
@@ -113,6 +113,10 @@ protected:
 
   void deleteFakeLines(int lastNr); /**< Deletes last @p lastNr lines from the fake list. */
 
+      /** Calculates score view scale factor with given user scale @p sc
+       * and minimal score height @p minH */
+  qreal getScaleFactor(int minH, qreal sc);
+
 protected slots:
 	void keyChangedSlot();
 
diff --git a/src/libs/score/tsimplescore.cpp b/src/libs/score/tsimplescore.cpp
index a56490275825c1884353b319e8b6ff0fe0fae85d..5bc8e01d778a82448037705dd4f48a17f87ae8c3 100644
--- a/src/libs/score/tsimplescore.cpp
+++ b/src/libs/score/tsimplescore.cpp
@@ -28,6 +28,7 @@
 #include <tcolor.h>
 #include <tnoofont.h>
 #include <QtWidgets/QtWidgets>
+#include <tmtr.h>
 
 
 #define TAP_TIME (200) //  ms
@@ -36,10 +37,11 @@
 
 TsimpleScore::TsimpleScore(int notesNumber, QWidget* parent) :
   QGraphicsView(parent),
-  m_notesNr(notesNumber),
   m_bgGlyph(0),
-	m_prevBGglyph(-1)
-{   
+  m_notesNr(notesNumber),
+  m_prevBGglyph(-1),
+  m_pinchZoomEmitted(false)
+{
   if (TscoreNote::touchEnabled())
     viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
   else {
@@ -342,7 +344,7 @@ void TsimpleScore::resizeEvent(QResizeEvent* event) {
  * with latest touch position in scene coordinates. To use them, TscoreItem subclass has to map it first.
  * When second finger touches screen, last touch event is canceled by putting null point as a parameter.
  * So far, only: @class TscoreNote, @class TscoreKeySignature and @class TscoreClef handles those methods.
- * Two fingers touch is used to scroll score
+ * Two fingers touch is used to scroll score and to zoom score in/out
  */
 bool TsimpleScore::viewportEvent(QEvent* event) {
   if (TscoreNote::touchEnabled()) {
@@ -366,6 +368,7 @@ bool TsimpleScore::viewportEvent(QEvent* event) {
             break;
           }
           case Qt::TouchPointReleased: {
+            m_pinchZoomEmitted = false;
             if (m_currentIt) {
               m_currentIt->untouched(touchScenePos);
               m_currentIt = 0;
@@ -381,7 +384,21 @@ bool TsimpleScore::viewportEvent(QEvent* event) {
           m_currentIt->untouched(QPointF(0, 0));
           m_currentIt = 0;
         }
-        verticalScrollBar()->setValue(verticalScrollBar()->value() + (te->touchPoints()[0].lastPos().y() - te->touchPoints()[0].pos().y()));
+        QLineF l1(te->touchPoints()[0].startPos(), te->touchPoints()[1].startPos()); // initial double-touch position
+        QLineF l2(te->touchPoints()[0].pos(), te->touchPoints()[1].pos()); // final position
+        qreal distance = l1.length() - l2.length();
+        if (distance < -2.0 * Tmtr::fingerPixels()) { // zoom out
+          if (!m_pinchZoomEmitted) {
+            emit pinchZoom(1);
+            m_pinchZoomEmitted = true;
+          }
+        } else if (distance > 2.0 * Tmtr::fingerPixels()) { // zoom in
+          if (!m_pinchZoomEmitted) {
+            emit pinchZoom(-1);
+            m_pinchZoomEmitted = true;
+          }
+        } else if (distance < Tmtr::fingerPixels()) // just scroll
+          verticalScrollBar()->setValue(verticalScrollBar()->value() + int(te->touchPoints()[0].lastPos().y() - te->touchPoints()[0].pos().y()));
         return true;
       }
     }
diff --git a/src/libs/score/tsimplescore.h b/src/libs/score/tsimplescore.h
index 029434e7d5cac38dfc5b291e592e800ee1de1286..5f0fa9912b887126c899cee643377ad8b100fa96 100644
--- a/src/libs/score/tsimplescore.h
+++ b/src/libs/score/tsimplescore.h
@@ -77,7 +77,7 @@ public:
 
   bool isPianoStaff();
 
-      /** Sets background color. Alpha value will be overriden. */
+      /** Sets background color. Alpha value will be overridden. */
   void setBGcolor(QColor bgColor);
 
       /** It disables accids buttons and locks editing,
@@ -106,6 +106,13 @@ signals:
       /** TsimpleScore takes care about changing staves but also emits this signal when changes are done.*/
   void clefChanged(Tclef);
 
+      /** Emitted when score detects pinch touch gesture.
+       * Emitted value is zoom factor:
+       * - positive (> 0): zoom out
+       * - negative (< 0): zoom in
+       * Signal is emitted once per touch (m_pinchZoomEmitted is responsible for it) */
+  void pinchZoom(int);
+
 
 public slots:
   virtual void noteWasClicked(int index);
@@ -150,9 +157,10 @@ private:
   Tclef::Etype								 m_clefType;
   QSize												 m_sizeHint;
   QPointer<TscoreItem>				 m_currentIt;
-  QPointF											 m_initPos; /** In scene coordinates. */
+  QPointF											 m_initPos; /**< In scene coordinates. */
   bool                         m_wheelFree;
   QTimer                      *m_wheelLockTimer;
+  bool                         m_pinchZoomEmitted;
 };
 
 #endif // TSIMPLESCORE_H
diff --git a/src/main.cpp b/src/main.cpp
index 337a3be889da034ef92b1e7822128e8e0a67bb90..08f91d2cdad2116127d29ddbfc6b68342cc399a9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -130,9 +130,9 @@ int main(int argc, char *argv[])
         QFile f(confFile);
         f.remove();
         Tandroid::restartNootka(); // and call Nootka after delay
-        a->quit();
     }
     resetConfig = false; // do - while loop doesn't work with Android
+//    a->quit(); // HACK: calling QApplication::quick() solves hang on x86 when Qt uses native (usually obsolete) SSL libraries
 #endif
 	} while (resetConfig);
   delete a;