Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>

<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.firefox.qt.common

import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.provider.Settings

object BatteryOptimizationHelper {

@JvmStatic
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
return try {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
pm.isIgnoringBatteryOptimizations(context.packageName)
} catch (e: Exception) { true }
}

@JvmStatic
fun hasRequestIgnoreBatteryOptimizationsPermission(context: Context): Boolean {
return try {
context.packageManager.checkPermission(
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS",
context.packageName) == PackageManager.PERMISSION_GRANTED
} catch (e: Exception) { false }
}

@JvmStatic
fun getRequestIgnoreBatteryOptimizationsIntent(context: Context): Intent? {
return try {
if (hasRequestIgnoreBatteryOptimizationsPermission(context)) {
Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = Uri.parse("package:${context.packageName}")
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:${context.packageName}")
}
} else {
Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
}
} catch (e: Exception) { null }
}
}
43 changes: 43 additions & 0 deletions src/platforms/android/androidbatteryoptimizer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "androidbatteryoptimizer.h"

#include <QJniObject>

#include "androidcommons.h"

constexpr auto BATTERY_HELPER_CLASS =
"org/mozilla/firefox/qt/common/BatteryOptimizationHelper";

// static
bool AndroidBatteryOptimizer::batteryOptimizationEnabled() {
QJniObject ctx = AndroidCommons::getActivity();
jboolean ignoring = QJniObject::callStaticMethod<jboolean>(
BATTERY_HELPER_CLASS, "isIgnoringBatteryOptimizations",
"(Landroid/content/Context;)Z", ctx.object<jobject>());
return !ignoring;
}

// static
bool AndroidBatteryOptimizer::canTriggerIntent() {
QJniObject ctx = AndroidCommons::getActivity();
return (bool)QJniObject::callStaticMethod<jboolean>(
BATTERY_HELPER_CLASS, "hasRequestIgnoreBatteryOptimizationsPermission",
"(Landroid/content/Context;)Z", ctx.object<jobject>());
}

// static
void AndroidBatteryOptimizer::triggerBatteryOptimizationIntent() {
QJniObject ctx = AndroidCommons::getActivity();
QJniObject intent = QJniObject::callStaticObjectMethod(
BATTERY_HELPER_CLASS, "getRequestIgnoreBatteryOptimizationsIntent",
"(Landroid/content/Context;)Landroid/content/Intent;",
ctx.object<jobject>());
if (!intent.isValid()) {
return;
}
ctx.callMethod<void>("startActivity", "(Landroid/content/Intent;)V",
intent.object<jobject>());
}
23 changes: 23 additions & 0 deletions src/platforms/android/androidbatteryoptimizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef ANDROIDBATTERYOPTIMIZER_H
#define ANDROIDBATTERYOPTIMIZER_H

class AndroidBatteryOptimizer {
public:
// true → optimization IS enabled (VPN at risk)
// false → app is exempted (good)
static bool batteryOptimizationEnabled();

// true → app holds REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; system dialog shown
// directly
static bool canTriggerIntent();

// Opens battery settings (system dialog or app details depending on
// permission/SDK)
static void triggerBatteryOptimizationIntent();
};

#endif // ANDROIDBATTERYOPTIMIZER_H
2 changes: 2 additions & 0 deletions src/platforms/android/sources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ target_sources(shared-sources INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcryptosettings.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcommons.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcommons.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidbatteryoptimizer.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidbatteryoptimizer.cpp
)

target_link_libraries(shared-sources INTERFACE -ljnigraphics)
10 changes: 10 additions & 0 deletions src/settingslist.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,16 @@ SETTING_BOOL(onboardingCompleted, // getter
false // sensitive (do not log)
)

SETTING_BOOL(hasDismissedBatteryOptimization, // getter
setHasDismissedBatteryOptimization, // setter
removeHasDismissedBatteryOptimization, // remover
hasHasDismissedBatteryOptimization, // has
"hasDismissedBatteryOptimization", // key
false, // default value
true, // remove when reset (clears on logout)
false // sensitive (do not log)
)

