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
97 public static void startService(final Context context, final Intent intent) {
98 try {
99 if (Compatibility.runsAndTargetsTwentySix(context)) {
100 intent.putExtra(EXTRA_NEEDS_FOREGROUND_SERVICE, true);
101 ContextCompat.startForegroundService(context, intent);
102 } else {
103 context.startService(intent);
104 }
105 } catch (final RuntimeException e) {
106 Log.d(
107 Config.LOGTAG,
108 context.getClass().getSimpleName() + " was unable to start service");
109 }
110 }
111
112 @SuppressLint("UnsupportedChromeOsCameraSystemFeature")
113 public static boolean hasFeatureCamera(final Context context) {
114 final PackageManager packageManager = context.getPackageManager();
115 return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
116 }
117
118 @RequiresApi(api = Build.VERSION_CODES.N)
119 public static int getRestrictBackgroundStatus(
120 @NonNull final ConnectivityManager connectivityManager) {
121 try {
122 return connectivityManager.getRestrictBackgroundStatus();
123 } catch (final Exception e) {
124 Log.d(
125 Config.LOGTAG,
126 "platform bug detected. Unable to get restrict background status",
127 e);
128 return ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
129 }
130 }
131
132 @RequiresApi(api = Build.VERSION_CODES.N)
133 public static boolean isActiveNetworkMetered(
134 @NonNull final ConnectivityManager connectivityManager) {
135 try {
136 return connectivityManager.isActiveNetworkMetered();
137 } catch (final RuntimeException e) {
138 // when in doubt better assume it's metered
139 return true;
140 }
141 }
142
143 public static Bundle pgpStartIntentSenderOptions() {
144 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
145 return ActivityOptions.makeBasic()
146 .setPendingIntentBackgroundActivityStartMode(
147 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
148 .toBundle();
149 } else {
150 return null;
151 }
152 }
153}