Commit ba663d28 authored by Sergey Galin's avatar Sergey Galin

Merge branch 'feature/font_scales' into 'master'

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

See merge request qt/qtandroidextensions!83
parents d782977c 7d036e8e
......@@ -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,18 @@ static float densityFromTheme(QAndroidDisplayMetrics::Theme theme)
}
static QAndroidDisplayMetrics::Theme themeFromDensity(int density_dpi, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
// Find a theme that matches logical density.
// Normally, density_dpi should be EXACT MATCH to one of the known themes.
// However, if the application doesn't want to use some intermediate densities
// and the device offers one, it will return the next "more round" theme.
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 +114,85 @@ 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 (relatively) the numbers are.
// a, b, should be > 1e-6.
static float proximityRatio(float a, float b)
{
return (a <= 1e-6f || b <= 1e-6f)
? 0.0f
: ((a > b) ? b / a : a / b);
}
// Find a theme that matches hardware density (thus ignoring manufacturer's choice
// of the theme for the device). The matching is done by choosing the theme that
// has the closest DPI to the hardware value.
static QAndroidDisplayMetrics::Theme themeFromHardwareDensity(
float density_dpi
, QAndroidDisplayMetrics::IntermediateDensities intermediate_densities)
{
QAndroidDisplayMetrics::Theme resulting_theme = all_themes[0].theme;
float bestK = proximityRatio(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 = proximityRatio(density_dpi, static_cast<float>(all_themes[i].starting_ppi));
if (k > bestK)
{
bestK = k;
resulting_theme = all_themes[i].theme;
}
}
return resulting_theme;
}
// Find out which actual hardware DPI we have. Some Android devices report wrong hardware
// DPI values so we have to use some heuristics.
static float guessRealisticHardwareDpi(float xdpi, float ydpi, float logicalDpi)
{
// Let's start with average hardware resoltion.
float realisticDpi = (xdpi + ydpi) / 2.0f;
if (logicalDpi > 0.0f)
{
// Max difference between standard adjacent logical DPI values is 1.5 or 0.66.
// If hardware DPI differs from logical DPI more than 0.66 times we assume
// that the hardware value is set wrong and fall back to the logical value.
if (proximityRatio(realisticDpi, logicalDpi) < 0.66f)
{
qWarning() << "Average hardware DPI is reported as" << realisticDpi
<< "but logical DPI is" << logicalDpi
<< "(too different). Falling back to the 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)
, densityFromSystemTheme_(1.0f)
, scaledDensityFromSystemTheme_(1.0f)
, densityFromHardwareDpiTheme_(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 +206,45 @@ 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");
// Calculating theme
theme_ = themeFromDensity(densityDpi_, intermediate_densities);
realisticPhysicalDpi_ = guessRealisticHardwareDpi(
physicalXDpi_
, physicalYDpi_
, static_cast<float>(densityDpi_));
// Calculating scaler from the theme
densityFromDpi_ = densityFromTheme(theme_);
themeFromDensityDpi_ = themeFromLogicalDensity(densityDpi_, allow_intermediate_densities);
themeFromHardwareDpi_ = themeFromHardwareDensity(realisticPhysicalDpi_, allow_intermediate_densities);
if (density_ > 0.0f)
{
scaledDensityFromDpi_ = densityFromDpi_ * (scaledDensity_ / density_);
}
else
{
scaledDensityFromDpi_ = densityFromDpi_;
}
densityFromSystemTheme_ = densityFromTheme(themeFromDensityDpi_);
densityFromHardwareDpiTheme_ = densityFromTheme(themeFromHardwareDpi_);
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_);
}
}
const float scale = (density_ > 0.0f) ? (scaledDensity_ / density_) : 1.0f;
scaledDensityFromSystemTheme_ = densityFromSystemTheme_ * scale;
scaledDensityFromHardwareDpiTheme_ = densityFromHardwareDpiTheme_ * scale;
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_;
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 =" << densityFromSystemTheme_
<< "/ scaled =" << scaledDensityFromSystemTheme_
<< "/ HARDWARE THEME:" << static_cast<int>(themeFromHardwareDpi_) << themeDirectoryName(themeFromHardwareDpi_)
<< "/ density =" << densityFromHardwareDpiTheme_
<< "/ scale =" << scaledDensityFromHardwareDpiTheme_;
}
void QAndroidDisplayMetrics::preloadJavaClasses()
{
QAndroidQPAPluginGap::preloadJavaClasses();
......@@ -190,6 +253,7 @@ void QAndroidDisplayMetrics::preloadJavaClasses()
QAndroidQPAPluginGap::preloadJavaClass("android/content/res/Configuration");
}
QString QAndroidDisplayMetrics::themeDirectoryName(Theme theme)
{
switch(theme)
......@@ -212,6 +276,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 densityFromSystemTheme READ densityFromSystemTheme)
Q_PROPERTY(float scaledDensityFromSystemTheme READ scaledDensityFromSystemTheme)
Q_PROPERTY(float densityFromHardwareDpiTheme READ densityFromHardwareDpiTheme)
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,92 @@ public:
ANDROID_DENSITY_560 = 560,
ANDROID_DENSITY_XXXHIGH = 640;
// Enum to control selection on additional intermediate screen densities.
// The bigger the integer value, the more possible themes can be matched.
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 densityFromSystemTheme() or scaledDensityFromSystemTheme(),
// 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.
// On all properly configured devices the value should be the same as density().
// Old name: densityFromDpi().
float densityFromSystemTheme() const { return densityFromSystemTheme_; }
// Get screen density for the theme based on the hardware parameters rather than
// the the manufacturer's choice.
float densityFromHardwareDpiTheme() const { return densityFromHardwareDpiTheme_; }
// Workaround function: get screen densityFromDpi() and apply user setting
// for UI scaling.
// Old name: scaledDensityFromDpi().
float scaledDensityFromSystemTheme() const { return scaledDensityFromSystemTheme_; }
// 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 +204,17 @@ private:
float density_;
int densityDpi_;
float scaledDensity_;
float densityFromDpi_;
float scaledDensityFromDpi_;
float xdpi_;
float ydpi_;
float realisticDpi_;
float densityFromSystemTheme_;
float scaledDensityFromSystemTheme_;
float densityFromHardwareDpiTheme_;
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