SETTING_BOOL(onboardingDataCollectionEnabled, // getter
setOnboardingDataCollectionEnabled, // setter
removeOnboardingDataCollectionEnabled, // remover
Expand Down
6 changes: 6 additions & 0 deletions src/translations/strings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ settings:
dnsSettingsWarning: These options may cause some websites to break or not display content correctly.
dnsSettingsInputPlaceholder: Enter custom DNS
dnsSettingsDisconnectWarning: Changing these settings will cause the VPN to disconnect and reconnect automatically.
batteryOptimizationWarning:
value: On some devices, Mozilla VPN may be disconnected in the background. If you’ve experienced this, allow it to run in the background.
comment: Warning shown in the settings screen on Android when battery optimization is enabled for the app.
batteryOptimizationWarningButton:
value: Allow background permissions
comment: Button label for the call to action to allow background permissions on Android.
appExclusionClearAllApps:
value: Clear all
comment: Button label to clear all apps from the exclusions list
Expand Down
3 changes: 3 additions & 0 deletions src/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,10 @@ target_include_directories(mozillavpn-uiplugin PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/types
)

target_sources(mozillavpn-ui PRIVATE singletons/BatteryOptimizer.h)

if(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
target_compile_definitions(mozillavpn-ui PRIVATE MZ_ANDROID)
target_sources(mozillavpn-ui PRIVATE singletons/VPNAndroidUtils.h)
target_sources(mozillavpn-ui PRIVATE singletons/VPNAndroidCommons.h)
endif()
Expand Down
24 changes: 24 additions & 0 deletions src/ui/screens/settings/ViewSettingsMenu.qml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ MZViewBase {
spacing: MZTheme.theme.windowMargin
Layout.fillWidth: true

MZInformationCard {
id: batteryOptimizationCard
visible: !MZSettings.hasDismissedBatteryOptimization
&& BatteryOptimizer.batteryOptimizationEnabled
&& BatteryOptimizer.canTriggerIntent
cardType: MZInformationCard.CardType.Warning
Layout.preferredWidth: Math.min(window.width - MZTheme.theme.windowMargin * 2,
MZTheme.theme.navBarMaxWidth)
Layout.minimumHeight: batteryWarningText.height + MZTheme.theme.windowMargin * 2
Layout.alignment: Qt.AlignHCenter

_infoContent: MZTextBlock {
id: batteryWarningText
Layout.fillWidth: true
text: MZI18n.SettingsBatteryOptimizationWarning
}
_buttonAction: function() {
MZSettings.hasDismissedBatteryOptimization = true
BatteryOptimizer.triggerBatteryOptimizationIntent()
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we set hasDismissedBatteryOptimization to true, the warning won't reappear, right? (e.g. if the user dismissed the system dialog)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the idea :) - The intention is to give a hint, not to force them to enable this.

_buttonText : MZI18n.SettingsBatteryOptimizationWarningButton

}

ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: MZTheme.theme.windowMargin /2
Expand Down
54 changes: 54 additions & 0 deletions src/ui/singletons/BatteryOptimizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BATTERYOPTIMIZER_H
#define BATTERYOPTIMIZER_H

#include <QObject>
#include <QQmlEngine>

#ifdef MZ_ANDROID
# include "platforms/android/androidbatteryoptimizer.h"
#endif

class BatteryOptimizer : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON

Q_PROPERTY(bool batteryOptimizationEnabled READ batteryOptimizationEnabled
NOTIFY stateChanged)
Q_PROPERTY(bool canTriggerIntent READ canTriggerIntent NOTIFY stateChanged)

public:
bool batteryOptimizationEnabled() const {
#ifdef MZ_ANDROID
return AndroidBatteryOptimizer::batteryOptimizationEnabled();
#else
return false;
#endif
}

bool canTriggerIntent() const {
#ifdef MZ_ANDROID
return AndroidBatteryOptimizer::canTriggerIntent();
#else
return false;
#endif
}

Q_INVOKABLE void triggerBatteryOptimizationIntent() {
#ifdef MZ_ANDROID
AndroidBatteryOptimizer::triggerBatteryOptimizationIntent();
#endif
}

// Call from QML after returning from the settings screen to refresh bindings.
Q_INVOKABLE void refreshState() { emit stateChanged(); }

signals:
void stateChanged();
};

#endif // BATTERYOPTIMIZER_H
Loading