1package eu.siacs.conversations.utils;
2
3import static eu.siacs.conversations.receiver.SystemEventReceiver.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.PreferenceManager;
16import android.util.Log;
17
18import androidx.annotation.BoolRes;
19import androidx.annotation.NonNull;
20import androidx.annotation.RequiresApi;
21import androidx.core.content.ContextCompat;
22
23import eu.siacs.conversations.AppSettings;
24import eu.siacs.conversations.Config;
25import eu.siacs.conversations.R;
26
27public class Compatibility {
28
29 public static boolean hasStoragePermission(final Context context) {
30 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU || ContextCompat.checkSelfPermission(
31 context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
32 }
33
34 public static boolean s() {
35 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
36 }
37
38 private static boolean runsTwentyFour() {
39 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
40 }
41
42 public static boolean runsTwentySix() {
43 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
44 }
45
46 public static boolean twentyEight() {
47 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
48 }
49
50 private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) {
51 return getPreferences(context).getBoolean(name, context.getResources().getBoolean(res));
52 }
53
54 private static SharedPreferences getPreferences(final Context context) {
55 return PreferenceManager.getDefaultSharedPreferences(context);
56 }
57
58 private static boolean targetsTwentySix(Context context) {
59 try {
60 final PackageManager packageManager = context.getPackageManager();
61 final ApplicationInfo applicationInfo =
62 packageManager.getApplicationInfo(context.getPackageName(), 0);
63 return applicationInfo.targetSdkVersion >= 26;
64 } catch (PackageManager.NameNotFoundException | RuntimeException e) {
65 return true; // when in doubt…
66 }
67 }
68
69 private static boolean targetsTwentyFour(Context context) {
70 try {
71 final PackageManager packageManager = context.getPackageManager();
72 final ApplicationInfo applicationInfo =
73 packageManager.getApplicationInfo(context.getPackageName(), 0);
74 return applicationInfo.targetSdkVersion >= 24;
75 } catch (PackageManager.NameNotFoundException | RuntimeException e) {
76 return true; // when in doubt…
77 }
78 }
79
80 public static boolean runsAndTargetsTwentySix(Context context) {
81 return runsTwentySix() && targetsTwentySix(context);
82 }
83
84 public static boolean runsAndTargetsTwentyFour(Context context) {
85 return runsTwentyFour() && targetsTwentyFour(context);
86 }
87
88 public static boolean keepForegroundService(Context context) {
89 return runsAndTargetsTwentySix(context)
90 || getBooleanPreference(
91 context,
92 AppSettings.KEEP_FOREGROUND_SERVICE,
93 R.bool.enable_foreground_service);
94 }
95
96 public static void startService(final Context context, final Intent intent) {
97 try {
98 if (Compatibility.runsAndTargetsTwentySix(context)) {
99 intent.putExtra(EXTRA_NEEDS_FOREGROUND_SERVICE, true);
100 context.startForegroundService(intent);
101 } else {
102 context.startService(intent);
103 }
104 } catch (final RuntimeException e) {
105 Log.d(
106 Config.LOGTAG,
107 context.getClass().getSimpleName() + " was unable to start service");
108 }
109 }
110
111 @SuppressLint("UnsupportedChromeOsCameraSystemFeature")
112 public static boolean hasFeatureCamera(final Context context) {
113 final PackageManager packageManager = context.getPackageManager();
114 return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
115 }
116
117 @RequiresApi(api = Build.VERSION_CODES.N)
118 public static int getRestrictBackgroundStatus(
119 @NonNull final ConnectivityManager connectivityManager) {
120 try {
121 return connectivityManager.getRestrictBackgroundStatus();
122 } catch (final Exception e) {
123 Log.d(
124 Config.LOGTAG,
125 "platform bug detected. Unable to get restrict background status",
126 e);
127 return ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
128 }
129 }
130
131 @RequiresApi(api = Build.VERSION_CODES.N)
132 public static boolean isActiveNetworkMetered(
133 @NonNull final ConnectivityManager connectivityManager) {
134 try {
135 return connectivityManager.isActiveNetworkMetered();
136 } catch (final RuntimeException e) {
137 // when in doubt better assume it's metered
138 return true;
139 }
140 }
141
142 public static Bundle pgpStartIntentSenderOptions() {
143 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
144 return ActivityOptions.makeBasic()
145 .setPendingIntentBackgroundActivityStartMode(
146 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
147 .toBundle();
148 } else {
149 return null;
150 }
151 }
152}