1 /*
2     Copyright (C) 2014 Aseman
3     http://aseman.co
4 
5     This project is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This project is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #define LINUX_DEFAULT_DPI 96
20 #define WINDOWS_DEFAULT_DPI 96
21 #define UTOUCH_DEFAULT_DPI 76
22 
23 #include "asemandevices.h"
24 #include "asemanapplication.h"
25 
26 #ifdef Q_OS_ANDROID
27 #include "asemanjavalayer.h"
28 #endif
29 
30 #include <QTimerEvent>
31 #include <QGuiApplication>
32 #include <QMimeType>
33 #include <QMimeDatabase>
34 #include <QUrl>
35 #include <QDesktopServices>
36 #include <QDir>
37 #include <QFileInfo>
38 #include <QFile>
39 #include <QClipboard>
40 #include <QtCore/qmath.h>
41 #include <QScreen>
42 #include <QDateTime>
43 #include <QDebug>
44 #include <QMimeData>
45 
46 #ifdef ASEMAN_MULTIMEDIA
47 #if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0))
48 #include <QCameraInfo>
49 #endif
50 #endif
51 
52 #ifdef Q_OS_WIN
53 #include <QSysInfo>
54 #endif
55 
56 class AsemanDevicesPrivate
57 {
58 public:
59     int hide_keyboard_timer;
60     bool keyboard_stt;
61 
62     QMimeDatabase mime_db;
63 
64 #ifdef Q_OS_ANDROID
65     AsemanJavaLayer *java_layer;
66 #endif
67 };
68 
AsemanDevices(QObject * parent)69 AsemanDevices::AsemanDevices(QObject *parent) :
70     QObject(parent)
71 {
72     p = new AsemanDevicesPrivate;
73     p->hide_keyboard_timer = 0;
74     p->keyboard_stt = false;
75 
76 #ifdef Q_OS_ANDROID
77     p->java_layer = AsemanJavaLayer::instance();
78 
79     connect( p->java_layer, SIGNAL(incomingShare(QString,QString)), SLOT(incoming_share(QString,QString)), Qt::QueuedConnection );
80     connect( p->java_layer, SIGNAL(incomingImage(QString))        , SLOT(incoming_image(QString))        , Qt::QueuedConnection );
81     connect( p->java_layer, SIGNAL(selectImageResult(QString))    , SLOT(select_image_result(QString))   , Qt::QueuedConnection );
82     connect( p->java_layer, SIGNAL(activityPaused())              , SLOT(activity_paused())              , Qt::QueuedConnection );
83     connect( p->java_layer, SIGNAL(activityResumed())             , SLOT(activity_resumed())             , Qt::QueuedConnection );
84 #endif
85 
86     connect( QGuiApplication::inputMethod(), SIGNAL(visibleChanged()), SLOT(keyboard_changed()) );
87     connect( static_cast<QGuiApplication*>(QCoreApplication::instance())->clipboard(), SIGNAL(dataChanged()), SIGNAL(clipboardChanged()) );
88 
89     QScreen *scr = screen();
90     if( scr )
91         connect( scr, SIGNAL(geometryChanged(QRect)), SIGNAL(geometryChanged()) );
92 }
93 
isMobile() const94 bool AsemanDevices::isMobile() const
95 {
96     return isTouchDevice() && !isTablet();
97 }
98 
isTablet() const99 bool AsemanDevices::isTablet() const
100 {
101 #ifdef Q_OS_ANDROID
102     return isTouchDevice() && p->java_layer->isTablet();
103 #else
104     return isTouchDevice() && lcdPhysicalSize() >= 6;
105 #endif
106 }
107 
isLargeTablet() const108 bool AsemanDevices::isLargeTablet() const
109 {
110 #ifdef Q_OS_ANDROID
111     return isTablet() && p->java_layer->getSizeName() == 3;
112 #else
113     return isTouchDevice() && lcdPhysicalSize() >= 9;
114 #endif
115 }
116 
isTouchDevice()117 bool AsemanDevices::isTouchDevice()
118 {
119     return isAndroid() || isIOS() || isWindowsPhone() || isUbuntuTouch();
120 }
121 
isDesktop()122 bool AsemanDevices::isDesktop()
123 {
124     return !isTouchDevice();
125 }
126 
isMacX()127 bool AsemanDevices::isMacX()
128 {
129 #ifdef Q_OS_MAC
130     return true;
131 #else
132     return false;
133 #endif
134 }
135 
isWindows()136 bool AsemanDevices::isWindows()
137 {
138 #ifdef Q_OS_WIN
139     return true;
140 #else
141     return false;
142 #endif
143 }
144 
isLinux()145 bool AsemanDevices::isLinux()
146 {
147 #if defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD)
148     return true;
149 #else
150     return false;
151 #endif
152 }
153 
isAndroid()154 bool AsemanDevices::isAndroid()
155 {
156 #ifdef Q_OS_ANDROID
157     return true;
158 #else
159     return false;
160 #endif
161 }
162 
isIOS()163 bool AsemanDevices::isIOS()
164 {
165 #ifdef Q_OS_IOS
166     return true;
167 #else
168     return false;
169 #endif
170 }
171 
isUbuntuTouch()172 bool AsemanDevices::isUbuntuTouch()
173 {
174 #ifdef Q_OS_UBUNTUTOUCH
175     return true;
176 #else
177     return false;
178 #endif
179 }
180 
isWindowsPhone()181 bool AsemanDevices::isWindowsPhone()
182 {
183 #ifdef Q_OS_WINPHONE
184     return true;
185 #else
186     return false;
187 #endif
188 }
189 
isWindows8()190 bool AsemanDevices::isWindows8()
191 {
192 #ifdef Q_OS_WIN
193     return QSysInfo::windowsVersion() == QSysInfo::WV_WINDOWS8 ||
194            QSysInfo::windowsVersion() == QSysInfo::WV_WINDOWS8_1;
195 #else
196     return false;
197 #endif
198 }
199 
screen() const200 QScreen *AsemanDevices::screen() const
201 {
202     const QList<QScreen*> & screens = QGuiApplication::screens();
203     if( screens.isEmpty() )
204         return 0;
205 
206     return screens.first();
207 }
208 
screenObj() const209 QObject *AsemanDevices::screenObj() const
210 {
211     return screen();
212 }
213 
lcdPhysicalSize() const214 qreal AsemanDevices::lcdPhysicalSize() const
215 {
216     qreal w = lcdPhysicalWidth();
217     qreal h = lcdPhysicalHeight();
218 
219     return qSqrt( h*h + w*w );
220 }
221 
lcdPhysicalWidth() const222 qreal AsemanDevices::lcdPhysicalWidth() const
223 {
224     if( QGuiApplication::screens().isEmpty() )
225         return 0;
226 
227     return (qreal)screenSize().width()/lcdDpiX();
228 }
229 
lcdPhysicalHeight() const230 qreal AsemanDevices::lcdPhysicalHeight() const
231 {
232     if( QGuiApplication::screens().isEmpty() )
233         return 0;
234 
235     return (qreal)screenSize().height()/lcdDpiY();
236 }
237 
lcdDpiX() const238 qreal AsemanDevices::lcdDpiX() const
239 {
240 #ifdef Q_OS_ANDROID
241     return p->java_layer->densityDpi();
242 #else
243     if( QGuiApplication::screens().isEmpty() )
244         return 0;
245 
246     QScreen *scr = QGuiApplication::screens().first();
247     return scr->physicalDotsPerInchX();
248 #endif
249 }
250 
lcdDpiY() const251 qreal AsemanDevices::lcdDpiY() const
252 {
253 #ifdef Q_OS_ANDROID
254     return p->java_layer->densityDpi();
255 #else
256     if( QGuiApplication::screens().isEmpty() )
257         return 0;
258 
259     QScreen *scr = QGuiApplication::screens().first();
260     return scr->physicalDotsPerInchY();
261 #endif
262 }
263 
screenSize() const264 QSize AsemanDevices::screenSize() const
265 {
266 #ifdef Q_OS_ANDROID
267     return QSize(p->java_layer->screenSizeWidth(),
268                  p->java_layer->screenSizeHeight());
269 #else
270     if( QGuiApplication::screens().isEmpty() )
271         return QSize();
272 
273     QScreen *scr = QGuiApplication::screens().first();
274     return scr->size();
275 #endif
276 }
277 
keyboardHeight() const278 qreal AsemanDevices::keyboardHeight() const
279 {
280 #ifdef Q_OS_UBUNTUTOUCH
281     return screenSize().height()*0.5;
282 #else
283     const QSize & scr_size = screenSize();
284     bool portrait = scr_size.width()<scr_size.height();
285     if( portrait )
286     {
287         if( isMobile() )
288             return screenSize().height()*0.6;
289         else
290             return screenSize().height()*0.4;
291     }
292     else
293     {
294         if( isMobile() )
295             return screenSize().height()*0.7;
296         else
297             return screenSize().height()*0.5;
298     }
299 #endif
300 }
301 
deviceName() const302 QString AsemanDevices::deviceName() const
303 {
304     if(isDesktop())
305 #ifdef Q_OS_WIN
306         return QSysInfo::prettyProductName() + " " + QSysInfo::currentCpuArchitecture();
307 #else
308         return "desktop";
309 #endif
310 #ifdef Q_OS_ANDROID
311     else
312         return p->java_layer->deviceName();
313 #else
314     else
315         return "mobile";
316 #endif
317 }
318 
transparentStatusBar() const319 bool AsemanDevices::transparentStatusBar() const
320 {
321 #ifdef Q_OS_ANDROID
322     return p->java_layer->transparentStatusBar();
323 #else
324     return false;
325 #endif
326 }
327 
transparentNavigationBar() const328 bool AsemanDevices::transparentNavigationBar() const
329 {
330 #ifdef Q_OS_ANDROID
331     return p->java_layer->transparentNavigationBar();
332 #else
333     return false;
334 #endif
335 }
336 
standardTitleBarHeight() const337 qreal AsemanDevices::standardTitleBarHeight() const
338 {
339     static qreal res = 0;
340     if(res)
341         return res;
342 
343     if(isDesktop() || lcdPhysicalSize()<5)
344         res = 50*density()*1.2;
345     else
346         res = 50*density();
347 
348     return res;
349 }
350 
densityDpi() const351 int AsemanDevices::densityDpi() const
352 {
353 #ifdef Q_OS_ANDROID
354     return p->java_layer->densityDpi();
355 #else
356     return lcdDpiX();
357 #endif
358 }
359 
density() const360 qreal AsemanDevices::density() const
361 {
362 #ifdef Q_OS_ANDROID
363     qreal ratio = isTablet()? 1.28 : 1;
364 //    if( isLargeTablet() )
365 //        ratio = 1.6;
366 
367     return p->java_layer->density()*ratio;
368 #else
369 #ifdef Q_OS_IOS
370     qreal ratio = isTablet()? 1.28 : 1;
371     return ratio*densityDpi()/180.0;
372 #else
373 #if defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD)
374 #ifdef Q_OS_UBUNTUTOUCH
375     return screen()->logicalDotsPerInch()/UTOUCH_DEFAULT_DPI;
376 #else
377     return screen()->logicalDotsPerInch()/LINUX_DEFAULT_DPI;
378 #endif
379 #else
380 #ifdef Q_OS_WIN32
381     return 0.95*screen()->logicalDotsPerInch()/WINDOWS_DEFAULT_DPI;
382 #else
383     return 1;
384 #endif
385 #endif
386 #endif
387 #endif
388 }
389 
fontDensity() const390 qreal AsemanDevices::fontDensity() const
391 {
392 #ifdef Q_OS_ANDROID
393     qreal ratio = isMobile()? (1.28)*1.25 : (1.28)*1.35;
394     return p->java_layer->density()*ratio;
395 #else
396 #ifdef Q_OS_IOS
397     return 1.4;
398 #else
399 #if defined(Q_OS_LINUX) || defined(Q_OS_OPENBSD)
400 #ifdef Q_OS_UBUNTUTOUCH
401     qreal ratio = 1.3;
402     return ratio*density();
403 #else
404     qreal ratio = 1.3;
405     return ratio*density();
406 #endif
407 #else
408 #ifdef Q_OS_WIN32
409     qreal ratio = 1.4;
410     return ratio*density();
411 #else
412     qreal ratio = 1.3;
413     return ratio*density();
414 #endif
415 #endif
416 #endif
417 #endif
418 }
419 
cameraIsAvailable() const420 bool AsemanDevices::cameraIsAvailable() const
421 {
422 #ifdef ASEMAN_MULTIMEDIA
423 #if (QT_VERSION >= QT_VERSION_CHECK(5, 3, 0))
424     return QCameraInfo::availableCameras().count();
425 #else
426     return false;
427 #endif
428 #else
429     return false;
430 #endif
431 }
432 
localFilesPrePath()433 QString AsemanDevices::localFilesPrePath()
434 {
435 #ifdef Q_OS_WIN
436     return "file:///";
437 #else
438     return "file://";
439 #endif
440 }
441 
clipboard() const442 QString AsemanDevices::clipboard() const
443 {
444     return QGuiApplication::clipboard()->text();
445 }
446 
keyboard() const447 bool AsemanDevices::keyboard() const
448 {
449     return p->keyboard_stt;
450 }
451 
clipboardUrl() const452 QList<QUrl> AsemanDevices::clipboardUrl() const
453 {
454     return QGuiApplication::clipboard()->mimeData()->urls();
455 }
456 
setClipboardUrl(const QList<QUrl> & urls)457 void AsemanDevices::setClipboardUrl(const QList<QUrl> &urls)
458 {
459     QString data = "copy";
460 
461     foreach( const QUrl &url, urls )
462         data += "\nfile://" + url.toLocalFile();
463 
464     QMimeData *mime = new QMimeData();
465     mime->setUrls(urls);
466     mime->setData( "x-special/gnome-copied-files", data.toUtf8() );
467 
468     QGuiApplication::clipboard()->setMimeData(mime);
469 }
470 
cameraLocation()471 QString AsemanDevices::cameraLocation()
472 {
473     return AsemanApplication::cameraPath();
474 }
475 
picturesLocation()476 QString AsemanDevices::picturesLocation()
477 {
478     QStringList probs;
479     probs = QStandardPaths::standardLocations( QStandardPaths::PicturesLocation );
480 
481 #ifdef Q_OS_ANDROID
482     probs << "/sdcard/Pictures";
483 #else
484     probs << QDir::homePath() + "/Pictures";
485 #endif
486 
487     foreach( const QString & prob, probs )
488         if( QFile::exists(prob) )
489             return prob;
490 
491     return probs.last();
492 }
493 
musicsLocation()494 QString AsemanDevices::musicsLocation()
495 {
496     QStringList probs;
497     probs = QStandardPaths::standardLocations( QStandardPaths::MusicLocation );
498 
499 #ifdef Q_OS_ANDROID
500     probs << "/sdcard/Music";
501 #else
502     probs << QDir::homePath() + "/Music";
503 #endif
504 
505     foreach( const QString & prob, probs )
506         if( QFile::exists(prob) )
507             return prob;
508 
509     return probs.last();
510 }
511 
documentsLocation()512 QString AsemanDevices::documentsLocation()
513 {
514     QStringList probs;
515     probs = QStandardPaths::standardLocations( QStandardPaths::DocumentsLocation );
516 
517 #ifdef Q_OS_ANDROID
518     probs << "/sdcard/documents";
519     probs << "/sdcard/Documents";
520 #else
521     probs << QDir::homePath() + "/Documents";
522 #endif
523 
524     foreach( const QString & prob, probs )
525         if( QFile::exists(prob) )
526             return prob;
527 
528     return probs.last();
529 }
530 
downloadsLocation()531 QString AsemanDevices::downloadsLocation()
532 {
533     QStringList probs;
534     probs = QStandardPaths::standardLocations( QStandardPaths::DownloadLocation );
535 
536 #ifdef Q_OS_ANDROID
537     probs << "/sdcard/downloads";
538     probs << "/sdcard/Downloads";
539 #else
540     probs << QDir::homePath() + "/Downloads";
541 #endif
542 
543     foreach( const QString & prob, probs )
544         if( QFile::exists(prob) )
545             return prob;
546 
547     return probs.last();
548 }
549 
resourcePath()550 QString AsemanDevices::resourcePath()
551 {
552 #ifdef Q_OS_ANDROID
553     return "assets:";
554 #else
555 #ifndef Q_OS_MAC
556     QString result = QCoreApplication::applicationDirPath() + "/../share/" + QCoreApplication::applicationName().toLower();
557     QFileInfo file(result);
558     if(file.exists() && file.isDir())
559         return file.filePath();
560     else
561         return QCoreApplication::applicationDirPath() + "/";
562 #else
563     return QCoreApplication::applicationDirPath() + "/../Resources/";
564 #endif
565 #endif
566 }
567 
resourcePathQml()568 QString AsemanDevices::resourcePathQml()
569 {
570 #ifdef Q_OS_ANDROID
571     return resourcePath();
572 #else
573     return localFilesPrePath() + resourcePath();
574 #endif
575 }
576 
libsPath()577 QString AsemanDevices::libsPath()
578 {
579 #ifndef Q_OS_MAC
580     QString result = QCoreApplication::applicationDirPath() + "/../lib/" + QCoreApplication::applicationName().toLower();
581     QFileInfo file(result);
582     if(file.exists() && file.isDir())
583         return file.filePath();
584     else
585         return QCoreApplication::applicationDirPath() + "/";
586 #else
587     return QCoreApplication::applicationDirPath() + "/../Resources/";
588 #endif
589 }
590 
hideKeyboard()591 void AsemanDevices::hideKeyboard()
592 {
593 #ifndef DESKTOP_DEVICE
594     if( p->hide_keyboard_timer )
595         killTimer(p->hide_keyboard_timer);
596 
597     p->hide_keyboard_timer = startTimer(250);
598 #endif
599 }
600 
showKeyboard()601 void AsemanDevices::showKeyboard()
602 {
603 #ifndef DESKTOP_DEVICE
604     if( p->hide_keyboard_timer )
605     {
606         killTimer(p->hide_keyboard_timer);
607         p->hide_keyboard_timer = 0;
608     }
609 
610     QGuiApplication::inputMethod()->show();
611     p->keyboard_stt = true;
612 
613     emit keyboardChanged();
614 #endif
615 }
616 
share(const QString & subject,const QString & message)617 void AsemanDevices::share(const QString &subject, const QString &message)
618 {
619 #ifdef Q_OS_ANDROID
620     p->java_layer->sharePaper( subject, message );
621 #else
622     QString adrs = QString("mailto:%1?subject=%2&body=%3").arg(QString(),subject,message);
623     QDesktopServices::openUrl( adrs );
624 #endif
625 }
626 
openFile(const QString & address)627 void AsemanDevices::openFile(const QString &address)
628 {
629 #ifdef Q_OS_ANDROID
630     const QMimeType & t = p->mime_db.mimeTypeForFile(address);
631     p->java_layer->openFile( address, t.name() );
632 #else
633     QDesktopServices::openUrl( QUrl(address) );
634 #endif
635 }
636 
shareFile(const QString & address)637 void AsemanDevices::shareFile(const QString &address)
638 {
639 #ifdef Q_OS_ANDROID
640     const QMimeType & t = p->mime_db.mimeTypeForFile(address);
641     p->java_layer->shareFile( address, t.name() );
642 #else
643     QDesktopServices::openUrl( QUrl(address) );
644 #endif
645 }
646 
setClipboard(const QString & text)647 void AsemanDevices::setClipboard(const QString &text)
648 {
649     QGuiApplication::clipboard()->setText( text );
650 }
651 
startCameraPicture()652 bool AsemanDevices::startCameraPicture()
653 {
654 #ifdef Q_OS_ANDROID
655     return p->java_layer->startCamera( cameraLocation() + "/aseman_" + QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch()) + ".jpg" );
656 #else
657     return false;
658 #endif
659 }
660 
getOpenPictures()661 bool AsemanDevices::getOpenPictures()
662 {
663 #ifdef Q_OS_ANDROID
664     return p->java_layer->getOpenPictures();
665 #else
666     return false;
667 #endif
668 }
669 
incoming_share(const QString & title,const QString & msg)670 void AsemanDevices::incoming_share(const QString &title, const QString &msg)
671 {
672     emit incomingShare(title,msg);
673 }
674 
incoming_image(const QString & path)675 void AsemanDevices::incoming_image(const QString &path)
676 {
677     emit incomingImage(path);
678 }
679 
select_image_result(const QString & path)680 void AsemanDevices::select_image_result(const QString &path)
681 {
682     emit selectImageResult(path);
683 }
684 
activity_paused()685 void AsemanDevices::activity_paused()
686 {
687     emit activityPaused();
688 }
689 
activity_resumed()690 void AsemanDevices::activity_resumed()
691 {
692     emit activityResumed();
693 }
694 
keyboard_changed()695 void AsemanDevices::keyboard_changed()
696 {
697     emit keyboardChanged();
698 }
699 
timerEvent(QTimerEvent * e)700 void AsemanDevices::timerEvent(QTimerEvent *e)
701 {
702     if( e->timerId() == p->hide_keyboard_timer )
703     {
704         killTimer(p->hide_keyboard_timer);
705         p->hide_keyboard_timer = 0;
706 
707         QGuiApplication::inputMethod()->hide();
708         p->keyboard_stt = false;
709 
710         emit keyboardChanged();
711     }
712 }
713 
~AsemanDevices()714 AsemanDevices::~AsemanDevices()
715 {
716     delete p;
717 }
718