Skip to content

Commit 9193bd5

Browse files
authored
Merge pull request #1257 from dg0yt
Update Android support
2 parents 5596ec1 + e1e5bbb commit 9193bd5

File tree

7 files changed

+321
-4
lines changed

7 files changed

+321
-4
lines changed

android/AndroidManifest.xml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,16 @@
8888
</activity>
8989
</application>
9090

91-
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="19"/>
91+
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
9292
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
9393

9494
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
9595
Remove the comment if you do not require these default permissions. -->
9696
<!-- Not used for Mapper: %%INSERT_PERMISSIONS -->
97-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
98-
<uses-permission android:name="android.permission.INTERNET"/>
99-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
97+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><!-- PROTECTION_DANGEROUS -->
98+
<uses-permission android:name="android.permission.INTERNET"/><!-- PROTECTION_NORMAL -->
99+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><!-- PROTECTION_DANGEROUS -->
100+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><!-- PROTECTION_DANGEROUS -->
100101

101102
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
102103
Remove the comment if you do not require these default features. -->

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ set(Mapper_Common_SRCS
4444
mapper_resource.cpp
4545
settings.cpp
4646

47+
core/app_permissions.cpp
4748
core/autosave.cpp
4849
core/crs_template.cpp
4950
core/crs_template_implementation.cpp

src/core/app_permissions.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2019 Kai Pastor
3+
*
4+
* This file is part of OpenOrienteering.
5+
*
6+
* OpenOrienteering is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* OpenOrienteering is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#include "app_permissions.h"
22+
23+
#ifdef Q_OS_ANDROID
24+
# include "app_permissions_android.cpp"
25+
#endif

src/core/app_permissions.h

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright 2019 Kai Pastor
3+
*
4+
* This file is part of OpenOrienteering.
5+
*
6+
* OpenOrienteering is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* OpenOrienteering is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#ifndef OPENORIENTEERING_APP_PERMISSIONS_H
22+
#define OPENORIENTEERING_APP_PERMISSIONS_H
23+
24+
#include <QtGlobal>
25+
26+
#ifdef Q_OS_ANDROID
27+
# include <functional>
28+
# include <type_traits>
29+
# include <QtAndroid>
30+
# include <QObject>
31+
# include <QPointer>
32+
# include <QStringList>
33+
# include <QTimer>
34+
#endif
35+
36+
37+
/**
38+
* A generic utility for requesting app permissions from the user.
39+
*
40+
* This class is modeled after the patterns found in [Qt]Android,
41+
* but provides an abstraction from OS specific aspects.
42+
*/
43+
namespace AppPermissions
44+
{
45+
/// Permissions which are required for certain features of the application.
46+
enum AppPermission
47+
{
48+
LocationAccess,
49+
StorageAccess,
50+
};
51+
52+
/// Possible results of requesting a permission.
53+
enum PermissionResult
54+
{
55+
Denied,
56+
Granted,
57+
};
58+
59+
60+
61+
/**
62+
* Checks if the permission was granted or not.
63+
*/
64+
PermissionResult checkPermission(AppPermission permission);
65+
66+
/**
67+
* Asynchronously requests a new permission to be granted.
68+
*
69+
* The given member function on the receiver will be called when the
70+
* permission is actually granted.
71+
*
72+
* This function must not be called while the requested permission is granted.
73+
*/
74+
template<class T>
75+
void requestPermission(AppPermission permission, T* object, void (T::* function)());
76+
77+
/**
78+
* Requests a permissions to be granted to the application.
79+
*
80+
* This function must not be called while the requested permission is granted.
81+
*/
82+
PermissionResult requestPermissionSync(AppPermission permission);
83+
84+
85+
86+
#ifdef Q_OS_ANDROID
87+
88+
// The Android implementation uses some helper functions.
89+
// Only the generic parts are defined in the header.
90+
91+
/// Returns the list of Android permission for the given abstract permission.
92+
QStringList androidPermissions(AppPermission permission);
93+
94+
/// Tests if all the requested Android permissions are granted.
95+
bool permissionsGranted(const QStringList& requested_permissions, const QtAndroid::PermissionResultMap& actual_permissions);
96+
97+
/// A utility for safely doing callbacks after asynchronuous requests.
98+
template <class T>
99+
struct PermissionCallback
100+
{
101+
Q_STATIC_ASSERT((std::is_base_of<QObject, T>::value));
102+
103+
using Function = void (T::*)();
104+
const QPointer<T> object;
105+
const Function function;
106+
const QStringList requested_permissions;
107+
108+
PermissionCallback(T* object, Function function, const QStringList& android_permissions)
109+
: object{object}
110+
, function{function}
111+
, requested_permissions{android_permissions}
112+
{}
113+
114+
void run(const QtAndroid::PermissionResultMap& actual_permissions)
115+
{
116+
if (object && permissionsGranted(requested_permissions, actual_permissions))
117+
QTimer::singleShot(10, object, function);
118+
delete this;
119+
}
120+
121+
};
122+
123+
template<class T>
124+
void requestPermission(AppPermission permission, T* object, void (T::* function)())
125+
{
126+
auto const android_permissions = androidPermissions(permission);
127+
auto* callback = new PermissionCallback<T>(object, function, android_permissions);
128+
QtAndroid::requestPermissions(android_permissions, [callback](const auto& result) { callback->run(result); });
129+
}
130+
131+
#else
132+
133+
// The default implementation is fully inline,
134+
// in order to allow the compiler to optimize it out.
135+
136+
inline PermissionResult checkPermission(AppPermission /*permission*/)
137+
{
138+
return Granted;
139+
}
140+
141+
template<class T>
142+
void requestPermission(AppPermission /*permission*/, T* /*object*/, void (T::* /*function*/)())
143+
{
144+
// requestPermission() shouldn't be called because permissions are always granted.
145+
Q_UNREACHABLE();
146+
}
147+
148+
inline PermissionResult requestPermissionSync(AppPermission /*permission*/)
149+
{
150+
// requestPermission() shouldn't be called because permissions are always granted.
151+
Q_UNREACHABLE();
152+
return Granted;
153+
}
154+
155+
#endif
156+
157+
} // namespace AppPermissions
158+
159+
#endif // OPENORIENTEERING_APP_PERMISSIONS_H
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
* Copyright 2019 Kai Pastor
3+
*
4+
* This file is part of OpenOrienteering.
5+
*
6+
* OpenOrienteering is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* OpenOrienteering is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#include "app_permissions.h"
22+
23+
#include <algorithm>
24+
#include <QtAndroid>
25+
26+
27+
namespace AppPermissions
28+
{
29+
30+
namespace
31+
{
32+
33+
/**
34+
* Returns a functor that tests if the passed permission is granted.
35+
*/
36+
auto granted()
37+
{
38+
return [](const QString& permission)->bool {
39+
return QtAndroid::checkPermission(permission) == QtAndroid::PermissionResult::Granted;
40+
};
41+
}
42+
43+
/**
44+
* Returns a functor that tests if the passed permissions are granted
45+
* in the actual permissions given at functor creation time.
46+
*/
47+
auto granted(const QtAndroid::PermissionResultMap& actual_permissions)
48+
{
49+
return [&actual_permissions](const QString& permission)->bool {
50+
return actual_permissions.contains(permission)
51+
&& actual_permissions[permission] == QtAndroid::PermissionResult::Granted;
52+
};
53+
}
54+
55+
} // namespace
56+
57+
58+
59+
QStringList androidPermissions(AppPermission permission)
60+
{
61+
QStringList android_permissions;
62+
android_permissions.reserve(2);
63+
64+
switch (permission)
65+
{
66+
case StorageAccess:
67+
android_permissions << QStringLiteral("android.permission.READ_EXTERNAL_STORAGE")
68+
<< QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE");
69+
break;
70+
71+
case LocationAccess:
72+
android_permissions << QStringLiteral("android.permission.ACCESS_FINE_LOCATION");
73+
break;
74+
}
75+
76+
return android_permissions;
77+
}
78+
79+
80+
bool permissionsGranted(const QStringList& requested_permissions, const QtAndroid::PermissionResultMap& actual_permissions)
81+
{
82+
using std::begin; using std::end;
83+
if (std::all_of(begin(requested_permissions), end(requested_permissions), granted(actual_permissions)))
84+
return Granted;
85+
return Denied;
86+
}
87+
88+
89+
PermissionResult checkPermission(AppPermission permission)
90+
{
91+
using std::begin; using std::end;
92+
auto const requested_permissions = androidPermissions(permission);
93+
if (std::all_of(begin(requested_permissions), end(requested_permissions), granted()))
94+
return Granted;
95+
return Denied;
96+
}
97+
98+
99+
PermissionResult requestPermissionSync(AppPermission permission)
100+
{
101+
auto const requested_permissions = androidPermissions(permission);
102+
auto const actual_permissions = QtAndroid::requestPermissionsSync(requested_permissions);
103+
if (permissionsGranted(requested_permissions, actual_permissions))
104+
return Granted;
105+
return Denied;
106+
}
107+
108+
109+
} // namespace AppPermissions

src/gui/widgets/home_screen_widget.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <QVBoxLayout>
3636

3737
#include "settings.h"
38+
#include "core/app_permissions.h"
3839
#include "core/storage_location.h" // IWYU pragma: keep
3940
#include "fileformats/file_format_registry.h"
4041
#include "gui/home_screen_controller.h"
@@ -475,6 +476,23 @@ QListWidget* HomeScreenWidgetMobile::makeFileListWidget()
475476
void HomeScreenWidgetMobile::updateFileListWidget()
476477
{
477478
file_list_widget->clear();
479+
480+
auto storage_access = AppPermissions::checkPermission(AppPermissions::StorageAccess);
481+
if (storage_access != AppPermissions::Granted)
482+
{
483+
AppPermissions::requestPermission(AppPermissions::StorageAccess, this, &HomeScreenWidgetMobile::updateFileListWidget);
484+
485+
// List examples
486+
constexpr auto filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot;
487+
constexpr auto flags = QDir::DirsLast | QDir::Name | QDir::IgnoreCase | QDir::LocaleAware;
488+
auto const info_list = QDir(QLatin1String("data:/examples")).entryInfoList(filters, flags);
489+
for (const auto& file_info : info_list)
490+
{
491+
addItemToFileList(file_info);
492+
}
493+
return;
494+
}
495+
478496
if (history.empty())
479497
{
480498
// First screen.

src/main.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ int main(int argc, char** argv)
112112
QApplication qapp(argc, argv);
113113
#endif
114114

115+
#ifdef Q_OS_ANDROID
116+
qputenv("QT_USE_ANDROID_NATIVE_STYLE", "1");
117+
#endif
118+
115119
// Load resources
116120
Q_INIT_RESOURCE(resources);
117121

0 commit comments

Comments
 (0)