Commit 8b7f4e75 authored by Sergey Galin's avatar Sergey Galin

Added theme / density calculation based on hadrware DPI rather than system (manufacturer's) choice.

Refactoring of QAndroidDisplayMetrics.
parent d782977c
......@@ -13,13 +13,13 @@
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the DoubleGIS, LLC nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
......@@ -84,12 +84,14 @@ static float densityFromTheme(QAndroidDisplayMetrics::Theme theme)
}
static QAndroidDisplayMetrics::Theme themeFromDensity(int density_dpi, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
static QAndroidDisplayMetrics::Theme themeFromLogicalDensity(
int density_dpi
, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
{
QAndroidDisplayMetrics::Theme resulting_theme = all_themes[0].theme;
for (size_t i = 0; i < all_themes_count; ++i)
for (size_t i = 1; i < all_themes_count; ++i)
{
// Don't see unallowed themes
// Skip unwanted themes
if (all_themes[i].availability > intermediate_densities)
{
continue;
......@@ -108,21 +110,81 @@ static QAndroidDisplayMetrics::Theme themeFromDensity(int density_dpi, QAndroidD
}
// Returns ratio of a/b or b/a, the one which is <= 1.0.
// The bigger the ratio, the closer the numbers are.
// a, b, should be > 1e-6.
static float matchK(float a, float b)
{
return (a <= 1e-6f || b <= 1e-6f)
? 0.0f
: ((a > b) ? b / a : a / b);
}
static QAndroidDisplayMetrics::Theme themeFromHardwareDensity(
float density_dpi
, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
{
QAndroidDisplayMetrics::Theme resulting_theme = all_themes[0].theme;
float bestK = matchK(density_dpi, static_cast<float>(all_themes[0].starting_ppi));
for (size_t i = 1; i < all_themes_count; ++i)
{
// Skip unwanted themes
if (all_themes[i].availability > intermediate_densities)
{
continue;
}
const float k = matchK(density_dpi, static_cast<float>(all_themes[i].starting_ppi));
if (k > bestK)
{
bestK = k;
resulting_theme = all_themes[i].theme;
}
}
return resulting_theme;
}
static float guessRealisticDpi(float xdpi, float ydpi, float logicalDpi)
{
float realisticDpi = (xdpi + ydpi) / 2.0f;
if (logicalDpi > 0.0f)
{
float difference = realisticDpi / logicalDpi;
if (difference > 1.0f)
{
difference = 1.0f / difference;
}
if (difference < 0.75f)
{
qWarning() << "Average hardware DPI is reported as" << realisticDpi
<< "but physical DPI is" << logicalDpi
<< "(too different). Falling back to logical value.";
realisticDpi = logicalDpi;
}
}
return realisticDpi;
}
QAndroidDisplayMetrics::QAndroidDisplayMetrics(
QObject * parent
, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
, QAndroidDisplayMetrics::IntermediateDensities allow_intermediate_densities)
: QObject(parent)
, density_(1.0f)
, densityDpi_(160)
, scaledDensity_(1.0f)
, densityFromDpi_(1.0f)
, scaledDensityFromDpi_(1.0f)
, xdpi_(160.0f)
, ydpi_(160.0f)
, realisticDpi_(160.0f)
, logicalDensityFromCurrentTheme_(1.0f)
, scaledDensityFromCurrentTheme_(1.0f)
, logicalDensityFromHardwareDpiTheme_(1.0f)
, scaledDensityFromHardwareDpiTheme_(1.0f)
, physicalXDpi_(160.0f)
, physicalYDpi_(160.0f)
, realisticPhysicalDpi_(160.0f)
, widthPixels_(240)
, heightPixels_(240)
, theme_(ThemeMDPI)
, themeFromDensityDpi_(ThemeMDPI)
, themeFromHardwareDpi_(ThemeMDPI)
{
QJniObject metrics("android/util/DisplayMetrics", "");
{
......@@ -136,52 +198,44 @@ QAndroidDisplayMetrics::QAndroidDisplayMetrics(
densityDpi_ = metrics.getIntField("densityDpi");
heightPixels_ = metrics.getIntField("heightPixels");
scaledDensity_ = metrics.getFloatField("scaledDensity");
xdpi_ = metrics.getFloatField("xdpi");
ydpi_ = metrics.getFloatField("ydpi");
physicalXDpi_ = metrics.getFloatField("xdpi");
physicalYDpi_ = metrics.getFloatField("ydpi");
widthPixels_ = metrics.getIntField("widthPixels");
heightPixels_ = metrics.getIntField("heightPixels");
realisticPhysicalDpi_ = guessRealisticDpi(
physicalXDpi_
, physicalYDpi_
, static_cast<float>(densityDpi_));
// Calculating theme
theme_ = themeFromDensity(densityDpi_, intermediate_densities);
themeFromDensityDpi_ = themeFromLogicalDensity(densityDpi_, allow_intermediate_densities);
themeFromHardwareDpi_ = themeFromHardwareDensity(realisticPhysicalDpi_, allow_intermediate_densities);
// Calculating scaler from the theme
densityFromDpi_ = densityFromTheme(theme_);
logicalDensityFromCurrentTheme_ = densityFromTheme(themeFromDensityDpi_);
logicalDensityFromHardwareDpiTheme_ = densityFromTheme(themeFromHardwareDpi_);
if (density_ > 0.0f)
{
scaledDensityFromDpi_ = densityFromDpi_ * (scaledDensity_ / density_);
}
else
{
scaledDensityFromDpi_ = densityFromDpi_;
}
const float scale = (density_ > 0.0f) ? (scaledDensity_ / density_) : 1.0f;
realisticDpi_ = (xdpi_ + ydpi_) / 2.0f;
if (densityDpi_ > 0.0f)
{
float difference = realisticDpi_ / float(densityDpi_);
if (difference > 1.0f)
{
difference = 1.0f / difference;
}
if (difference < 0.75f)
{
qWarning() << "Average hardware DPI is reported as" << realisticDpi_ << "but physical DPI is"
<< densityDpi_ << "(too different).";
realisticDpi_ = float(densityDpi_);
}
}
scaledDensityFromCurrentTheme_ = logicalDensityFromCurrentTheme_ * scale;
scaledDensityFromHardwareDpiTheme_ = logicalDensityFromHardwareDpiTheme_ * scale;
qDebug()
<< "QAndroidDisplayMetrics: DP =" << density()
<< "/ logical DPI =" << densityDpi()
<< "/ scaled DP =" << scaledDensity()
<< "/ DPI X =" << xdpi() << "/ Y =" << ydpi()
<< "/ realistic DPI =" << realisticPhysicalDpi_
<< "/ W =" << widthPixels() << "/ H =" << heightPixels()
<< "/ SYSTEM THEME:" << static_cast<int>(themeFromDensityDpi_) << themeDirectoryName()
<< "/ density =" << logicalDensityFromCurrentTheme_
<< "/ scaled =" << scaledDensityFromCurrentTheme_
qDebug() << "QAndroidDisplayMetrics: density =" << density() << "/ densityDpi =" << densityDpi()
<< "/ scaledDensity =" << scaledDensity()
<< "/ xdpi =" << xdpi() << "/ ydpi =" << ydpi()
<< "/ realisticDpi =" << realisticDpi_
<< "/ widthPixels =" << widthPixels() << "/ heightPixels =" << heightPixels()
<< "/ Theme =" << int(theme_) << themeDirectoryName()
<< "/ densityFromDpi =" << densityFromDpi_
<< "/ scaledDensityFromDpi =" << scaledDensityFromDpi_;
<< "/ HARDWARE THEME:" << static_cast<int>(themeFromHardwareDpi_) << themeDirectoryName(themeFromHardwareDpi_)
<< "/ density =" << logicalDensityFromHardwareDpiTheme_
<< "/ scale =" << scaledDensityFromHardwareDpiTheme_;
}
void QAndroidDisplayMetrics::preloadJavaClasses()
{
QAndroidQPAPluginGap::preloadJavaClasses();
......@@ -190,6 +244,7 @@ void QAndroidDisplayMetrics::preloadJavaClasses()
QAndroidQPAPluginGap::preloadJavaClass("android/content/res/Configuration");
}
QString QAndroidDisplayMetrics::themeDirectoryName(Theme theme)
{
switch(theme)
......@@ -212,6 +267,7 @@ QString QAndroidDisplayMetrics::themeDirectoryName(Theme theme)
};
}
float QAndroidDisplayMetrics::fontScale()
{
QAndroidQPAPluginGap::Context activity;
......
......@@ -74,15 +74,19 @@ private:
Q_PROPERTY(float density READ density)
Q_PROPERTY(int densityDpi READ densityDpi)
Q_PROPERTY(float scaledDensity READ scaledDensity)
Q_PROPERTY(float densityFromDpi READ densityFromDpi)
Q_PROPERTY(float scaledDensityFromDpi READ scaledDensityFromDpi)
Q_PROPERTY(float logicalDensityFromCurrentTheme READ logicalDensityFromCurrentTheme)
Q_PROPERTY(float scaledDensityFromCurrentTheme READ scaledDensityFromCurrentTheme)
Q_PROPERTY(float logicalDensityFromHardwareDpiTheme READ logicalDensityFromHardwareDpiTheme)
Q_PROPERTY(float scaledDensityFromHardwareDpiTheme READ scaledDensityFromHardwareDpiTheme)
Q_PROPERTY(float xdpi READ xdpi)
Q_PROPERTY(float ydpi READ ydpi)
Q_PROPERTY(float realisticDpi READ realisticDpi)
Q_PROPERTY(int widthPixels READ widthPixels)
Q_PROPERTY(int heightPixels READ heightPixels)
Q_PROPERTY(Theme theme READ theme)
Q_PROPERTY(Theme hardwareTheme READ hardwareTheme)
Q_PROPERTY(QString themeDirectoryName READ themeDirectoryName)
Q_PROPERTY(QString hardwareThemeDirectoryName READ hardwareThemeDirectoryName)
public:
// Correctness of the names and constants can be verified here:
......@@ -103,85 +107,91 @@ public:
ANDROID_DENSITY_560 = 560,
ANDROID_DENSITY_XXXHIGH = 640;
// Enum to control selection on additional intermediate screen densities.
// The integer values are just hints for readability.
enum IntermediateDensities
{
// Themes availablity.
// The integer values are just hints for readability.
IntermediateNone = 0, // Use only major themes (integer and below 1.0 densities).
IntermediateWithStep0_5 = 1, // Use major themes and X.5 themes.
IntermediateAll = 2 // Use all possible themes, including X.33 and X.67 themes.
};
QAndroidDisplayMetrics(QObject * parent = 0, IntermediateDensities intermediate_densities = IntermediateAll);
QAndroidDisplayMetrics(
QObject * parent = 0
, IntermediateDensities allow_intermediate_densities = IntermediateAll);
static void preloadJavaClasses();
/*!
* Size multiplier relative to size optimized for the default (medium) DPI (160).
* As for 2014/03 the value seems to be varying from 0.75 to 3.
* Note: some devices have wrong density value based on wrong physical DPI value.
* It is safer to use densityFromDpi() or scaledDensityFromDpi().
*/
// Basic pixel density for this device as reported by the system.
// This is the number used to scale layout designed for a device with default (medium)
// resolution (160 DPI) to match the current device.
// Note: some devices have wrong density value based on wrong physical DPI value.
// It is safer to use logicalDensityFromCurrentTheme() or scaledDensityFromCurrentTheme(),
// especially when supporting older Androids.
float density() const { return density_; }
/*!
* "Logical" resoultion of the screen.
* This function should return value from one of the ANDROID_DENSITY_... constants
* and can be used to select graphical resources.
*/
// Logical resolution of the screen (the one that matches density()).
// It is a value selected by maker of the device from the set of standard resolutions
// and should be something close to the hardware resolution.
// This function should return value from one of the ANDROID_DENSITY_... constants
// and can be used to select set of graphical resources appopriate for the device.
int densityDpi() const { return densityDpi_; }
/*!
* Size multiplier relative to size optimized for the default (medium) DPI (160),
* but also takes into account user's enlarged/reduced font setting.
* See also comments for density().
*/
// Size multiplier relative to size optimized for the default (medium) DPI (160),
// but also takes into account user's enlarged/reduced font setting.
// See also comments for density().
float scaledDensity() const { return scaledDensity_; }
Theme theme() const { return theme_; }
// Device's default theme, based on densityDpi().
Theme theme() const { return themeFromDensityDpi_; }
// Device's theme based on the hardware parameters rather than the manufacturer's choice.
Theme hardwareTheme() const { return themeFromHardwareDpi_; }
// Standard resource directory name (suffix) used for the theme: "ldpi", "mdpi", and etc.
static QString themeDirectoryName(Theme theme);
// Standard resource directory name for the current device's theme().
QString themeDirectoryName() const { return themeDirectoryName(theme()); }
/*!
* Same as density(), but the value is looked up from a table by densityDpi().
* This is more reliable because some devices have wrong density value, but
* logical DPI is always correct.
*/
float densityFromDpi() const { return densityFromDpi_; }
/*!
* Same as density(), but the value is looked up from a table by densityDpi().
* This is more reliable because some devices have wrong density value, but
* logical DPI is always correct.
*/
float scaledDensityFromDpi() const { return scaledDensityFromDpi_; }
/*!
* Exact physical resolution of the screen.
* Maybe set to wrong value on some devices.
*/
float xdpi() const { return xdpi_; }
/*!
* Exact physical resolution of the screen.
* Maybe set to wrong value on some devices.
*/
float ydpi() const { return ydpi_; }
/*!
* Returns the most probable value of physical screen DPI.
* This is either average between xdpi and ydpi, or logical DPI if
* it is too different from the reported physical DPI.
* This value works good for phones and tablets with wrong physical
* DPI setting.
*/
float realisticDpi() const { return realisticDpi_; }
//! Pixel size of the screen
// Standard resource directory name for theme based on hardware parameters rather
// than manufacturer's choice.
QString hardwareThemeDirectoryName() const { return themeDirectoryName(hardwareTheme()); }
// Workaround function: get screen density exactly matching theme().
// This is more reliable as some older devices have wrong density value, but
// logical DPI is always correct.
// Old name: densityFromDpi().
float logicalDensityFromCurrentTheme() const { return logicalDensityFromCurrentTheme_; }
// Get screen density for the theme based on the hardware parameters rather than
// the the manufacturer's choice.
float logicalDensityFromHardwareDpiTheme() const { return logicalDensityFromHardwareDpiTheme_; }
// Workaround function: get screen densityFromDpi() and apply user setting
// for UI scaling.
// Old name: scaledDensityFromDpi().
float scaledDensityFromCurrentTheme() const { return scaledDensityFromCurrentTheme_; }
// Get scaled screen density for the theme based on the hardware parameters rather than
// manufacturer's choice.
float scaledDensityFromHardwareDpiTheme() const { return scaledDensityFromHardwareDpiTheme_; }
// Exact physical resolution of the screen.
// Warning: it may be set to a wrong value on some devices (due to manufacturer's
// error or bad way of UI tweaking).
float xdpi() const { return physicalXDpi_; }
float ydpi() const { return physicalYDpi_; }
// Returns the best possible bet on physical screen resolution.
// This is either average between xdpi and ydpi, or logical DPI if
// it is too different from the reported physical DPI.
// This value works good for phones and tablets with wrong physical
// DPI setting.
float realisticDpi() const { return realisticPhysicalDpi_; }
// Pixel size of the screen
int widthPixels() const { return widthPixels_; }
//! Pixel size of the screen
int heightPixels() const { return heightPixels_; }
// Font scale, as configured by user.
......@@ -193,14 +203,17 @@ private:
float density_;
int densityDpi_;
float scaledDensity_;
float densityFromDpi_;
float scaledDensityFromDpi_;
float xdpi_;
float ydpi_;
float realisticDpi_;
float logicalDensityFromCurrentTheme_;
float scaledDensityFromCurrentTheme_;
float logicalDensityFromHardwareDpiTheme_;
float scaledDensityFromHardwareDpiTheme_;
float physicalXDpi_;
float physicalYDpi_;
float realisticPhysicalDpi_;
int widthPixels_;
int heightPixels_;
Theme theme_;
Theme themeFromDensityDpi_;
Theme themeFromHardwareDpi_;
};
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment