Omnilib: Import omni utils

Change-Id: Ic1a25aaa2b2be77ff0d9f03798e80ebca4a8f620
diff --git a/src/org/omnirom/omnilib/utils/DeviceKeyHandler.java b/src/org/omnirom/omnilib/utils/DeviceKeyHandler.java
new file mode 100644
index 0000000..699ea76
--- /dev/null
+++ b/src/org/omnirom/omnilib/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.omnilib.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/src/org/omnirom/omnilib/utils/DeviceUtils.java b/src/org/omnirom/omnilib/utils/DeviceUtils.java
new file mode 100644
index 0000000..5c1188c
--- /dev/null
+++ b/src/org/omnirom/omnilib/utils/DeviceUtils.java
@@ -0,0 +1,135 @@
+/*
+* 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.omnilib.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);
+    }
+}
diff --git a/src/org/omnirom/omnilib/utils/OmniServiceLocator.java b/src/org/omnirom/omnilib/utils/OmniServiceLocator.java
index 4348751..db1736d 100644
--- a/src/org/omnirom/omnilib/utils/OmniServiceLocator.java
+++ b/src/org/omnirom/omnilib/utils/OmniServiceLocator.java
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2020 The OmniROM Project
+ *  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
diff --git a/src/org/omnirom/omnilib/utils/OmniUtils.java b/src/org/omnirom/omnilib/utils/OmniUtils.java
new file mode 100644
index 0000000..a144532
--- /dev/null
+++ b/src/org/omnirom/omnilib/utils/OmniUtils.java
@@ -0,0 +1,161 @@
+/*
+* 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.omnilib.utils;
+
+import android.content.Context;
+import android.content.Intent;
+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.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 {
+    /**
+     * @hide
+     */
+    public static final String OMNILIB_PACKAGE_NAME = "org.omnirom.omnilib";
+
+    /**
+     * @hide
+     */
+    public static final String ACTION_DISMISS_KEYGUARD = OMNILIB_PACKAGE_NAME +".ACTION_DISMISS_KEYGUARD";
+
+    /**
+     * @hide
+     */
+    public static final String DISMISS_KEYGUARD_EXTRA_INTENT = "launch";
+
+    /**
+     * @hide
+     */
+    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);
+    }
+
+    /**
+     * @hide
+     */
+    public static void sendKeycode(int keycode) {
+        sendKeycode(keycode, false);
+    }
+
+    /**
+     * @hide
+     */
+    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;
+    }
+}
diff --git a/src/org/omnirom/omnilib/utils/OmniVibe.java b/src/org/omnirom/omnilib/utils/OmniVibe.java
index 9c89597..c57fe27 100644
--- a/src/org/omnirom/omnilib/utils/OmniVibe.java
+++ b/src/org/omnirom/omnilib/utils/OmniVibe.java
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2018 The OmniROM Project
+ *  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
@@ -26,7 +26,6 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.view.HapticFeedbackConstants;
-//import com.android.internal.util.omni.DeviceUtils;
 
 public class OmniVibe{
 
diff --git a/src/org/omnirom/omnilib/utils/PackageUtils.java b/src/org/omnirom/omnilib/utils/PackageUtils.java
new file mode 100644
index 0000000..fb586a3
--- /dev/null
+++ b/src/org/omnirom/omnilib/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.omnilib.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/src/org/omnirom/omnilib/utils/SystemKeyEventHandler.java b/src/org/omnirom/omnilib/utils/SystemKeyEventHandler.java
new file mode 100644
index 0000000..2ba2bc0
--- /dev/null
+++ b/src/org/omnirom/omnilib/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.omnilib.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();
+        }
+    }
+}