1package eu.siacs.conversations.utils;
2
3import static eu.siacs.conversations.services.EventReceiver.EXTRA_NEEDS_FOREGROUND_SERVICE;
4
5import android.annotation.SuppressLint;
6import android.app.ActivityOptions;
7import android.content.Context;
8import android.content.Intent;
9import android.content.SharedPreferences;
10import android.content.pm.ApplicationInfo;
11import android.content.pm.PackageManager;
12import android.net.ConnectivityManager;
13import android.os.Build;
14import android.os.Bundle;
15import android.preference.Preference;
16import android.preference.PreferenceCategory;
17import android.preference.PreferenceManager;
18import android.util.Log;
19
20import androidx.annotation.BoolRes;
21import androidx.annotation.NonNull;
22import androidx.annotation.RequiresApi;
23import androidx.core.content.ContextCompat;
24
25import eu.siacs.conversations.Config;
26import eu.siacs.conversations.R;
27import eu.siacs.conversations.ui.SettingsActivity;
28import eu.siacs.conversations.ui.SettingsFragment;
29
30import java.util.Arrays;
31import java.util.Collections;
32import java.util.List;
33
34public class Compatibility {
35
36 private static final List<String> UNUSED_SETTINGS_POST_TWENTYSIX =
37 Arrays.asList(
38 "led",
39 "notification_ringtone",
40 "notification_headsup",
41 "vibrate_on_notification");
42 private static final List<String> UNUSED_SETTINGS_PRE_TWENTYSIX =
43 Collections.singletonList("message_notification_settings");
44
45 public static boolean hasStoragePermission(final Context context) {
46 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || ContextCompat.checkSelfPermission(
47 context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
48 }
49
50 public static boolean s() {
51 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
52 }
53
54 private static boolean runsTwentyFour() {
55 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
56 }
57
58 public static boolean runsTwentySix() {
59 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
60 }
61
62 public static boolean twentyEight() {
63 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
64 }
65
66 private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) {
67 return getPreferences(context).getBoolean(name, context.getResources().getBoolean(res));
68 }
69
70 private static SharedPreferences getPreferences(final Context context) {
71 return PreferenceManager.getDefaultSharedPreferences(context);
72 }
73
74 private static boolean targetsTwentySix(Context context) {
75 try {
76 final PackageManager packageManager = context.getPackageManager();
77 final ApplicationInfo applicationInfo =
78 packageManager.getApplicationInfo(context.getPackageName(), 0);
79 return applicationInfo == null || applicationInfo.targetSdkVersion >= 26;
80 } catch (PackageManager.NameNotFoundException | RuntimeException e) {
81 return true; // when in doubt…
82 }
83 }
84
85 private static boolean targetsTwentyFour(Context context) {
86 try {
87 final PackageManager packageManager = context.getPackageManager();
88 final ApplicationInfo applicationInfo =
89 packageManager.getApplicationInfo(context.getPackageName(), 0);
90 return applicationInfo == null || applicationInfo.targetSdkVersion >= 24;
91 } catch (PackageManager.NameNotFoundException | RuntimeException e) {
92 return true; // when in doubt…
93 }
94 }
95
96 public static boolean runsAndTargetsTwentySix(Context context) {
97 return runsTwentySix() && targetsTwentySix(context);
98 }
99
100 public static boolean runsAndTargetsTwentyFour(Context context) {
101 return runsTwentyFour() && targetsTwentyFour(context);
102 }
103
104 public static boolean keepForegroundService(Context context) {
105 return runsAndTargetsTwentySix(context)
106 || getBooleanPreference(
107 context,
108 SettingsActivity.KEEP_FOREGROUND_SERVICE,
109 R.bool.enable_foreground_service);
110 }
111
112 public static void removeUnusedPreferences(SettingsFragment settingsFragment) {
113 List<PreferenceCategory> categories =
114 Arrays.asList(
115 (PreferenceCategory)
116 settingsFragment.findPreference("notification_category"),
117 (PreferenceCategory) settingsFragment.findPreference("advanced"));
118 for (String key :
119 (runsTwentySix()
120 ? UNUSED_SETTINGS_POST_TWENTYSIX
121 : UNUSED_SETTINGS_PRE_TWENTYSIX)) {
122 Preference preference = settingsFragment.findPreference(key);
123 if (preference != null) {
124 for (PreferenceCategory category : categories) {
125 if (category != null) {
126 category.removePreference(preference);
127 }
128 }
129 }
130 }
131 if (Compatibility.runsTwentySix()) {
132 if (targetsTwentySix(settingsFragment.getContext())) {
133 Preference preference =
134 settingsFragment.findPreference(SettingsActivity.KEEP_FOREGROUND_SERVICE);
135 if (preference != null) {
136 for (PreferenceCategory category : categories) {
137 if (category != null) {
138 category.removePreference(preference);
139 }
140 }
141 }
142 }
143 }
144 }
145
146 public static void startService(Context context, Intent intent) {
147 try {
148 if (Compatibility.runsAndTargetsTwentySix(context)) {
149 intent.putExtra(EXTRA_NEEDS_FOREGROUND_SERVICE, true);
150 ContextCompat.startForegroundService(context, intent);
151 } else {
152 context.startService(intent);
153 }
154 } catch (RuntimeException e) {
155 Log.d(
156 Config.LOGTAG,
157 context.getClass().getSimpleName() + " was unable to start service");
158 }
159 }
160
161 @SuppressLint("UnsupportedChromeOsCameraSystemFeature")
162 public static boolean hasFeatureCamera(final Context context) {
163 final PackageManager packageManager = context.getPackageManager();
164 return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
165 }
166
167 @RequiresApi(api = Build.VERSION_CODES.N)
168 public static int getRestrictBackgroundStatus(
169 @NonNull final ConnectivityManager connectivityManager) {
170 try {
171 return connectivityManager.getRestrictBackgroundStatus();
172 } catch (final Exception e) {
173 Log.d(
174 Config.LOGTAG,
175 "platform bug detected. Unable to get restrict background status",
176 e);
177 return ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
178 }
179 }
180
181 @RequiresApi(api = Build.VERSION_CODES.N)
182 public static boolean isActiveNetworkMetered(
183 @NonNull final ConnectivityManager connectivityManager) {
184 try {
185 return connectivityManager.isActiveNetworkMetered();
186 } catch (final RuntimeException e) {
187 // when in doubt better assume it's metered
188 return true;
189 }
190 }
191
192 public static Bundle pgpStartIntentSenderOptions() {
193 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
194 return ActivityOptions.makeBasic()
195 .setPendingIntentBackgroundActivityStartMode(
196 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
197 .toBundle();
198 } else {
199 return null;
200 }
201 }
202}