OmniLib: Move utils packages into private api and the rest, public
Change-Id: Ic9fa3ae9ae9c741b86bcb0fd7a52658d8af6dcbd
diff --git a/core/AndroidManifest.xml b/core/AndroidManifest.xml
new file mode 100644
index 0000000..5e3bcdd
--- /dev/null
+++ b/core/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2016 The OmniROM Project
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.omnirom.omnilibcore">
+
+</manifest>
diff --git a/core/org/omnirom/omnilibcore/utils/DeviceKeyHandler.java b/core/org/omnirom/omnilibcore/utils/DeviceKeyHandler.java
new file mode 100644
index 0000000..39313ab
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/DeviceKeyHandler.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod Project
+ * Copyright (C) 2015-2021 The OmniROM Project
+ *
+ * Licensed under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
+ * or agreed to in writing, software distributed under the License is
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+package org.omnirom.omnilibcore.utils;
+
+import android.content.Intent;
+import android.hardware.SensorEvent;
+import android.view.KeyEvent;
+
+public interface DeviceKeyHandler {
+
+ /**
+ * Invoked when an unknown key was detected by the system, letting the device handle
+ * this special keys prior to pass the key to the active app.
+ *
+ * @param event The key event to be handled
+ * @return If the event is consume
+ */
+ public boolean handleKeyEvent(KeyEvent event);
+
+ /**
+ * Invoked when an unknown key was detected by the system,
+ * this should NOT handle the key just return if it WOULD be handled
+ *
+ * @param event The key event to be handled
+ * @return If the event will be consumed
+ */
+ public boolean canHandleKeyEvent(KeyEvent event);
+
+ /**
+ * Special key event that should be treated as
+ * a camera launch event
+ *
+ * @param event The key event to be handled
+ * @return If the event is a camera launch event
+ */
+ public boolean isCameraLaunchEvent(KeyEvent event);
+
+ /**
+ * Special key event that should be treated as
+ * a wake event
+ *
+ * @param event The key event to be handled
+ * @return If the event is a wake event
+ */
+ public boolean isWakeEvent(KeyEvent event);
+
+ /**
+ * Return false if this event should be ignored
+ *
+ * @param event The key event to be handled
+ * @return If the event should be ignored
+ */
+ public boolean isDisabledKeyEvent(KeyEvent event);
+
+ /**
+ * Return an Intent that should be launched for that KeyEvent
+ *
+ * @param event The key event to be handled
+ * @return an Intent or null
+ */
+ public Intent isActivityLaunchEvent(KeyEvent event);
+
+ default public String getCustomProxiSensor() {
+ return null;
+ }
+
+ default public boolean getCustomProxiIsNear(SensorEvent event) {
+ return false;
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/DeviceUtils.java b/core/org/omnirom/omnilibcore/utils/DeviceUtils.java
new file mode 100644
index 0000000..9ae10a8
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/DeviceUtils.java
@@ -0,0 +1,141 @@
+/*
+* Copyright (C) 2014-2021 The OmniROM Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.omnirom.omnilibcore.utils;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraManager;
+import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
+import android.nfc.NfcAdapter;
+import android.os.SystemProperties;
+import android.os.Vibrator;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+import android.util.DisplayMetrics;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+import android.provider.Settings;
+
+import com.android.internal.telephony.PhoneConstants;
+import static android.hardware.Sensor.TYPE_LIGHT;
+import static android.hardware.Sensor.TYPE_PROXIMITY;
+
+import java.util.List;
+
+public class DeviceUtils {
+
+ // Device types
+ public static final int DEVICE_PHONE = 0;
+ public static final int DEVICE_HYBRID = 1;
+ public static final int DEVICE_TABLET = 2;
+
+ public static boolean deviceSupportsNfc(Context context) {
+ return (NfcAdapter.getDefaultAdapter(context) != null) ||
+ context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+
+ public static boolean deviceSupportsVibrator(Context ctx) {
+ Vibrator vibrator = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE);
+ return vibrator.hasVibrator();
+ }
+
+ public static boolean deviceSupportsProximitySensor(Context context) {
+ SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ return sm.getDefaultSensor(TYPE_PROXIMITY) != null;
+ }
+
+ public static boolean deviceSupportsLightSensor(Context context) {
+ SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ return sm.getDefaultSensor(TYPE_LIGHT) != null;
+ }
+
+ public static boolean deviceSupportNavigationBar(Context context) {
+ return deviceSupportNavigationBarForUser(context, UserHandle.USER_CURRENT);
+ }
+
+ public static boolean deviceSupportNavigationBarForUser(Context context, int userId) {
+ final boolean showByDefault = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_showNavigationBar);
+ /*final int hasNavigationBar = Settings.System.getIntForUser(
+ context.getContentResolver(),
+ Settings.System.OMNI_NAVIGATION_BAR_SHOW, -1, userId);*/
+ final int hasNavigationBar = -1;
+ if (hasNavigationBar == -1) {
+ String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
+ if ("1".equals(navBarOverride)) {
+ return false;
+ } else if ("0".equals(navBarOverride)) {
+ return true;
+ } else {
+ return showByDefault;
+ }
+ } else {
+ return hasNavigationBar == 1;
+ }
+ }
+
+ private static int getScreenType(Context con) {
+ WindowManager wm = (WindowManager)con.getSystemService(Context.WINDOW_SERVICE);
+ DisplayInfo outDisplayInfo = new DisplayInfo();
+ wm.getDefaultDisplay().getDisplayInfo(outDisplayInfo);
+ int shortSize = Math.min(outDisplayInfo.logicalHeight, outDisplayInfo.logicalWidth);
+ int shortSizeDp =
+ shortSize * DisplayMetrics.DENSITY_DEFAULT / outDisplayInfo.logicalDensityDpi;
+ if (shortSizeDp < 600) {
+ return DEVICE_PHONE;
+ } else if (shortSizeDp < 720) {
+ return DEVICE_HYBRID;
+ } else {
+ return DEVICE_TABLET;
+ }
+ }
+
+ public static boolean isPhone(Context con) {
+ return getScreenType(con) == DEVICE_PHONE;
+ }
+
+ public static boolean isHybrid(Context con) {
+ return getScreenType(con) == DEVICE_HYBRID;
+ }
+
+ public static boolean isTablet(Context con) {
+ return getScreenType(con) == DEVICE_TABLET;
+ }
+
+ public static boolean isLandscapePhone(Context context) {
+ Configuration config = context.getResources().getConfiguration();
+ return config.orientation == Configuration.ORIENTATION_LANDSCAPE
+ && config.smallestScreenWidthDp < 600;
+ }
+
+ public static boolean isDataEncrypted() {
+ String voldState = SystemProperties.get("vold.decrypt");
+ return "1".equals(voldState) || "trigger_restart_min_framework".equals(voldState);
+ }
+
+ public static boolean isVoiceCapable(Context context) {
+ final TelephonyManager telephony =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephony != null && telephony.isVoiceCapable();
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/OmniServiceLocator.java b/core/org/omnirom/omnilibcore/utils/OmniServiceLocator.java
new file mode 100644
index 0000000..5703910
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/OmniServiceLocator.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2021 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.omnirom.omnilibcore.utils;
+
+import android.content.Context;
+import android.net.Uri;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.webkit.URLUtil;
+
+public class OmniServiceLocator {
+
+ private static String getWalllpaperBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "wallpaper_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://dl.omnirom.org/";
+ }
+ return s;
+ }
+
+ private static String getWalllpaperRootUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "wallpaper_root_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "images/wallpapers/";
+ }
+ return s;
+ }
+
+ private static String getWalllpaperQueryUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "wallpaper_query_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "images/wallpapers/thumbs/json_wallpapers_xml.php";
+ }
+ return s;
+ }
+
+ private static String getHeaderBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "header_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://dl.omnirom.org/";
+ }
+ return s;
+ }
+
+ private static String getHeaderRootUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "header_root_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "images/headers/";
+ }
+ return s;
+ }
+
+ private static String getHeaderQueryUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "header_query_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "images/headers/thumbs/json_headers_xml.php";
+ }
+ return s;
+ }
+
+ // OmniStore is external apk and dont use this so its just FYI
+ private static String getStoreBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "store_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://dl.omnirom.org/";
+ }
+ return s;
+ }
+
+ private static String getStoreRootUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "store_root_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "store/";
+ }
+ return s;
+ }
+
+ private static String getStoreQuertUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "store_query_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "store/apps.json";
+ }
+ return s;
+ }
+
+ private static String getBuildsBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://dl.omnirom.org/";
+ }
+ return s;
+ }
+
+ private static String getBuildsRootUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_root_uri");
+ if (TextUtils.isEmpty(s)) {
+ return null;
+ }
+ return s;
+ }
+
+ private static String getBuildsQueryUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_query_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "json.php";
+ }
+ return s;
+ }
+
+ private static String getBuildsSecondaryBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_secondary_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://dl.omnirom.org/tmp/";
+ }
+ return s;
+ }
+
+ private static String getBuildsDeltaBaseUrl(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_delta_base_url");
+ if (TextUtils.isEmpty(s)) {
+ return "https://delta.omnirom.org/";
+ }
+ return s;
+ }
+
+ private static String getBuildsDeltaRootUri(Context context) {
+ String s = Settings.System.getString(context.getContentResolver(), "builds_delta_root_uri");
+ if (TextUtils.isEmpty(s)) {
+ return "weeklies/";
+ }
+ return s;
+ }
+
+ public static String buildWalllpaperQueryUrl(Context context) {
+ String queryUri = getWalllpaperQueryUri(context);
+ if (URLUtil.isNetworkUrl(queryUri)) {
+ return queryUri;
+ }
+ Uri base = Uri.parse(getWalllpaperBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, queryUri);
+ return u.toString();
+ }
+
+ public static String buildWalllpaperRootUrl(Context context) {
+ String rootUri = getWalllpaperRootUri(context);
+ if (TextUtils.isEmpty(rootUri)) {
+ return getWalllpaperBaseUrl(context);
+ }
+ if (URLUtil.isNetworkUrl(rootUri)) {
+ return rootUri;
+ }
+ Uri base = Uri.parse(getWalllpaperBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, rootUri);
+ return u.toString();
+ }
+
+ public static String buildHeaderQueryUrl(Context context) {
+ String queryUri = getHeaderQueryUri(context);
+ if (URLUtil.isNetworkUrl(queryUri)) {
+ return queryUri;
+ }
+ Uri base = Uri.parse(getHeaderBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, queryUri);
+ return u.toString();
+ }
+
+ public static String buildHeaderRootUrl(Context context) {
+ String rootUri = getHeaderRootUri(context);
+ if (TextUtils.isEmpty(rootUri)) {
+ return getHeaderBaseUrl(context);
+ }
+ if (URLUtil.isNetworkUrl(rootUri)) {
+ return rootUri;
+ }
+ Uri base = Uri.parse(getHeaderBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, rootUri);
+ return u.toString();
+ }
+
+ public static String buildBuildsQueryUrl(Context context, boolean secondary) {
+ String queryUri = getBuildsQueryUri(context);
+ if (URLUtil.isNetworkUrl(queryUri)) {
+ return queryUri;
+ }
+ Uri base = Uri.parse(secondary ? getBuildsSecondaryBaseUrl(context) : getBuildsBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, queryUri);
+ return u.toString();
+ }
+
+ public static String buildBuildsRootUrl(Context context, boolean secondary) {
+ String rootUri = getBuildsRootUri(context);
+ if (TextUtils.isEmpty(rootUri)) {
+ return secondary ? getBuildsSecondaryBaseUrl(context) : getBuildsBaseUrl(context);
+ }
+ if (URLUtil.isNetworkUrl(rootUri)) {
+ return rootUri;
+ }
+ Uri base = Uri.parse(secondary ? getBuildsSecondaryBaseUrl(context) : getBuildsBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, rootUri);
+ return u.toString();
+ }
+
+ public static String buildBuildsDeltasRootUrl(Context context) {
+ String rootUri = getBuildsDeltaRootUri(context);
+ if (TextUtils.isEmpty(rootUri)) {
+ return getBuildsDeltaBaseUrl(context);
+ }
+ if (URLUtil.isNetworkUrl(rootUri)) {
+ return rootUri;
+ }
+ Uri base = Uri.parse(getBuildsDeltaBaseUrl(context));
+ Uri u = Uri.withAppendedPath(base, rootUri);
+ return u.toString();
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/OmniUtils.java b/core/org/omnirom/omnilibcore/utils/OmniUtils.java
new file mode 100644
index 0000000..bac06cc
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/OmniUtils.java
@@ -0,0 +1,179 @@
+/*
+* Copyright (C) 2017-2021 The OmniROM Project
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+package org.omnirom.omnilibcore.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.UserHandle;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.RectF;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+public class OmniUtils {
+
+ public static final String OMNILIB_PACKAGE_NAME = "org.omnirom.omnilib";
+
+ public static final String ACTION_DISMISS_KEYGUARD = OMNILIB_PACKAGE_NAME +".ACTION_DISMISS_KEYGUARD";
+
+ public static final String DISMISS_KEYGUARD_EXTRA_INTENT = "launch";
+
+ public static void launchKeyguardDismissIntent(Context context, UserHandle user, Intent launchIntent) {
+ Intent keyguardIntent = new Intent(ACTION_DISMISS_KEYGUARD);
+ keyguardIntent.setPackage(OMNILIB_PACKAGE_NAME);
+ keyguardIntent.putExtra(DISMISS_KEYGUARD_EXTRA_INTENT, launchIntent);
+ context.sendBroadcastAsUser(keyguardIntent, user);
+ }
+
+ public static void sendKeycode(int keycode) {
+ sendKeycode(keycode, false);
+ }
+
+ public static void sendKeycode(int keycode, boolean longpress) {
+ long when = SystemClock.uptimeMillis();
+ final KeyEvent evDown = new KeyEvent(when, when, KeyEvent.ACTION_DOWN, keycode, 0,
+ 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_FROM_SYSTEM,
+ InputDevice.SOURCE_KEYBOARD);
+ final KeyEvent evUp = KeyEvent.changeAction(evDown, KeyEvent.ACTION_UP);
+
+ final Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ InputManager.getInstance().injectInputEvent(evDown,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
+ });
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ InputManager.getInstance().injectInputEvent(evUp,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
+ }, longpress ? 750 : 20);
+ }
+
+ public static void goToSleep(Context context) {
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ if(pm != null) {
+ pm.goToSleep(SystemClock.uptimeMillis());
+ }
+ }
+
+ /* e.g.
+ <integer-array name="config_defaultNotificationVibePattern">
+ <item>0</item>
+ <item>350</item>
+ <item>250</item>
+ <item>350</item>
+ </integer-array>
+ */
+ public static void vibrateResourcePattern(Context context, int resId) {
+ if (DeviceUtils.deviceSupportsVibrator(context)) {
+ int[] pattern = context.getResources().getIntArray(resId);
+ if (pattern == null) {
+ return;
+ }
+ long[] out = new long[pattern.length];
+ for (int i=0; i<pattern.length; i++) {
+ out[i] = pattern[i];
+ }
+ ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(out, -1);
+ }
+ }
+
+ public static void vibratePattern(Context context, long[] pattern) {
+ if (DeviceUtils.deviceSupportsVibrator(context)) {
+ ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(pattern, -1);
+ }
+ }
+
+ public static Bitmap scaleCenterInside(final Bitmap source, final int newWidth, final int newHeight) {
+ int sourceWidth = source.getWidth();
+ int sourceHeight = source.getHeight();
+
+ float widthRatio = (float) newWidth / sourceWidth;
+ float heightRatio = (float) newHeight / sourceHeight;
+ float ratio = Math.max(widthRatio, heightRatio);
+ float scaledWidth = sourceWidth * ratio;
+ float scaledHeight = sourceHeight * ratio;
+
+ //Bitmap scaled = Bitmap.createScaledBitmap(source, (int)scaledWidth, (int)scaledHeight, true);
+
+ RectF targetRect = null;
+ if (newWidth > newHeight) {
+ float inset = (scaledHeight - newHeight) / 2;
+ targetRect = new RectF(0, -inset, newWidth, newHeight + inset);
+ } else {
+ float inset = (scaledWidth - newWidth) / 2;
+ targetRect = new RectF(-inset, 0, newWidth + inset, newHeight);
+ }
+ Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
+ Canvas canvas = new Canvas(dest);
+ canvas.drawBitmap(source, null, targetRect, null);
+ return dest;
+ }
+
+ public static int getQSColumnsCount(Context context, int resourceCount) {
+ final int QS_COLUMNS_MIN = 2;
+ final Resources res = context.getResources();
+ int value = QS_COLUMNS_MIN;
+ if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ value = Settings.System.getIntForUser(
+ context.getContentResolver(), "qs_layout_columns",
+ resourceCount, UserHandle.USER_CURRENT);
+ } else {
+ value = Settings.System.getIntForUser(
+ context.getContentResolver(), "qs_layout_columns_landscape",
+ resourceCount, UserHandle.USER_CURRENT);
+ }
+ return Math.max(QS_COLUMNS_MIN, value);
+ }
+
+ public static int getQuickQSColumnsCount(Context context, int resourceCount) {
+ return getQSColumnsCount(context, resourceCount);
+ }
+
+ public static boolean getQSTileLabelHide(Context context) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.OMNI_QS_TILE_LABEL_HIDE,
+ 0, UserHandle.USER_CURRENT) != 0;
+ }
+
+ public static boolean getQSTileVerticalLayout(Context context, int defaultValue) {
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.OMNI_QS_TILE_VERTICAL_LAYOUT,
+ defaultValue, UserHandle.USER_CURRENT) != 0;
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/OmniVibe.java b/core/org/omnirom/omnilibcore/utils/OmniVibe.java
new file mode 100644
index 0000000..387e933
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/OmniVibe.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.omnirom.omnilibcore.utils;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.view.HapticFeedbackConstants;
+
+public class OmniVibe{
+
+
+ // Vibrator pattern for haptic feedback of a long press.
+ private static long[] mLongPressVibePattern;
+
+ // Vibrator pattern for a short vibration when tapping on a day/month/year date of a Calendar.
+ private static long[] mCalendarDateVibePattern;
+
+ // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
+ private static long[] mSafeModeEnabledVibePattern;
+
+ public static void OmniVibe(){
+ }
+
+ public static boolean performHapticFeedbackLw(int effectId, boolean always, Context mContext) {
+ final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
+ if (hapticsDisabled && !always) {
+ return false;
+ }
+
+ VibrationEffect effect = getVibrationEffect(effectId);
+ if (effect == null) {
+ return false;
+ }
+
+ Vibrator mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ if (mVibrator.hasVibrator()){
+ mVibrator.vibrate(VibrationEffect.createOneShot(50,
+ VibrationEffect.DEFAULT_AMPLITUDE));
+ }
+ return true;
+ }
+
+ private static VibrationEffect getVibrationEffect(int effectId) {
+ long[] pattern;
+ switch (effectId) {
+ case HapticFeedbackConstants.CLOCK_TICK:
+ case HapticFeedbackConstants.CONTEXT_CLICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+ case HapticFeedbackConstants.ENTRY_BUMP:
+ case HapticFeedbackConstants.DRAG_CROSSING:
+ case HapticFeedbackConstants.GESTURE_END:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ case HapticFeedbackConstants.VIRTUAL_KEY:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.GESTURE_START:
+ return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+ case HapticFeedbackConstants.REJECT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+ case HapticFeedbackConstants.CALENDAR_DATE:
+ pattern = mCalendarDateVibePattern;
+ break;
+ case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+ pattern = mSafeModeEnabledVibePattern;
+ break;
+
+ default:
+ return null;
+ }
+ if (pattern.length == 0) {
+ // No vibration
+ return null;
+ } else if (pattern.length == 1) {
+ // One-shot vibration
+ return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
+ } else {
+ // Pattern vibration
+ return VibrationEffect.createWaveform(pattern, -1);
+ }
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/PackageUtils.java b/core/org/omnirom/omnilibcore/utils/PackageUtils.java
new file mode 100644
index 0000000..639dc1d
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/PackageUtils.java
@@ -0,0 +1,46 @@
+/*
+* Copyright (C) 2014-2021 The OmniROM Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.omnirom.omnilibcore.utils;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+public class PackageUtils {
+
+ public static boolean isAppInstalled(Context context, String appUri) {
+ try {
+ PackageManager pm = context.getPackageManager();
+ pm.getPackageInfo(appUri, PackageManager.GET_ACTIVITIES);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ public static boolean isAvailableApp(String packageName, Context context) {
+ Context mContext = context;
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
+ int enabled = pm.getApplicationEnabledSetting(packageName);
+ return enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED &&
+ enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/SystemKeyEventHandler.java b/core/org/omnirom/omnilibcore/utils/SystemKeyEventHandler.java
new file mode 100644
index 0000000..71c2a2e
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/SystemKeyEventHandler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package org.omnirom.omnilibcore.utils;
+
+import android.os.Handler;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+public class SystemKeyEventHandler {
+ private static final String TAG = "SystemKeyEventHandler";
+ private static final boolean DEBUG = false;
+ private boolean mDoubleTapPending;
+ private boolean mKeyPressed;
+ private boolean mKeyConsumed;
+ private Runnable mPressedAction;
+ private Runnable mDoubleTapAction;
+ private Runnable mLongPressAction;
+
+ private final Runnable mDoubleTapTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mDoubleTapPending) {
+ mDoubleTapPending = false;
+ if (mPressedAction != null) {
+ mPressedAction.run();
+ }
+ }
+ }
+ };
+
+ public SystemKeyEventHandler(Runnable pressedAction, Runnable doubleTapAction,
+ Runnable longPressAction) {
+ mPressedAction = pressedAction;
+ mDoubleTapAction = doubleTapAction;
+ mLongPressAction = longPressAction;
+ }
+
+ public int handleKeyEvent(Handler handler, KeyEvent event) {
+ final int repeatCount = event.getRepeatCount();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final boolean canceled = event.isCanceled();
+
+ if (DEBUG) {
+ Log.d(TAG, "handleKeyEvent " + event + " down = " + down + " repeatCount = " + repeatCount);
+ }
+ // If we have released the home key, and didn't do anything else
+ // while it was pressed, then it is time to go home!
+ if (!down) {
+ mKeyPressed = false;
+ if (mKeyConsumed) {
+ mKeyConsumed = false;
+ return -1;
+ }
+
+ if (canceled) {
+ return -1;
+ }
+
+ // Delay handling home if a double-tap is possible.
+ if (mDoubleTapAction != null) {
+ handler.removeCallbacks(mDoubleTapTimeoutRunnable); // just in case
+ mDoubleTapPending = true;
+ handler.postDelayed(mDoubleTapTimeoutRunnable,
+ ViewConfiguration.getDoubleTapTimeout());
+ return -1;
+ }
+
+ if (mPressedAction != null) {
+ // Post to main thread to avoid blocking input pipeline.
+ handler.post(() -> {
+ mPressedAction.run();
+ });
+ }
+ return -1;
+ }
+
+
+ if (repeatCount == 0) {
+ mKeyPressed = true;
+ if (mDoubleTapPending) {
+ mDoubleTapPending = false;
+ handler.removeCallbacks(mDoubleTapTimeoutRunnable);
+ handleDoubleTapOnKey();
+ }
+ } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+ if (mLongPressAction != null) {
+ // Post to main thread to avoid blocking input pipeline.
+ handler.post(() -> {
+ mLongPressAction.run();
+ });
+ }
+ }
+ return -1;
+ }
+
+ private void handleDoubleTapOnKey() {
+ mKeyConsumed = true;
+ if (mDoubleTapAction != null) {
+ mDoubleTapAction.run();
+ }
+ }
+}
diff --git a/core/org/omnirom/omnilibcore/utils/TaskUtils.java b/core/org/omnirom/omnilibcore/utils/TaskUtils.java
new file mode 100644
index 0000000..aa57b44
--- /dev/null
+++ b/core/org/omnirom/omnilibcore/utils/TaskUtils.java
@@ -0,0 +1,102 @@
+/*
+* Copyright (C) 2014-2022 The OmniROM Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.omnirom.omnilibcore.utils;
+
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import org.omnirom.omnilib.R;
+
+import java.util.List;
+
+public class TaskUtils {
+
+ public static void toggleLastApp(Context context, int userId) {
+ final ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(context.getPackageManager(), 0);
+ final ActivityManager am = (ActivityManager) context
+ .getSystemService(Activity.ACTIVITY_SERVICE);
+ final List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasks(8,
+ ActivityManager.RECENT_IGNORE_UNAVAILABLE |
+ ActivityManager.RECENT_WITH_EXCLUDED);
+
+ int lastAppId = 0;
+ for (int i = 1; i < tasks.size(); i++) {
+ final ActivityManager.RecentTaskInfo info = tasks.get(i);
+ boolean isExcluded = (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+ if (isExcluded) {
+ continue;
+ }
+ if (isCurrentHomeActivity(info.baseIntent.getComponent(), homeInfo)) {
+ continue;
+ }
+ lastAppId = info.persistentId;
+ break;
+ }
+ if (lastAppId > 0) {
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(context,
+ R.anim.last_app_in, R.anim.last_app_out);
+ try {
+ ActivityManagerNative.getDefault().startActivityFromRecents(
+ lastAppId, options.toBundle());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ private static boolean isCurrentHomeActivity(ComponentName component,
+ ActivityInfo homeInfo) {
+ return homeInfo != null
+ && homeInfo.packageName.equals(component.getPackageName())
+ && homeInfo.name.equals(component.getClassName());
+ }
+
+ private static int getRunningTask(Context context) {
+ final ActivityManager am = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+ if (tasks != null && !tasks.isEmpty()) {
+ return tasks.get(0).id;
+ }
+ return -1;
+ }
+
+ public static ActivityInfo getRunningActivityInfo(Context context) {
+ final ActivityManager am = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ final PackageManager pm = context.getPackageManager();
+
+ List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
+ if (tasks != null && !tasks.isEmpty()) {
+ ActivityManager.RunningTaskInfo top = tasks.get(0);
+ try {
+ return pm.getActivityInfo(top.topActivity, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+ return null;
+ }
+}