diff options
| author | qjohn <townex22@gmail.com> | 2018-06-29 04:26:43 +0700 |
|---|---|---|
| committer | qjohn <townex22@gmail.com> | 2018-06-29 04:26:43 +0700 |
| commit | cbe68b2b01b100c2760f4d2aaad55dd876b53b51 (patch) | |
| tree | 8de97822f2bcd8257a313e728176d33f5419bdea | |
| parent | ca0bc0ae505db0576d208ab5652129e3e236e888 (diff) | |
| parent | 5adcc23120fdb80e5b7e91cb6bb5e0d79a8f4db3 (diff) | |
Merge branch 'o8.1' of https://github.com/AICP/external_DUtils into o8.1
| -rw-r--r-- | Android.mk | 27 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/ActionConstants.java | 649 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/ActionHandler.java | 1169 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/ActionHolder.java | 43 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/Config.java | 563 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/DUActionUtils.java | 759 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/DUPackageMonitor.java | 126 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/DUSystemReceiver.java | 68 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/ImageHelper.java | 299 | ||||
| -rw-r--r-- | src/com/android/internal/utils/du/UserContentObserver.java | 92 |
10 files changed, 3778 insertions, 17 deletions
@@ -1,18 +1,11 @@ -#LOCAL_PATH:= $(call my-dir) -# -#include $(CLEAR_VARS) -# -#LOCAL_JAVA_LIBRARIES := org.dirtyunicorns.utils -# -#LOCAL_STATIC_JAVA_LIBRARIES := \ -# trail-drawing \ -# rebound -# -#LOCAL_SRC_FILES := $(call all-java-files-under, src) -# -#LOCAL_MODULE_TAGS := optional -# -#LOCAL_MODULE := org.dirtyunicorns.navigation-static-internal -# -#include $(BUILD_STATIC_JAVA_LIBRARY) +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE := org.dirtyunicorns.utils + +include $(BUILD_JAVA_LIBRARY) diff --git a/src/com/android/internal/utils/du/ActionConstants.java b/src/com/android/internal/utils/du/ActionConstants.java new file mode 100644 index 0000000..34c58bd --- /dev/null +++ b/src/com/android/internal/utils/du/ActionConstants.java @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * ActionConstants.java: A helper class to assist Config.java with loading + * and assigning default feature configurations. Nested classes implement + * the static interface Defaults, which allows Settings and Config.java + * to handle configurations in a non-implementation specific way, allowing + * for more generalized code structures. + * + * Of strong importance is the ConfigMap pojo class. Current settings use + * a ActionPreference which sets a single action. Therefore, we must have a + * way to map individual actions to their associated buttons. ActionPreference + * key MUST match the tag associated with the target ConfigMap. + * + */ + +package com.android.internal.utils.du; + +import java.util.HashMap; +import java.util.Map; + +import com.android.internal.utils.du.ActionHandler.SystemAction; +import com.android.internal.utils.du.Config.ActionConfig; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Bundle; +import android.provider.Settings; +import android.util.Log; +import android.util.TypedValue; + +public class ActionConstants { + public static interface Defaults { + public int getConfigType(); + public String getUri(); + public String getDefaultConfig(); + public int getMaxButtons(); + public Map<String, ConfigMap> getActionMap(); + public Bundle getConfigs(Context context); + } + + public static final String ACTION_DELIMITER = "|"; + public static final String EMPTY = "empty"; + public static final int SMARTBAR = 1; + public static final int HWKEYS = 2; + public static final int FLING = 3; + public static final int PIE_PRIMARY = 4; + public static final int PIE_SECONDARY = 5; + + private static final Smartbar smartbar = new Smartbar(); + private static final Hwkeys hwkeys = new Hwkeys(); + private static final Fling fling = new Fling(); + private static final PiePrimary pie_primary = new PiePrimary(); + private static final PieSecond pie_second = new PieSecond(); + + public static Defaults getDefaults(int type) { + if (type == SMARTBAR) { + return smartbar; + } else if (type == HWKEYS) { + return hwkeys; + } else if (type == FLING) { + return fling; + } else if (type == PIE_PRIMARY){ + return pie_primary; + } else if (type == PIE_SECONDARY) { + return pie_second; + } else { + return null; + } + } + + public static String dl(String s) { + return s + ACTION_DELIMITER; + } + + public static class Smartbar implements Defaults { + public static final int SMARTBAR_MAX_BUTTONS = 10; + public static final String SMARTBAR_DEF_BUTTONS = "3"; + public static final String BUTTON1_TAG = "smartbar_button_1"; + public static final String BUTTON2_TAG = "smartbar_button_2"; + public static final String BUTTON3_TAG = "smartbar_button_3"; + public static final String BUTTON4_TAG = "smartbar_button_4"; + public static final String BUTTON5_TAG = "smartbar_button_5"; + public static final String BUTTON6_TAG = "smartbar_button_6"; + public static final String BUTTON7_TAG = "smartbar_button_7"; + + public static final String SMARTBAR_CONFIG_DEFAULT = + dl(SMARTBAR_DEF_BUTTONS) // default number of ButtonConfig + + dl(BUTTON1_TAG) // button tag + + dl(SystemAction.Back.mAction) + dl(SystemAction.Back.mLabelRes) + dl(EMPTY) // single tap (PRIMARY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // long press (SECOND) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // double tap (THIRD) + + + dl(BUTTON2_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) + + dl(SystemAction.GoogleNowOnTap.mAction) + dl(SystemAction.GoogleNowOnTap.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(BUTTON3_TAG) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) + + dl(SystemAction.SplitScreen.mAction) + dl(SystemAction.SplitScreen.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; +/* + + dl(BUTTON4_TAG) + + dl(SystemAction.ExpandedDesktop.mAction) + dl(SystemAction.ExpandedDesktop.mLabelRes) + dl(EMPTY) + + dl(SystemAction.Flashlight.mAction) + dl(SystemAction.Flashlight.mLabelRes) + dl(EMPTY) + + dl(SystemAction.PowerMenu.mAction) + dl(SystemAction.PowerMenu.mLabelRes) + dl(EMPTY) + + + dl(BUTTON5_TAG) + + dl(SystemAction.Screenrecord.mAction) + dl(SystemAction.Screenrecord.mLabelRes) + dl(EMPTY) + + dl(SystemAction.KillApp.mAction) + dl(SystemAction.KillApp.mLabelRes) + dl(EMPTY) + + dl(SystemAction.ScreenOff.mAction) + dl(SystemAction.ScreenOff.mLabelRes) + EMPTY; +*/ + @Override + public String getUri() { + return "smartbar_button_config"; + } + + @Override + public String getDefaultConfig() { + return SMARTBAR_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return SMARTBAR_MAX_BUTTONS; + } + + @Override + public int getConfigType() { + return SMARTBAR; + } + + @Override + public Map<String, ConfigMap> getActionMap() { + return null; + } + + @Override + public Bundle getConfigs(Context context) { + // TODO Auto-generated method stub + return null; + } + } + + public static class Hwkeys implements Defaults { + public static final int HWKEY_MAX_BUTTONS = 7; + public static final String HWKEY_DEF_BUTTONS = "5"; + public static final String BACK_BUTTON_TAG = "hwkeys_button_back"; + public static final String HOME_BUTTON_TAG = "hwkeys_button_home"; + public static final String OVERVIEW_BUTTON_TAG = "hwkeys_button_overview"; + public static final String MENU_BUTTON_TAG = "hwkeys_button_menu"; + public static final String ASSIST_BUTTON_TAG = "hwkeys_button_assist"; + public static final String EXTRA1_BUTTON_TAG = "hwkeys_button_camera"; + public static final String EXTRA2_BUTTON_TAG = "hwkeys_button_extra"; + + public static final String BACK_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_back_single_tap"; + public static final String HOME_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_home_single_tap"; + public static final String OVERVIEW_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_overview_single_tap"; + public static final String MENU_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_menu_single_tap"; + public static final String ASSIST_BUTTON_SINGLE_TAP_TAG = "hwkeys_button_assist_single_tap"; + + public static final String BACK_BUTTON_LONG_PRESS_TAG = "hwkeys_button_back_long_press"; + public static final String HOME_BUTTON_LONG_PRESS_TAG = "hwkeys_button_home_long_press"; + public static final String OVERVIEW_BUTTON_LONG_PRESS_TAG = "hwkeys_button_overview_long_press"; + public static final String MENU_BUTTON_LONG_PRESS_TAG = "hwkeys_button_menu_long_press"; + public static final String ASSIST_BUTTON_LONG_PRESS_TAG = "hwkeys_button_assist_long_press"; + + public static final String BACK_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_back_double_tap"; + public static final String HOME_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_home_double_tap"; + public static final String OVERVIEW_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_overview_double_tap"; + public static final String MENU_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_menu_double_tap"; + public static final String ASSIST_BUTTON_DOUBLE_TAP_TAG = "hwkeys_button_assist_double_tap"; + + private static final Map<String, ConfigMap> configMap = new HashMap<String, ConfigMap>(); + + static { + configMap.put(BACK_BUTTON_SINGLE_TAP_TAG, new ConfigMap(0, ActionConfig.PRIMARY)); + configMap.put(HOME_BUTTON_SINGLE_TAP_TAG, new ConfigMap(1, ActionConfig.PRIMARY)); + configMap.put(OVERVIEW_BUTTON_SINGLE_TAP_TAG, new ConfigMap(2, ActionConfig.PRIMARY)); + configMap.put(MENU_BUTTON_SINGLE_TAP_TAG, new ConfigMap(3, ActionConfig.PRIMARY)); + configMap.put(ASSIST_BUTTON_SINGLE_TAP_TAG, new ConfigMap(4, ActionConfig.PRIMARY)); + configMap.put(BACK_BUTTON_LONG_PRESS_TAG, new ConfigMap(0, ActionConfig.SECOND)); + configMap.put(HOME_BUTTON_LONG_PRESS_TAG, new ConfigMap(1, ActionConfig.SECOND)); + configMap.put(OVERVIEW_BUTTON_LONG_PRESS_TAG, new ConfigMap(2, ActionConfig.SECOND)); + configMap.put(MENU_BUTTON_LONG_PRESS_TAG, new ConfigMap(3, ActionConfig.SECOND)); + configMap.put(ASSIST_BUTTON_LONG_PRESS_TAG, new ConfigMap(4, ActionConfig.SECOND)); + configMap.put(BACK_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(0, ActionConfig.THIRD)); + configMap.put(HOME_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(1, ActionConfig.THIRD)); + configMap.put(OVERVIEW_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(2, ActionConfig.THIRD)); + configMap.put(MENU_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(3, ActionConfig.THIRD)); + configMap.put(ASSIST_BUTTON_DOUBLE_TAP_TAG, new ConfigMap(4, ActionConfig.THIRD)); + } + + public static final String HWKEYS_CONFIG_DEFAULT = + dl(HWKEY_DEF_BUTTONS) + + dl(BACK_BUTTON_TAG) + + dl(SystemAction.Back.mAction) + dl(SystemAction.Back.mLabelRes) + dl(EMPTY) // single tap (PRIMARY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // long press (SECOND) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // double tap (THIRD) + + + dl(HOME_BUTTON_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) + + dl(SystemAction.Assistant.mAction) + dl(SystemAction.Assistant.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(OVERVIEW_BUTTON_TAG) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(MENU_BUTTON_TAG) + + dl(SystemAction.Menu.mAction) + dl(SystemAction.Menu.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(ASSIST_BUTTON_TAG) + + dl(SystemAction.Assistant.mAction) + dl(SystemAction.Assistant.mLabelRes) + dl(EMPTY) + + dl(SystemAction.VoiceSearch.mAction) + dl(SystemAction.VoiceSearch.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; + + @Override + public int getConfigType() { + return HWKEYS; + } + + @Override + public String getUri() { + //return Settings.System.HWKEY_BUTTON_ACTIONS; + return "hwkey_config"; + } + + @Override + public String getDefaultConfig() { + return HWKEYS_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return HWKEY_MAX_BUTTONS; + } + + @Override + public Map<String, ConfigMap> getActionMap() { + return configMap; + } + + @Override + public Bundle getConfigs(Context context) { + // TODO Auto-generated method stub + return null; + } + } + + public static class Fling implements Defaults { + public static final int FLING_MAX_BUTTONS = 10; + public static final String FLING_DEF_BUTTONS = "5"; + + public static final String RIGHT_TAP_TAG = "fling_right_taps"; + public static final String LEFT_TAP_TAG = "fling_left_taps"; + public static final String RIGHT_FLING_TAG = "fling_right"; + public static final String LEFT_FLING = "fling_left"; + public static final String UP_FLING = "fling_up"; + + public static final String SINGLE_LEFT_TAP_TAG = "single_left_tap"; + public static final String SINGLE_RIGHT_TAP_TAG = "single_right_tap"; + public static final String DOUBLE_LEFT_TAP_TAG = "double_left_tap"; + public static final String DOUBLE_RIGHT_TAP_TAG = "double_right_tap"; + public static final String LONG_LEFT_PRESS_TAG = "long_left_press"; + public static final String LONG_RIGHT_PRESS_TAG = "long_right_press"; + public static final String FLING_SHORT_LEFT_TAG = "fling_short_left"; + public static final String FLING_SHORT_RIGHT_TAG = "fling_short_right"; + public static final String FLING_LONG_LEFT_TAG = "fling_long_left"; + public static final String FLING_LONG_RIGHT_TAG = "fling_long_right"; + public static final String FLING_RIGHT_UP_TAG = "fling_right_up"; + public static final String FLING_LEFT_UP_TAG = "fling_left_up"; + public static final String CONFIG_fling_touchslop_increase_factor = "config_fling_touchslop_increase_factor"; + public static final String CONFIG_FlingLongSwipePortraitLeft = "config_FlingLongSwipePortraitLeft"; + public static final String CONFIG_FlingLongSwipePortraitRight = "config_FlingLongSwipePortraitRight"; + public static final String CONFIG_FlingLongSwipeLandscapeLeft = "config_FlingLongSwipeLandscapeLeft"; + public static final String CONFIG_FlingLongSwipeLandscapeRight = "config_FlingLongSwipeLandscapeRight"; + public static final String CONFIG_FlingLongSwipeVerticalUp = "config_FlingLongSwipeVerticalUp"; + public static final String CONFIG_FlingLongSwipeVerticalDown = "config_FlingLongSwipeVerticalDown"; + public static final String CONFIG_pulsePathEffect_1 = "config_pulsePathEffect_1"; + public static final String CONFIG_pulsePathEffect_2 = "config_pulsePathEffect_2"; + public static final String CONFIG_pulsePathStrokeWidth = "config_pulsePathStrokeWidth"; + public static final String CONFIG_pulseFillColor = "config_pulseFillColor"; + public static final String CONFIG_pulseDivisions = "config_pulseDivisions"; + public static final String CONFIG_pulseDbFuzzFactor = "config_pulseDbFuzzFactor"; + public static final String CONFIG_pulseDbFuzz = "config_pulseDbFuzz"; + + private static final Map<String, ConfigHolder> defMap = new HashMap<String, ConfigHolder>(); + + static { + defMap.put(CONFIG_fling_touchslop_increase_factor, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_fling_touchslop_increase_factor, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipePortraitLeft, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipePortraitLeft, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipePortraitRight, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipePortraitRight, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipeLandscapeLeft, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipeLandscapeLeft, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipeLandscapeRight, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipeLandscapeRight, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipeVerticalUp, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipeVerticalUp, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_FlingLongSwipeVerticalDown, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_FlingLongSwipeVerticalDown, + DUActionUtils.FORMAT_FLOAT, DUActionUtils.DIMEN)); + defMap.put(CONFIG_pulsePathEffect_1, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulsePathEffect_1, DUActionUtils.DIMEN_PIXEL)); + defMap.put(CONFIG_pulsePathEffect_2, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulsePathEffect_2, DUActionUtils.DIMEN_PIXEL)); + defMap.put(CONFIG_pulsePathStrokeWidth, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulsePathStrokeWidth, DUActionUtils.DIMEN_PIXEL)); + defMap.put(CONFIG_pulseFillColor, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulseFillColor, DUActionUtils.COLOR)); + defMap.put(CONFIG_pulseDivisions, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulseDivisions, DUActionUtils.INT)); + defMap.put(CONFIG_pulseDbFuzzFactor, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulseDbFuzzFactor, DUActionUtils.INT)); + defMap.put(CONFIG_pulseDbFuzz, new ConfigHolder( + DUActionUtils.PACKAGE_SYSTEMUI, CONFIG_pulseDbFuzz, DUActionUtils.INT)); + } + + private static final Map<String, ConfigMap> configMap = new HashMap<String, ConfigMap>(); + + static { + configMap.put(SINGLE_LEFT_TAP_TAG, new ConfigMap(1, ActionConfig.PRIMARY)); + configMap.put(SINGLE_RIGHT_TAP_TAG, new ConfigMap(0, ActionConfig.PRIMARY)); + configMap.put(DOUBLE_LEFT_TAP_TAG, new ConfigMap(1, ActionConfig.THIRD)); + configMap.put(DOUBLE_RIGHT_TAP_TAG, new ConfigMap(0, ActionConfig.THIRD)); + configMap.put(LONG_LEFT_PRESS_TAG, new ConfigMap(1, ActionConfig.SECOND)); + configMap.put(LONG_RIGHT_PRESS_TAG, new ConfigMap(0, ActionConfig.SECOND)); + configMap.put(FLING_SHORT_LEFT_TAG, new ConfigMap(3, ActionConfig.PRIMARY)); + configMap.put(FLING_SHORT_RIGHT_TAG, new ConfigMap(2, ActionConfig.PRIMARY)); + configMap.put(FLING_LONG_LEFT_TAG, new ConfigMap(3, ActionConfig.SECOND)); + configMap.put(FLING_LONG_RIGHT_TAG, new ConfigMap(2, ActionConfig.SECOND)); + configMap.put(FLING_RIGHT_UP_TAG, new ConfigMap(4, ActionConfig.PRIMARY)); + configMap.put(FLING_LEFT_UP_TAG, new ConfigMap(4, ActionConfig.SECOND)); + } + + public static final String FLING_CONFIG_DEFAULT = + dl(FLING_DEF_BUTTONS) + + dl(RIGHT_TAP_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) // short tap + + dl(SystemAction.GoogleNowOnTap.mAction) + dl(SystemAction.GoogleNowOnTap.mLabelRes) + dl(EMPTY) // long press + + dl(SystemAction.LastApp.mAction) + dl(SystemAction.LastApp.mLabelRes) + dl(EMPTY) // double tap + + + dl(LEFT_TAP_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) // short tap + + dl(SystemAction.GoogleNowOnTap.mAction) + dl(SystemAction.GoogleNowOnTap.mLabelRes) + dl(EMPTY) // long press + + dl(SystemAction.ScreenOff.mAction) + dl(SystemAction.ScreenOff.mLabelRes) + dl(EMPTY) // double tap + + + dl(RIGHT_FLING_TAG) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) // short fling + + dl(SystemAction.Assistant.mAction) + dl(SystemAction.Assistant.mLabelRes) + dl(EMPTY) // long fling + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // super fling? + + + dl(LEFT_FLING) + + dl(SystemAction.Back.mAction) + dl(SystemAction.Back.mLabelRes) + dl(EMPTY) // short fling + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // long fling + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // super fling? + + + dl(UP_FLING) + + dl(SystemAction.SplitScreen.mAction) + dl(SystemAction.SplitScreen.mLabelRes) + dl(EMPTY) // right side (short fling only) + + dl(SystemAction.SplitScreen.mAction) + dl(SystemAction.SplitScreen.mLabelRes) + dl(EMPTY) // left side (short fling only) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; + + @Override + public int getConfigType() { + return FLING; + } + + @Override + public String getUri() { + return Settings.Secure.FLING_GESTURE_ACTIONS; + } + + @Override + public String getDefaultConfig() { + return FLING_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return FLING_MAX_BUTTONS; + } + + @Override + public Map<String, ConfigMap> getActionMap() { + return configMap; + } + + @Override + public Bundle getConfigs(Context context) { + return loadConfigsFromMap(context, defMap); + } + } + + public static class PiePrimary implements Defaults { + public static final int PIE_PRIMARY_MAX_BUTTONS = 3; + public static final String PIE_PRIMARY_DEF_BUTTONS = "3"; + public static final String PIE_BACK_BUTTON_TAG = "pie_button_back"; + public static final String PIE_HOME_BUTTON_TAG = "pie_button_home"; + public static final String PIE_OVERVIEW_BUTTON_TAG = "pie_button_overview"; + + public static final String PIE_BACK_BUTTON_SINGLE_TAP_TAG = "pie_button_back_single_tap"; + public static final String PIE_HOME_BUTTON_SINGLE_TAP_TAG = "pie_button_home_single_tap"; + public static final String PIE_OVERVIEW_BUTTON_SINGLE_TAP_TAG = "pie_button_overview_single_tap"; + + public static final String PIE_BACK_BUTTON_LONG_PRESS_TAG = "pie_button_back_long_press"; + public static final String PIE_HOME_BUTTON_LONG_PRESS_TAG = "pie_button_home_long_press"; + public static final String PIE_OVERVIEW_BUTTON_LONG_PRESS_TAG = "pie_button_overview_long_press"; + + private static final Map<String, ConfigMap> configMap = new HashMap<String, ConfigMap>(); + + static { + configMap.put(PIE_BACK_BUTTON_SINGLE_TAP_TAG, new ConfigMap(0, ActionConfig.PRIMARY)); + configMap.put(PIE_HOME_BUTTON_SINGLE_TAP_TAG, new ConfigMap(1, ActionConfig.PRIMARY)); + configMap.put(PIE_OVERVIEW_BUTTON_SINGLE_TAP_TAG, new ConfigMap(2, ActionConfig.PRIMARY)); + configMap.put(PIE_BACK_BUTTON_LONG_PRESS_TAG, new ConfigMap(0, ActionConfig.SECOND)); + configMap.put(PIE_HOME_BUTTON_LONG_PRESS_TAG, new ConfigMap(1, ActionConfig.SECOND)); + configMap.put(PIE_OVERVIEW_BUTTON_LONG_PRESS_TAG, new ConfigMap(2, ActionConfig.SECOND)); + } + + public static final String PIE_PRIMARY_CONFIG_DEFAULT = + dl(PIE_PRIMARY_DEF_BUTTONS) + + dl(PIE_BACK_BUTTON_TAG) + + dl(SystemAction.Back.mAction) + dl(SystemAction.Back.mLabelRes) + dl(EMPTY) // single tap (PRIMARY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // long press (SECOND) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // double tap (NO-OP on Pie) + + + dl(PIE_HOME_BUTTON_TAG) + + dl(SystemAction.Home.mAction) + dl(SystemAction.Home.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(PIE_OVERVIEW_BUTTON_TAG) + + dl(SystemAction.Overview.mAction) + dl(SystemAction.Overview.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; // don't delimit final string + + + @Override + public int getConfigType() { + return PIE_PRIMARY; + } + + @Override + public String getUri() { + //return Settings.System.PIE_BUTTONS_CONFIG; + return "pie_primary_config"; + } + + @Override + public String getDefaultConfig() { + return PIE_PRIMARY_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return PIE_PRIMARY_MAX_BUTTONS; + } + + @Override + public Map<String, ConfigMap> getActionMap() { + return configMap; + } + + @Override + public Bundle getConfigs(Context context) { + // TODO Auto-generated method stub + return null; + } + } + + public static class PieSecond implements Defaults { + public static final int PIE_SECOND_MAX_BUTTONS = 7; + public static final String PIE_SECOND_DEF_BUTTONS = "5"; + public static final String PIE_BUTTON1_TAG = "pie_button_1"; + public static final String PIE_BUTTON2_TAG = "pie_button_2"; + public static final String PIE_BUTTON3_TAG = "pie_button_3"; + public static final String PIE_BUTTON4_TAG = "pie_button_4"; + public static final String PIE_BUTTON5_TAG = "pie_button_5"; + public static final String PIE_BUTTON6_TAG = "pie_button_6"; + public static final String PIE_BUTTON7_TAG = "pie_button_7"; + + public static final String PIE_SECOND_BUTTON1_SINGLE_TAP_TAG = "pie_second_button_1_single_tap"; + public static final String PIE_SECOND_BUTTON2_SINGLE_TAP_TAG = "pie_second_button_2_single_tap"; + public static final String PIE_SECOND_BUTTON3_SINGLE_TAP_TAG = "pie_second_button_3_single_tap"; + public static final String PIE_SECOND_BUTTON4_SINGLE_TAP_TAG = "pie_second_button_4_single_tap"; + public static final String PIE_SECOND_BUTTON5_SINGLE_TAP_TAG = "pie_second_button_5_single_tap"; + public static final String PIE_SECOND_BUTTON6_SINGLE_TAP_TAG = "pie_second_button_6_single_tap"; + public static final String PIE_SECOND_BUTTON7_SINGLE_TAP_TAG = "pie_second_button_7_single_tap"; + + public static final String PIE_SECOND_BUTTON1_LONG_PRESS_TAG = "pie_second_button_1_long_press"; + public static final String PIE_SECOND_BUTTON2_LONG_PRESS_TAG = "pie_second_button_2_long_press"; + public static final String PIE_SECOND_BUTTON3_LONG_PRESS_TAG = "pie_second_button_3_long_press"; + public static final String PIE_SECOND_BUTTON4_LONG_PRESS_TAG = "pie_second_button_4_long_press"; + public static final String PIE_SECOND_BUTTON5_LONG_PRESS_TAG = "pie_second_button_5_long_press"; + public static final String PIE_SECOND_BUTTON6_LONG_PRESS_TAG = "pie_second_button_6_long_press"; + public static final String PIE_SECOND_BUTTON7_LONG_PRESS_TAG = "pie_second_button_7_long_press"; + + private static final Map<String, ConfigMap> configMap = new HashMap<String, ConfigMap>(); + + static { + configMap.put(PIE_SECOND_BUTTON1_SINGLE_TAP_TAG, new ConfigMap(0, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON2_SINGLE_TAP_TAG, new ConfigMap(1, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON3_SINGLE_TAP_TAG, new ConfigMap(2, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON4_SINGLE_TAP_TAG, new ConfigMap(3, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON5_SINGLE_TAP_TAG, new ConfigMap(4, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON6_SINGLE_TAP_TAG, new ConfigMap(5, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON7_SINGLE_TAP_TAG, new ConfigMap(6, ActionConfig.PRIMARY)); + configMap.put(PIE_SECOND_BUTTON1_LONG_PRESS_TAG, new ConfigMap(0, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON2_LONG_PRESS_TAG, new ConfigMap(1, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON3_LONG_PRESS_TAG, new ConfigMap(2, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON4_LONG_PRESS_TAG, new ConfigMap(3, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON5_LONG_PRESS_TAG, new ConfigMap(4, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON6_LONG_PRESS_TAG, new ConfigMap(5, ActionConfig.SECOND)); + configMap.put(PIE_SECOND_BUTTON7_LONG_PRESS_TAG, new ConfigMap(6, ActionConfig.SECOND)); + } + + public static final String PIE_SECOND_CONFIG_DEFAULT = + dl(PIE_SECOND_DEF_BUTTONS) + + dl(PIE_BUTTON1_TAG) + + dl(SystemAction.PowerMenu.mAction) + dl(SystemAction.PowerMenu.mLabelRes) + dl(EMPTY) // single tap (PRIMARY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // long press (SECOND) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) // double tap (NO-OP on Pie) + + + dl(PIE_BUTTON2_TAG) + + dl(SystemAction.NotificationPanel.mAction) + dl(SystemAction.NotificationPanel.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(PIE_BUTTON3_TAG) + + dl(SystemAction.Assistant.mAction) + dl(SystemAction.Assistant.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(PIE_BUTTON4_TAG) + + dl(SystemAction.Screenshot.mAction) + dl(SystemAction.Screenshot.mLabelRes) + dl(EMPTY) + + dl(SystemAction.RegionScreenshot.mAction) + dl(SystemAction.RegionScreenshot.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + + dl(PIE_BUTTON5_TAG) + + dl(SystemAction.LastApp.mAction) + dl(SystemAction.LastApp.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + dl(EMPTY) + + dl(SystemAction.NoAction.mAction) + dl(SystemAction.NoAction.mLabelRes) + EMPTY; + + @Override + public int getConfigType() { + return PIE_SECONDARY; + } + + @Override + public String getUri() { + //return Settings.System.PIE_BUTTONS_CONFIG_SECOND_LAYER; + return "pie_second_config"; + } + + @Override + public String getDefaultConfig() { + return PIE_SECOND_CONFIG_DEFAULT; + } + + @Override + public int getMaxButtons() { + return PIE_SECOND_MAX_BUTTONS; + } + + @Override + public Map<String, ConfigMap> getActionMap() { + return configMap; + } + + @Override + public Bundle getConfigs(Context context) { + // TODO Auto-generated method stub + return null; + } + + } + + private static Bundle loadConfigsFromMap(Context ctx, Map<String, ConfigHolder> configMap) { + Bundle b = new Bundle(); + for (Map.Entry<String, ConfigHolder> entry : configMap.entrySet()) { + ConfigHolder holder = entry.getValue(); + Object obj = DUActionUtils.getValue(ctx, holder.name, holder.type, holder.format, + holder.pkg); + DUActionUtils.putValue(holder.name, obj, holder.type, b); + } + return b; + } + + private static class ConfigHolder { + public String pkg; + public String name; + public String format; + public String type; + + public ConfigHolder(String pkg, String name, String type) { + this(pkg, name, null, type); + } + + public ConfigHolder(String pkg, String name, String format, String type) { + this.pkg = pkg; + this.name = name; + this.format = format; + this.type = type; + } + } + + public static class ConfigMap { + public int button = -1; + public int action = -1; + + public ConfigMap() { + }; + + public ConfigMap(int button, int action) { + this.button = button; + this.action = action; + } + } + + public static final int PIE_PRIMARY_MAX_BUTTONS = 5; + public static final int PIE_SECONDAY_MAX_BUTTONS = 7; + +} diff --git a/src/com/android/internal/utils/du/ActionHandler.java b/src/com/android/internal/utils/du/ActionHandler.java new file mode 100644 index 0000000..f8c0ad8 --- /dev/null +++ b/src/com/android/internal/utils/du/ActionHandler.java @@ -0,0 +1,1169 @@ +/* + * Copyright (C) 2015 The TeamEos Project + * Copyright (C) 2016-2017 The DirtyUnicorns 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. + * + * Launches actions assigned to widgets. Creates bundles of state based + * on the type of action passed. + * + */ + +package com.android.internal.utils.du; + +import android.app.ActivityManager; +import android.app.ActivityManagerNative; +import android.app.ActivityOptions; +import android.app.IActivityManager; +import android.app.SearchManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.bluetooth.BluetoothAdapter; +import android.content.ActivityNotFoundException; +import android.content.ContentResolver; +import android.content.Context; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.hardware.input.InputManager; +import android.media.AudioManager; +import android.media.ToneGenerator; +import android.media.session.MediaSessionLegacyHelper; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.os.Vibrator; +import android.provider.Settings; +import android.service.wallpaper.WallpaperService; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.InputDevice; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.WindowManagerGlobal; +//import android.view.WindowManagerPolicyControl; +import android.view.inputmethod.InputMethodManager; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.utils.du.Config.ActionConfig; + +public class ActionHandler { + public static String TAG = ActionHandler.class.getSimpleName(); + + public static final String SYSTEM_PREFIX = "task"; + public static final String SYSTEMUI = "com.android.systemui"; + + // track and filter special actions + public static final String TASK_IME = "task_ime"; + public static final String TASK_MEDIA = "task_media"; + public static final String TASK_SOUNDMODE = "task_soundmode"; + + public static final String SYSTEMUI_TASK_NO_ACTION = "task_no_action"; + public static final String SYSTEMUI_TASK_SETTINGS_PANEL = "task_settings_panel"; + public static final String SYSTEMUI_TASK_NOTIFICATION_PANEL = "task_notification_panel"; + public static final String SYSTEMUI_TASK_SCREENSHOT = "task_screenshot"; + public static final String SYSTEMUI_TASK_REGION_SCREENSHOT = "task_region_screenshot"; + public static final String SYSTEMUI_TASK_SCREENRECORD = "task_screenrecord"; + // public static final String SYSTEMUI_TASK_AUDIORECORD = + // "task_audiorecord"; + public static final String SYSTEMUI_TASK_EXPANDED_DESKTOP = "task_expanded_desktop"; + public static final String SYSTEMUI_TASK_SCREENOFF = "task_screenoff"; + public static final String SYSTEMUI_TASK_KILL_PROCESS = "task_killcurrent"; + public static final String SYSTEMUI_TASK_ASSIST = "task_assist"; + public static final String SYSTEMUI_TASK_GOOGLE_NOW_ON_TAP = "task_google_now_on_tap"; + public static final String SYSTEMUI_TASK_POWER_MENU = "task_powermenu"; + public static final String SYSTEMUI_TASK_TORCH = "task_torch"; + public static final String SYSTEMUI_TASK_CAMERA = "task_camera"; + public static final String SYSTEMUI_TASK_BT = "task_bt"; + public static final String SYSTEMUI_TASK_WIFI = "task_wifi"; + public static final String SYSTEMUI_TASK_WIFIAP = "task_wifiap"; + public static final String SYSTEMUI_TASK_RECENTS = "task_recents"; + public static final String SYSTEMUI_TASK_LAST_APP = "task_last_app"; + public static final String SYSTEMUI_TASK_VOICE_SEARCH = "task_voice_search"; + public static final String SYSTEMUI_TASK_APP_SEARCH = "task_app_search"; + public static final String SYSTEMUI_TASK_MENU = "task_menu"; + public static final String SYSTEMUI_TASK_BACK = "task_back"; + public static final String SYSTEMUI_TASK_HOME = "task_home"; + public static final String SYSTEMUI_TASK_IME_SWITCHER = "task_ime_switcher"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_LEFT = "task_ime_navigation_left"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_RIGHT = "task_ime_navigation_right"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_UP = "task_ime_navigation_up"; + public static final String SYSTEMUI_TASK_IME_NAVIGATION_DOWN = "task_ime_navigation_down"; + public static final String SYSTEMUI_TASK_MEDIA_PREVIOUS = "task_media_previous"; + public static final String SYSTEMUI_TASK_MEDIA_NEXT = "task_media_next"; + public static final String SYSTEMUI_TASK_MEDIA_PLAY_PAUSE = "task_media_play_pause"; + public static final String SYSTEMUI_TASK_SOUNDMODE_VIB = "task_soundmode_vib"; + public static final String SYSTEMUI_TASK_SOUNDMODE_SILENT = "task_soundmode_silent"; + public static final String SYSTEMUI_TASK_SOUNDMODE_VIB_SILENT = "task_soundmode_vib_silent"; + public static final String SYSTEMUI_TASK_WAKE_DEVICE = "task_wake_device"; + public static final String SYSTEMUI_TASK_STOP_SCREENPINNING = "task_stop_screenpinning"; + public static final String SYSTEMUI_TASK_CLEAR_NOTIFICATIONS = "task_clear_notifications"; + public static final String SYSTEMUI_TASK_VOLUME_PANEL = "task_volume_panel"; + public static final String SYSTEMUI_TASK_EDITING_SMARTBAR = "task_editing_smartbar"; + public static final String SYSTEMUI_TASK_SPLIT_SCREEN = "task_split_screen"; + public static final String SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT = "task_one_handed_mode_left"; + public static final String SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT = "task_one_handed_mode_right"; + public static final String SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH = "task_assistant_sound_search"; + + public static final String INTENT_SHOW_POWER_MENU = "action_handler_show_power_menu"; + public static final String INTENT_TOGGLE_SCREENRECORD = "action_handler_toggle_screenrecord"; + public static final String INTENT_SCREENSHOT = "action_handler_screenshot"; + public static final String INTENT_REGION_SCREENSHOT = "action_handler_region_screenshot"; + + // remove actions from here as they come back on deck + static final Set<String> sDisabledActions = new HashSet<String>(); + static { + sDisabledActions.add(SYSTEMUI_TASK_SCREENRECORD); + sDisabledActions.add(SYSTEMUI_TASK_EXPANDED_DESKTOP); + sDisabledActions.add(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT); + sDisabledActions.add(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT); + // we need to make this more reliable when the user tap the partial screenshot button + // quickly and more times + sDisabledActions.add(SYSTEMUI_TASK_REGION_SCREENSHOT); + } + + static enum SystemAction { + NoAction(SYSTEMUI_TASK_NO_ACTION, SYSTEMUI, "label_action_no_action", "ic_sysbar_no_action"), + SettingsPanel(SYSTEMUI_TASK_SETTINGS_PANEL, SYSTEMUI, "label_action_settings_panel", "ic_sysbar_settings_panel"), + NotificationPanel(SYSTEMUI_TASK_NOTIFICATION_PANEL, SYSTEMUI, "label_action_notification_panel", "ic_sysbar_notification_panel"), + Screenshot(SYSTEMUI_TASK_SCREENSHOT, SYSTEMUI, "label_action_screenshot", "ic_sysbar_screenshot"), + RegionScreenshot(SYSTEMUI_TASK_REGION_SCREENSHOT, SYSTEMUI, "label_action_region_screenshot", "ic_sysbar_region_screenshot"), + Screenrecord(SYSTEMUI_TASK_SCREENRECORD, SYSTEMUI, "label_action_screenrecord", "ic_sysbar_record_screen"), + ExpandedDesktop(SYSTEMUI_TASK_EXPANDED_DESKTOP, SYSTEMUI, "label_action_expanded_desktop", "ic_sysbar_expanded_desktop"), + ScreenOff(SYSTEMUI_TASK_SCREENOFF, SYSTEMUI, "label_action_screen_off", "ic_sysbar_screen_off"), + KillApp(SYSTEMUI_TASK_KILL_PROCESS, SYSTEMUI, "label_action_force_close_app", "ic_sysbar_killtask"), + Assistant(SYSTEMUI_TASK_ASSIST, SYSTEMUI, "label_action_search_assistant", "ic_sysbar_assist"), + GoogleNowOnTap(SYSTEMUI_TASK_GOOGLE_NOW_ON_TAP, SYSTEMUI, "label_action_google_now_on_tap", "ic_sysbar_google_now_on_tap"), + VoiceSearch(SYSTEMUI_TASK_VOICE_SEARCH, SYSTEMUI, "label_action_voice_search", "ic_sysbar_search"), + InAppSearch(SYSTEMUI_TASK_APP_SEARCH, SYSTEMUI, "label_action_in_app_search", "ic_sysbar_in_app_search"), + Flashlight(SYSTEMUI_TASK_TORCH, SYSTEMUI, "label_action_flashlight", "ic_sysbar_torch"), + Bluetooth(SYSTEMUI_TASK_BT, SYSTEMUI, "label_action_bluetooth", "ic_sysbar_bt"), + WiFi(SYSTEMUI_TASK_WIFI, SYSTEMUI, "label_action_wifi", "ic_sysbar_wifi"), + Hotspot(SYSTEMUI_TASK_WIFIAP, SYSTEMUI, "label_action_hotspot", "ic_sysbar_hotspot"), + LastApp(SYSTEMUI_TASK_LAST_APP, SYSTEMUI, "label_action_last_app", "ic_sysbar_lastapp"), + Overview(SYSTEMUI_TASK_RECENTS, SYSTEMUI, "label_action_overview", "ic_sysbar_recent"), + PowerMenu(SYSTEMUI_TASK_POWER_MENU, SYSTEMUI, "label_action_power_menu", "ic_sysbar_power_menu"), + Menu(SYSTEMUI_TASK_MENU, SYSTEMUI, "label_action_menu", "ic_sysbar_menu"), + Back(SYSTEMUI_TASK_BACK, SYSTEMUI, "label_action_back", "ic_sysbar_back"), + Home(SYSTEMUI_TASK_HOME, SYSTEMUI, "label_action_home", "ic_sysbar_home"), + Ime(SYSTEMUI_TASK_IME_SWITCHER, SYSTEMUI, "label_action_ime_switcher", "ic_ime_switcher_smartbar"), + StopScreenPinning(SYSTEMUI_TASK_STOP_SCREENPINNING, SYSTEMUI, "label_action_stop_screenpinning", "ic_smartbar_screen_pinning_off"), + ImeArrowDown(SYSTEMUI_TASK_IME_NAVIGATION_DOWN, SYSTEMUI, "label_action_ime_down", "ic_sysbar_ime_down"), + ImeArrowLeft(SYSTEMUI_TASK_IME_NAVIGATION_LEFT, SYSTEMUI, "label_action_ime_left", "ic_sysbar_ime_left"), + ImeArrowRight(SYSTEMUI_TASK_IME_NAVIGATION_RIGHT, SYSTEMUI, "label_action_ime_right", "ic_sysbar_ime_right"), + ImeArrowUp(SYSTEMUI_TASK_IME_NAVIGATION_UP, SYSTEMUI, "label_action_ime_up", "ic_sysbar_ime_up"), + ClearNotifications(SYSTEMUI_TASK_CLEAR_NOTIFICATIONS, SYSTEMUI, "label_action_clear_notifications", "ic_sysbar_clear_notifications"), + VolumePanel(SYSTEMUI_TASK_VOLUME_PANEL, SYSTEMUI, "label_action_volume_panel", "ic_sysbar_volume_panel"), + EditingSmartbar(SYSTEMUI_TASK_EDITING_SMARTBAR, SYSTEMUI, "label_action_editing_smartbar", "ic_sysbar_editing_smartbar"), + SplitScreen(SYSTEMUI_TASK_SPLIT_SCREEN, SYSTEMUI, "label_action_split_screen", "ic_sysbar_docked"), + OneHandedModeLeft(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT, SYSTEMUI, "label_action_one_handed_mode_left", "ic_sysbar_one_handed_mode_left"), + OneHandedModeRight(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT, SYSTEMUI, "label_action_one_handed_mode_right", "ic_sysbar_one_handed_mode_right"), + MediaArrowLeft(SYSTEMUI_TASK_MEDIA_PREVIOUS, SYSTEMUI, "label_action_media_left", "ic_skip_previous"), + MediaArrowRight(SYSTEMUI_TASK_MEDIA_NEXT, SYSTEMUI, "label_action_media_right", "ic_skip_next"), + AssistantSoundSearch(SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH, SYSTEMUI, "label_action_assistant_sound_search", "ic_assistant_sound_search"); + + String mAction; + String mResPackage; + String mLabelRes; + String mIconRes; + String mDarkIconRes; + + private SystemAction(String action, String resPackage, String labelRes, String iconRes) { + mAction = action; + mResPackage = resPackage; + mLabelRes = labelRes; + mIconRes = iconRes; + mDarkIconRes = iconRes + "_dark"; + } + + private ActionConfig create(Context ctx) { + return new ActionConfig(ctx, mAction); + } + } + + /* + * Enumerated system actions with label and drawable support + */ + static SystemAction[] systemActions = new SystemAction[] { + SystemAction.NoAction, SystemAction.SettingsPanel, + SystemAction.NotificationPanel, SystemAction.Screenshot, + SystemAction.ScreenOff, SystemAction.KillApp, + SystemAction.Assistant, SystemAction.GoogleNowOnTap, + SystemAction.Flashlight, SystemAction.Bluetooth, + SystemAction.WiFi, SystemAction.Hotspot, + SystemAction.LastApp, SystemAction.PowerMenu, + SystemAction.Overview,SystemAction.Menu, + SystemAction.Back, SystemAction.VoiceSearch, + SystemAction.Home, SystemAction.ExpandedDesktop, + SystemAction.Screenrecord, SystemAction.Ime, + SystemAction.StopScreenPinning, SystemAction.ImeArrowDown, + SystemAction.ImeArrowLeft, SystemAction.ImeArrowRight, + SystemAction.ImeArrowUp, SystemAction.InAppSearch, + SystemAction.VolumePanel, SystemAction.ClearNotifications, + SystemAction.EditingSmartbar, SystemAction.SplitScreen, + SystemAction.RegionScreenshot, SystemAction.OneHandedModeLeft, + SystemAction.OneHandedModeRight, SystemAction.MediaArrowLeft, + SystemAction.MediaArrowRight, SystemAction.AssistantSoundSearch + }; + + public static class ActionIconResources { + Drawable[] mDrawables; + Drawable[] mDarkDrawables; + Map<String, Integer> mIndexMap; + + public ActionIconResources(Resources res) { + mDrawables = new Drawable[systemActions.length]; + mDarkDrawables = new Drawable[systemActions.length]; + mIndexMap = new HashMap<String, Integer>(); + for (int i = 0; i < systemActions.length; i++) { + mIndexMap.put(systemActions[i].mAction, i); + mDrawables[i] = DUActionUtils.getDrawable(res, systemActions[i].mIconRes, + systemActions[i].mResPackage); + mDarkDrawables[i] = DUActionUtils.getDrawable(res, systemActions[i].mDarkIconRes, + systemActions[i].mResPackage); + } + } + + public void updateResources(Resources res) { + for (int i = 0; i < mDrawables.length; i++) { + mDrawables[i] = DUActionUtils.getDrawable(res, systemActions[i].mIconRes, + systemActions[i].mResPackage); + mDarkDrawables[i] = DUActionUtils.getDrawable(res, systemActions[i].mDarkIconRes, + systemActions[i].mResPackage); + } + } + + public Drawable getActionDrawable(String action) { + return mDrawables[mIndexMap.get(action)]; + } + + public Drawable getDarkActionDrawable(String action) { + return mDarkDrawables[mIndexMap.get(action)]; + } + } + + /* + * Default list to display in an action picker + * Filter by device capabilities and actions used internally + * but we don't really want as assignable + */ + public static ArrayList<ActionConfig> getSystemActions(Context context) { + ArrayList<ActionConfig> bundle = new ArrayList<ActionConfig>(); + for (int i = 0; i < systemActions.length; i++) { + ActionConfig c = systemActions[i].create(context); + String action = c.getAction(); + if (sDisabledActions.contains(action)) { + continue; + } + if (TextUtils.equals(action, SYSTEMUI_TASK_STOP_SCREENPINNING) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_DOWN) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_LEFT) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_RIGHT) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_NAVIGATION_UP) + || TextUtils.equals(action, SYSTEMUI_TASK_IME_SWITCHER) + || TextUtils.equals(action, SYSTEMUI_TASK_MEDIA_PREVIOUS) + || TextUtils.equals(action, SYSTEMUI_TASK_MEDIA_NEXT)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_WIFIAP) + && !DUActionUtils.deviceSupportsMobileData(context)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_BT) + && !DUActionUtils.deviceSupportsBluetooth()) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_TORCH) + && !DUActionUtils.deviceSupportsFlashLight(context)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_CAMERA) + && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) { + continue; + } else if (TextUtils.equals(action, SYSTEMUI_TASK_SCREENRECORD)) { + if (!DUActionUtils.getBoolean(context, "config_enableScreenrecordChord", + DUActionUtils.PACKAGE_ANDROID)) { + continue; + } + } else if (TextUtils.equals(action, SYSTEMUI_TASK_EDITING_SMARTBAR)) { + // don't allow smartbar editor on Fling + if (Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.NAVIGATION_BAR_MODE, 0, + UserHandle.USER_CURRENT) == 1) { + continue; + } + } + bundle.add(c); + } + Collections.sort(bundle); + return bundle; + } + + private static final class StatusBarHelper { + private static boolean isPreloaded = false; + private static IStatusBarService mService = null; + + private static IStatusBarService getStatusBarService() { + synchronized (StatusBarHelper.class) { + if (mService == null) { + try { + mService = IStatusBarService.Stub.asInterface( + ServiceManager.getService("statusbar")); + } catch (Exception e) { + } + } + return mService; + } + } + + private static void dispatchNavigationEditorResult(Intent intent) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.dispatchNavigationEditorResults(intent); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + + private static void toggleNavigationEditor() { + IStatusBarService service = getStatusBarService(); + try { + service.toggleNavigationEditor(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private static void toggleFlashlight() { + IStatusBarService service = getStatusBarService(); + try { + service.toggleFlashlight(); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private static void toggleRecentsApps() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + sendCloseSystemWindows("recentapps"); + service.toggleRecentApps(); + } catch (RemoteException e) { + return; + } + isPreloaded = false; + } + } + + private static void cancelPreloadRecentApps() { + if (isPreloaded == false) + return; + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.cancelPreloadRecentApps(); + } catch (Exception e) { + return; + } + } + isPreloaded = false; + } + + private static void preloadRecentApps() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.preloadRecentApps(); + } catch (RemoteException e) { + isPreloaded = false; + return; + } + isPreloaded = true; + } + } + + private static void expandNotificationPanel() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandNotificationsPanel(); + } catch (RemoteException e) { + } + } + } + + private static void expandSettingsPanel() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.expandSettingsPanel(null); + } catch (RemoteException e) { + } + } + } + + private static void fireGoogleNowOnTap() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.startAssist(new Bundle()); + } catch (RemoteException e) { + } + } + } + + private static void splitScreen() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.toggleSplitScreen(); + } catch (RemoteException e) { + } + } + } +/* + private static void fireIntentAfterKeyguard(Intent intent) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.showCustomIntentAfterKeyguard(intent); + } catch (RemoteException e) { + } + } + } +*/ + private static void clearAllNotifications() { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.onClearAllNotifications(ActivityManager.getCurrentUser()); + } catch (RemoteException e) { + } + } + } + + private static void sendSystemKeyToStatusBar(int keyCode) { + IStatusBarService service = getStatusBarService(); + if (service != null) { + try { + service.handleSystemKey(keyCode); + } catch (RemoteException e) { + } + } + } + } + + public static void toggleRecentApps() { + StatusBarHelper.toggleRecentsApps(); + } + + public static void cancelPreloadRecentApps() { + StatusBarHelper.cancelPreloadRecentApps(); + } + + public static void preloadRecentApps() { + StatusBarHelper.preloadRecentApps(); + } +/* + public static void performTaskFromKeyguard(Context ctx, String action) { + // null: throw it out + if (action == null) { + return; + } + // not a system action, should be intent + if (!action.startsWith(SYSTEM_PREFIX)) { + Intent intent = DUActionUtils.getIntent(action); + if (intent == null) { + return; + } + StatusBarHelper.fireIntentAfterKeyguard(intent); + } else { + performTask(ctx, action); + } + } +*/ + public static void dispatchNavigationEditorResult(Intent intent) { + StatusBarHelper.dispatchNavigationEditorResult(intent); + } + + public static void performTask(Context context, String action) { + // null: throw it out + if (action == null) { + return; + } + if (sDisabledActions.contains(action)) { + return; + } + // not a system action, should be intent + if (!action.startsWith(SYSTEM_PREFIX)) { + Intent intent = DUActionUtils.getIntent(action); + if (intent == null) { + return; + } + launchActivity(context, intent); + return; + } else if (action.equals(SYSTEMUI_TASK_NO_ACTION)) { + return; + } else if (action.equals(SYSTEMUI_TASK_KILL_PROCESS)) { + killProcess(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENSHOT)) { + sendCommandToWindowManager(new Intent(INTENT_SCREENSHOT)); + return; + } else if (action.equals(SYSTEMUI_TASK_REGION_SCREENSHOT)) { + sendCommandToWindowManager(new Intent(INTENT_REGION_SCREENSHOT)); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENRECORD)) { + sendCommandToWindowManager(new Intent(INTENT_TOGGLE_SCREENRECORD)); + return; + // } else if (action.equals(SYSTEMUI_TASK_AUDIORECORD)) { + // takeAudiorecord(); + } else if (action.equals(SYSTEMUI_TASK_EXPANDED_DESKTOP)) { + // toggleExpandedDesktop(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SCREENOFF)) { + screenOff(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WAKE_DEVICE)) { + PowerManager powerManager = + (PowerManager) context.getSystemService(Context.POWER_SERVICE); + if (!powerManager.isScreenOn()) { + powerManager.wakeUp(SystemClock.uptimeMillis()); + } + return; + } else if (action.equals(SYSTEMUI_TASK_ASSIST)) { + launchAssistAction(context); + return; + } else if (action.equals(SYSTEMUI_TASK_GOOGLE_NOW_ON_TAP)) { + StatusBarHelper.fireGoogleNowOnTap(); + return; + } else if (action.equals(SYSTEMUI_TASK_POWER_MENU)) { + sendCommandToWindowManager(new Intent(INTENT_SHOW_POWER_MENU)); + return; + } else if (action.equals(SYSTEMUI_TASK_TORCH)) { + StatusBarHelper.toggleFlashlight(); + return; + } else if (action.equals(SYSTEMUI_TASK_CAMERA)) { + launchCamera(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WIFI)) { + toggleWifi(context); + return; + } else if (action.equals(SYSTEMUI_TASK_WIFIAP)) { + toggleWifiAP(context); + return; + } else if (action.equals(SYSTEMUI_TASK_BT)) { + toggleBluetooth(); + return; + } else if (action.equals(SYSTEMUI_TASK_RECENTS)) { + toggleRecentApps(); + return; + } else if (action.equals(SYSTEMUI_TASK_LAST_APP)) { + switchToLastApp(context); + return; + } else if (action.equals(SYSTEMUI_TASK_SETTINGS_PANEL)) { + StatusBarHelper.expandSettingsPanel(); + return; + } else if (action.equals(SYSTEMUI_TASK_NOTIFICATION_PANEL)) { + StatusBarHelper.expandNotificationPanel(); + return; + } else if (action.equals(SYSTEMUI_TASK_VOICE_SEARCH)) { + launchVoiceSearch(context); + return; + } else if (action.equals(SYSTEMUI_TASK_APP_SEARCH)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_SEARCH); + return; + } else if (action.equals(SYSTEMUI_TASK_MENU)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_MENU); + return; + } else if (action.equals(SYSTEMUI_TASK_BACK)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_BACK); + return; + } else if (action.equals(SYSTEMUI_TASK_HOME)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_HOME); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_SWITCHER)) { + ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)) + .showInputMethodPicker(true /* showAuxiliarySubtypes */); + return; + } else if (action.equals(SYSTEMUI_TASK_STOP_SCREENPINNING)) { + turnOffLockTask(); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_RIGHT)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_RIGHT); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_UP)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_UP); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_DOWN)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_DOWN); + return; + } else if (action.equals(SYSTEMUI_TASK_IME_NAVIGATION_LEFT)) { + triggerVirtualKeypress(context, KeyEvent.KEYCODE_DPAD_LEFT); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_PREVIOUS)) { + StatusBarHelper.sendSystemKeyToStatusBar(KeyEvent.KEYCODE_MEDIA_PREVIOUS); + //dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PREVIOUS, context); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_NEXT)) { + StatusBarHelper.sendSystemKeyToStatusBar(KeyEvent.KEYCODE_MEDIA_NEXT); + //dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_NEXT, context); + return; + } else if (action.equals(SYSTEMUI_TASK_MEDIA_PLAY_PAUSE)) { + dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, context); + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_VIB)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null) { + vib.vibrate(50); + } + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() != AudioManager.RINGER_MODE_SILENT) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_SOUNDMODE_VIB_SILENT)) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + if (am != null && ActivityManagerNative.isSystemReady()) { + if (am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) { + am.setRingerMode(AudioManager.RINGER_MODE_VIBRATE); + Vibrator vib = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + if (vib != null) { + vib.vibrate(50); + } + } else if (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) { + am.setRingerMode(AudioManager.RINGER_MODE_SILENT); + } else { + am.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + ToneGenerator tg = new ToneGenerator( + AudioManager.STREAM_NOTIFICATION, + (int) (ToneGenerator.MAX_VOLUME * 0.85)); + if (tg != null) { + tg.startTone(ToneGenerator.TONE_PROP_BEEP); + } + } + } + return; + } else if (action.equals(SYSTEMUI_TASK_CLEAR_NOTIFICATIONS)) { + StatusBarHelper.clearAllNotifications(); + return; + } else if (action.equals(SYSTEMUI_TASK_VOLUME_PANEL)) { + volumePanel(context); + return; + } else if (action.equals(SYSTEMUI_TASK_EDITING_SMARTBAR)) { + StatusBarHelper.toggleNavigationEditor(); + return; + } else if (action.equals(SYSTEMUI_TASK_SPLIT_SCREEN)) { + StatusBarHelper.splitScreen(); + return; + } else if (action.equals(SYSTEMUI_TASK_ONE_HANDED_MODE_LEFT)) { +// toggleOneHandedMode(context, "left"); + return; + } else if (action.equals(SYSTEMUI_TASK_ONE_HANDED_MODE_RIGHT)) { +// toggleOneHandedMode(context, "right"); + return; + } else if (action.equals(SYSTEMUI_TASK_ASSISTANT_SOUND_SEARCH)) { + startAssistantSoundSearch(context); + return; + } + } + + public static boolean isActionKeyEvent(String action) { + if (action.equals(SYSTEMUI_TASK_HOME) + || action.equals(SYSTEMUI_TASK_BACK) +// || action.equals(SYSTEMUI_TASK_SEARCH) + || action.equals(SYSTEMUI_TASK_MENU) +// || action.equals(ActionConstants.ACTION_MENU_BIG) + || action.equals(SYSTEMUI_TASK_NO_ACTION)) { + return true; + } + return false; + } + + private static void launchActivity(Context context, Intent intent) { + try { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } catch (Exception e) { + Log.i(TAG, "Unable to launch activity " + e); + } + } + + private static void switchToLastApp(Context context) { + final ActivityManager am = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.RunningTaskInfo lastTask = getLastTask(context, am); + + if (lastTask != null) { + am.moveTaskToFront(lastTask.id, ActivityManager.MOVE_TASK_NO_USER_ACTION, + getAnimation(context).toBundle()); + } + cancelPreloadRecentApps(); + } + + private static ActivityOptions getAnimation(Context context) { + return ActivityOptions.makeCustomAnimation(context, + com.android.internal.R.anim.du_app_in, + com.android.internal.R.anim.du_app_out); + } + + private static ActivityManager.RunningTaskInfo getLastTask(Context context, + final ActivityManager am) { + final List<String> packageNames = getCurrentLauncherPackages(context); + final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(5); + for (int i = 1; i < tasks.size(); i++) { + String packageName = tasks.get(i).topActivity.getPackageName(); + if (!packageName.equals(context.getPackageName()) + && !packageName.equals(SYSTEMUI) + && !packageNames.contains(packageName)) { + return tasks.get(i); + } + } + return null; + } + + private static List<String> getCurrentLauncherPackages(Context context) { + final PackageManager pm = context.getPackageManager(); + final List<ResolveInfo> homeActivities = new ArrayList<>(); + pm.getHomeActivities(homeActivities); + final List<String> packageNames = new ArrayList<>(); + for (ResolveInfo info : homeActivities) { + final String name = info.activityInfo.packageName; + if (!name.equals("com.android.settings")) { + packageNames.add(name); + } + } + return packageNames; + } + + private static void sendCloseSystemWindows(String reason) { + if (ActivityManagerNative.isSystemReady()) { + try { + ActivityManagerNative.getDefault().closeSystemDialogs(reason); + } catch (RemoteException e) { + } + } + } + +/* + private static void toggleExpandedDesktop(Context context) { + ContentResolver cr = context.getContentResolver(); + String newVal = ""; + String currentVal = Settings.Global.getString(cr, Settings.Global.POLICY_CONTROL); + if (currentVal == null) { + currentVal = newVal; + } + if ("".equals(currentVal)) { + newVal = "immersive.full=*"; + } + Settings.Global.putString(cr, Settings.Global.POLICY_CONTROL, newVal); + if (newVal.equals("")) { + WindowManagerPolicyControl.reloadFromSetting(context); + } + } +*/ + private static void launchVoiceSearch(Context context) { + sendCloseSystemWindows("assist"); + // launch the search activity + Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS); + try { + // TODO: This only stops the factory-installed search manager. + // Need to formalize an API to handle others + SearchManager searchManager = (SearchManager) context + .getSystemService(Context.SEARCH_SERVICE); + if (searchManager != null) { + searchManager.stopSearch(); + } + launchActivity(context, intent); + } catch (ActivityNotFoundException e) { + Slog.w(TAG, "No assist activity installed", e); + } + } + + private static void dispatchMediaKeyWithWakeLock(int keycode, Context context) { + if (ActivityManagerNative.isSystemReady()) { + KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + event = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); + MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true); + } + } + + private static void triggerVirtualKeypress(Context context, final int keyCode) { + final InputManager im = InputManager.getInstance(); + final long now = SystemClock.uptimeMillis(); + int downflags = 0; + + if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT + || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT + || keyCode == KeyEvent.KEYCODE_DPAD_UP + || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { + downflags = KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE; + } else { + downflags = KeyEvent.FLAG_FROM_SYSTEM; + } + + final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, + keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, + downflags, InputDevice.SOURCE_KEYBOARD); + final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP); + final Handler handler = new Handler(Looper.getMainLooper()); + + final Runnable downRunnable = new Runnable() { + @Override + public void run() { + im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }; + + final Runnable upRunnable = new Runnable() { + @Override + public void run() { + im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } + }; + + handler.post(downRunnable); + handler.postDelayed(upRunnable, 10); + } + + private static void launchCamera(Context context) { + Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); + PackageManager pm = context.getPackageManager(); + final ResolveInfo mInfo = pm.resolveActivity(i, 0); + Intent intent = new Intent().setComponent(new ComponentName(mInfo.activityInfo.packageName, + mInfo.activityInfo.name)); + launchActivity(context, intent); + } + + private static void toggleWifi(Context context) { + WifiManager wfm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + wfm.setWifiEnabled(!wfm.isWifiEnabled()); + } + + private static void toggleBluetooth() { + BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + boolean enabled = bluetoothAdapter.isEnabled(); + if (enabled) { + bluetoothAdapter.disable(); + } else { + bluetoothAdapter.enable(); + } + } + + private static void toggleWifiAP(Context context) { + final ContentResolver cr = context.getContentResolver(); + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final ConnectivityManager mConnectivityManager; + mConnectivityManager = (ConnectivityManager) context.getSystemService( + Context.CONNECTIVITY_SERVICE); + int state = wm.getWifiApState(); + boolean enabled = false; + switch (state) { + case WifiManager.WIFI_AP_STATE_ENABLING: + case WifiManager.WIFI_AP_STATE_ENABLED: + enabled = false; + break; + case WifiManager.WIFI_AP_STATE_DISABLING: + case WifiManager.WIFI_AP_STATE_DISABLED: + enabled = true; + break; + } + + // Turn on the Wifi AP + if (enabled) { + OnStartTetheringCallback callback = new OnStartTetheringCallback(); + mConnectivityManager.startTethering( + ConnectivityManager.TETHERING_WIFI, false, callback); + } else { + mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); + } + } + + static final class OnStartTetheringCallback extends + ConnectivityManager.OnStartTetheringCallback { + @Override + public void onTetheringStarted() {} + @Override + public void onTetheringFailed() { + // TODO: Show error. + } + } + + private static void sendCommandToWindowManager(Intent intent) { + IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); + try { + wm.sendCustomAction(intent); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private static void killProcess(Context context) { + if (context.checkCallingOrSelfPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) == PackageManager.PERMISSION_GRANTED + && !isLockTaskOn()) { + try { + PackageManager packageManager = context.getPackageManager(); + final Intent intent = new Intent(Intent.ACTION_MAIN); + String defaultHomePackage = "com.android.launcher"; + intent.addCategory(Intent.CATEGORY_HOME); + final ResolveInfo res = packageManager.resolveActivity(intent, 0); + if (res.activityInfo != null + && !res.activityInfo.packageName.equals("android")) { + defaultHomePackage = res.activityInfo.packageName; + } + + // Use UsageStats to determine foreground app + UsageStatsManager usageStatsManager = (UsageStatsManager) + context.getSystemService(Context.USAGE_STATS_SERVICE); + long current = System.currentTimeMillis(); + long past = current - (1000 * 60 * 60); // uses snapshot of usage over past 60 minutes + + // Get the list, then sort it chronilogically so most recent usage is at start of list + List<UsageStats> recentApps = usageStatsManager.queryUsageStats( + UsageStatsManager.INTERVAL_DAILY, past, current); + Collections.sort(recentApps, new Comparator<UsageStats>() { + @Override + public int compare(UsageStats lhs, UsageStats rhs) { + long timeLHS = lhs.getLastTimeUsed(); + long timeRHS = rhs.getLastTimeUsed(); + if (timeLHS > timeRHS) { + return -1; + } else if (timeLHS < timeRHS) { + return 1; + } + return 0; + } + }); + + IActivityManager iam = ActivityManagerNative.getDefault(); + // this may not be needed due to !isLockTaskOn() in entry if + //if (am.getLockTaskModeState() != ActivityManager.LOCK_TASK_MODE_NONE) return; + + // Look for most recent usagestat with lastevent == 1 and grab package name + // ...this seems to map to the UsageEvents.Event.MOVE_TO_FOREGROUND + String pkg = null; + for (int i = 0; i < recentApps.size(); i++) { + UsageStats mostRecent = recentApps.get(i); + if (mostRecent.mLastEvent == 1) { + pkg = mostRecent.mPackageName; + break; + } + } + + if (pkg != null && !pkg.equals("com.android.systemui") + && !pkg.equals(defaultHomePackage)) { + + // Restore home screen stack before killing the app + Intent home = new Intent(Intent.ACTION_MAIN, null); + home.addCategory(Intent.CATEGORY_HOME); + home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + context.startActivity(home); + + // Kill the app + iam.forceStopPackage(pkg, UserHandle.USER_CURRENT); + + // Remove killed app from Recents + final ActivityManager am = (ActivityManager) + context.getSystemService(Context.ACTIVITY_SERVICE); + final List<ActivityManager.RecentTaskInfo> recentTasks = + am.getRecentTasksForUser(ActivityManager.getMaxRecentTasksStatic(), + ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS + | ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS + | ActivityManager.RECENT_IGNORE_UNAVAILABLE + | ActivityManager.RECENT_INCLUDE_PROFILES, + UserHandle.CURRENT.getIdentifier()); + final int size = recentTasks.size(); + for (int i = 0; i < size; i++) { + ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i); + if (recentInfo.baseIntent.getComponent().getPackageName().equals(pkg)) { + int taskid = recentInfo.persistentId; + am.removeTask(taskid); + } + } + + String pkgName; + try { + pkgName = (String) packageManager.getApplicationLabel( + packageManager.getApplicationInfo(pkg, PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException e) { + // Just use pkg if issues getting appName + pkgName = pkg; + } + + Resources systemUIRes = DUActionUtils.getResourcesForPackage(context, DUActionUtils.PACKAGE_SYSTEMUI); + int ident = systemUIRes.getIdentifier("app_killed_message", DUActionUtils.STRING, DUActionUtils.PACKAGE_SYSTEMUI); + String toastMsg = systemUIRes.getString(ident, pkgName); + Context ctx = getPackageContext(context, DUActionUtils.PACKAGE_SYSTEMUI); + Toast.makeText(ctx != null ? ctx : context, toastMsg, Toast.LENGTH_SHORT).show(); + return; + } else { + // make a "didnt kill anything" toast? + return; + } + } catch (RemoteException remoteException) { + Log.d("ActionHandler", "Caller cannot kill processes, aborting"); + } + } else { + Log.d("ActionHandler", "Caller cannot kill processes, aborting"); + } + } + + + public static Context getPackageContext(Context context, String packageName) { + Context pkgContext = null; + if (context.getPackageName().equals(packageName)) { + pkgContext = context; + } else { + try { + pkgContext = context.createPackageContext(packageName, + Context.CONTEXT_IGNORE_SECURITY + | Context.CONTEXT_INCLUDE_CODE); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + } + return pkgContext; + } + + private static boolean isPackageLiveWalls(Context ctx, String pkg) { + if (ctx == null || pkg == null) { + return false; + } + List<ResolveInfo> liveWallsList = ctx.getPackageManager().queryIntentServices( + new Intent(WallpaperService.SERVICE_INTERFACE), + PackageManager.GET_META_DATA); + if (liveWallsList == null) { + return false; + } + for (ResolveInfo info : liveWallsList) { + if (info.serviceInfo != null) { + String packageName = info.serviceInfo.packageName; + if (TextUtils.equals(pkg, packageName)) { + return true; + } + } + } + return false; + } + + private static void screenOff(Context context) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + pm.goToSleep(SystemClock.uptimeMillis()); + } + + private static void launchAssistAction(Context context) { + sendCloseSystemWindows("assist"); + Intent intent = ((SearchManager) context.getSystemService(Context.SEARCH_SERVICE)) + .getAssistIntent(true); + if (intent != null) { + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_SINGLE_TOP + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + try { + context.startActivityAsUser(intent, UserHandle.CURRENT); + } catch (ActivityNotFoundException e) { + Slog.w(TAG, "No activity to handle assist action.", e); + } + } + } + + public static void turnOffLockTask() { + try { + ActivityManagerNative.getDefault().stopLockTaskMode(); + } catch (Exception e) { + } + } + + public static boolean isLockTaskOn() { + try { + return ActivityManagerNative.getDefault().isInLockTaskMode(); + } catch (Exception e) { + } + return false; + } + + public static void volumePanel(Context context) { + AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + am.adjustVolume(AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); + } + +/* + private static void toggleOneHandedMode(Context context, String direction) { + String str = Settings.Global.getString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE); + + if (TextUtils.isEmpty(str)) + Settings.Global.putString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE, direction); + else + Settings.Global.putString(context.getContentResolver(), Settings.Global.SINGLE_HAND_MODE, ""); + } + */ + + public static void startAssistantSoundSearch(Context context) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setAction("com.google.android.googlequicksearchbox.MUSIC_SEARCH"); + context.startActivity(intent); + } +} diff --git a/src/com/android/internal/utils/du/ActionHolder.java b/src/com/android/internal/utils/du/ActionHolder.java new file mode 100644 index 0000000..6040e8f --- /dev/null +++ b/src/com/android/internal/utils/du/ActionHolder.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * Widgets may implement this interface to interact with action configurations + * + */ + +package com.android.internal.utils.du; + +import com.android.internal.utils.du.ActionConstants.ConfigMap; +import com.android.internal.utils.du.ActionConstants.Defaults; +import com.android.internal.utils.du.Config.ActionConfig; +import com.android.internal.utils.du.Config.ButtonConfig; + +public interface ActionHolder { + public String getTag(); + public void setTag(String tag); + public Defaults getDefaults(); + public void setDefaults(Defaults defaults); + public ConfigMap getConfigMap(); + public void setConfigMap(ConfigMap map); + public ButtonConfig getButtonConfig(); + public void setButtonConfig(ButtonConfig button); + public ActionConfig getActionConfig(); + public void setActionConfig(ActionConfig action); + public ButtonConfig getDefaultButtonConfig(); + public void setDefaultButtonConfig(ButtonConfig button); + public ActionConfig getDefaultActionConfig(); + public void setDefaultActionConfig(ActionConfig action); +} diff --git a/src/com/android/internal/utils/du/Config.java b/src/com/android/internal/utils/du/Config.java new file mode 100644 index 0000000..214d625 --- /dev/null +++ b/src/com/android/internal/utils/du/Config.java @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2015 TeamEos project + * Author Randall Rushing aka bigrushdog, randall.rushing@gmail.com + * + * 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. + * + * Config.java: A helper class for loading/setting feature button configurations + * to SettingsProvider. We start with defining a "action" in the nested + * ActionConfig class. A action holds a raw action string, a label for that action, + * and helper functions for loading an associated drawable for that action. + * + * The nested static class ButtonConfig is a convenience class that holds three + * ActionConfig objects. Those ActionConfig objects are labeled as "PRIMARY", + * "SECOND", and "THIRD", which will typically refer to a single tap, long press, + * and double tap, respectively. However, this is not always the case, thus the + * more generalized naming convention. + * + * ActionConfig and ButtonConfig implement the private Stringable interface to allow + * for easy loading and setting + * + */ +package com.android.internal.utils.du; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.android.internal.utils.du.ActionHandler; +import com.android.internal.utils.du.ActionConstants.ConfigMap; +import com.android.internal.utils.du.ActionConstants.Defaults; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; + +public class Config { + private interface Stringable { + public String toDelimitedString(); + public void fromList(Context ctx, List<String> items); + } + + /** + * For use with action/button based features that don't require a defaut configuration + */ + public static ArrayList<ButtonConfig> getConfig(Context ctx, String uri, + boolean fromSecureSettings) { + if (ctx == null || uri == null) { + return null; + } + String config; + if (fromSecureSettings) { + config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } else { + config = Settings.System.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } + if (TextUtils.isEmpty(config)) { + return null; + } + ArrayList<String> items = new ArrayList<String>(); + items.addAll(Arrays.asList(config.split("\\|"))); // split string into array elements + int numConfigs = Integer.parseInt(items.get(0)); // first element is always the number of + // ButtonConfigs to parse + items.remove(0); // remove button count for a clean list of buttons + + ArrayList<ButtonConfig> buttonList = new ArrayList<ButtonConfig>(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; // (0, 10), (10, 20)... + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); // initialize button from list + // elements + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static ArrayList<ButtonConfig> getConfig(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return null; + } + String config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), defaults.getUri(), + UserHandle.USER_CURRENT); + if (TextUtils.isEmpty(config)) { + config = defaults.getDefaultConfig(); + } + + ArrayList<String> items = new ArrayList<String>(); + items.addAll(Arrays.asList(config.split("\\|"))); // split string into array elements + int numConfigs = Integer.parseInt(items.get(0)); // first element is always the number of ButtonConfigs to parse + items.remove(0); // remove button count for a clean list of buttons + + ArrayList<ButtonConfig> buttonList = new ArrayList<ButtonConfig>(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; // (0, 10), (10, 20)... + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); // initialize button from list elements + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static ArrayList<ButtonConfig> getDefaultConfig(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return null; + } + String config = defaults.getDefaultConfig(); + ArrayList<String> items = new ArrayList<String>(); + items.addAll(Arrays.asList(config.split("\\|"))); + int numConfigs = Integer.parseInt(items.get(0)); + items.remove(0); + ArrayList<ButtonConfig> buttonList = new ArrayList<ButtonConfig>(); + ButtonConfig buttonConfig; + + for (int i = 0; i < numConfigs; i++) { + int from = i * ButtonConfig.NUM_ELEMENTS; + int to = from + ButtonConfig.NUM_ELEMENTS; + buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(from, to)); + buttonList.add(buttonConfig); + } + return buttonList; + } + + public static void setConfig(Context ctx, Defaults defaults, ArrayList<ButtonConfig> config) { + if (ctx == null || defaults == null || config == null) { + return; + } + int numConfigs = config.size(); + if (numConfigs <= 0) { + return; + } + StringBuilder b = new StringBuilder(); + b.append(String.valueOf(numConfigs)); + b.append(ActionConstants.ACTION_DELIMITER); // size of list is always first element + for (ButtonConfig button : config) { + b.append(button.toDelimitedString()); // this is just beautiful ;D + } + String s = b.toString(); + if (s.endsWith(ActionConstants.ACTION_DELIMITER)) { + s = removeLastChar(s); // trim final delimiter if need be + } + Settings.Secure.putStringForUser(ctx.getContentResolver(), defaults.getUri(), s, + UserHandle.USER_CURRENT); + } + + public static ButtonConfig getButtonConfigFromTag(ArrayList<ButtonConfig> configs, String tag) { + if (configs == null || tag == null) { + return null; + } + ButtonConfig config = null; + for (ButtonConfig b : configs) { + if (TextUtils.equals(b.getTag(), tag)) { + config = b; + break; + } + } + return config; + } + + public static ArrayList<ButtonConfig> replaceButtonAtPosition(ArrayList<ButtonConfig> configs, + ButtonConfig button, ConfigMap map) { + if (configs == null || button == null || map == null) { + return null; + } + configs.remove(map.button); + configs.add(map.button, button); + return configs; + } + + public static String removeLastChar(String s) { + if (s == null || s.length() == 0) { + return s; + } + return s.substring(0, s.length() - 1); + } + + public static class ButtonConfig implements Stringable, Parcelable { + public static final int NUM_ELEMENTS = 10; + protected ActionConfig[] configs = new ActionConfig[3]; + private String tag = ActionConstants.EMPTY; + + // internal use only + private ButtonConfig() { + } + + public ButtonConfig(Context ctx) { + configs[ActionConfig.PRIMARY] = new ActionConfig(ctx); + configs[ActionConfig.SECOND] = new ActionConfig(ctx); + configs[ActionConfig.THIRD] = new ActionConfig(ctx); + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public boolean hasCustomIcon() { + return configs[ActionConfig.PRIMARY].hasCustomIcon(); + } + + public void clearCustomIconIconUri() { + configs[ActionConfig.PRIMARY].clearCustomIconIconUri(); + } + + public void setCustomIconUri(String type, String packageName, String iconName) { + configs[ActionConfig.PRIMARY].setCustomIconUri(type, packageName, iconName); + } + + public void setCustomImageUri(Uri uri) { + configs[ActionConfig.PRIMARY].setCustomImageUri(uri); + } + + public Drawable getDefaultIcon(Context ctx) { + return configs[ActionConfig.PRIMARY].getDefaultIcon(ctx); + } + + public Drawable getCurrentIcon(Context ctx) { + return configs[ActionConfig.PRIMARY].getCurrentIcon(ctx); + } + + public boolean isSystemAction() { + return configs[ActionConfig.PRIMARY].isSystemAction(); + } + + public String getSystemActionIconName() { + return configs[ActionConfig.PRIMARY].getSystemActionIconName(); + } + + public ActionConfig getActionConfig(int which) { + if (which < ActionConfig.PRIMARY || which > ActionConfig.THIRD) { + return null; + } + return configs[which]; + } + + public void setActionConfig(ActionConfig config, int which) { + if (which < ActionConfig.PRIMARY || which > ActionConfig.THIRD || config == null) { + return; + } + configs[which] = config; + } + + public static void setButton(Context ctx, ButtonConfig button, String uri, boolean isSecure) { + StringBuilder b = new StringBuilder(); + b.append(button.toDelimitedString()); // this is just beautiful ;D + String s = b.toString(); + if (s.endsWith(ActionConstants.ACTION_DELIMITER)) { + s = removeLastChar(s); // trim final delimiter if need be + } + + if (isSecure) { + Settings.Secure.putStringForUser(ctx.getContentResolver(), uri, s, + UserHandle.USER_CURRENT); + } else { + Settings.System.putStringForUser(ctx.getContentResolver(), uri, s, + UserHandle.USER_CURRENT); + } + } + + public static ButtonConfig getButton(Context ctx, String uri, boolean isSecure) { + if (ctx == null || TextUtils.isEmpty(uri)) { + return null; + } + String config; + if (isSecure) { + config = Settings.Secure.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } else { + config = Settings.System.getStringForUser( + ctx.getContentResolver(), uri, + UserHandle.USER_CURRENT); + } + if (TextUtils.isEmpty(config)) { + return new ButtonConfig(ctx); + } + ArrayList<String> items = new ArrayList<String>(); + items.addAll(Arrays.asList(config.split("\\|"))); + ButtonConfig buttonConfig = new ButtonConfig(ctx); + buttonConfig.fromList(ctx, items.subList(0, NUM_ELEMENTS)); // initialize button from + // list elements + return buttonConfig; + } + + @Override + public String toDelimitedString() { + return tag + ActionConstants.ACTION_DELIMITER + + configs[ActionConfig.PRIMARY].toDelimitedString() + + configs[ActionConfig.SECOND].toDelimitedString() + + configs[ActionConfig.THIRD].toDelimitedString(); + } + + @Override + public void fromList(Context ctx, List<String> items) { + ArrayList<String> buttons = new ArrayList<String>(); + buttons.addAll(items); + tag = buttons.get(0); + + ActionConfig config = new ActionConfig(); + config.fromList(ctx, buttons.subList(1, 4)); + configs[ActionConfig.PRIMARY] = config; + + config = new ActionConfig(); + config.fromList(ctx, buttons.subList(4, 7)); + configs[ActionConfig.SECOND] = config; + + config = new ActionConfig(); + config.fromList(ctx, buttons.subList(7, 10)); + configs[ActionConfig.THIRD] = config; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(tag); + dest.writeParcelable(configs[ActionConfig.PRIMARY], flags); + dest.writeParcelable(configs[ActionConfig.SECOND], flags); + dest.writeParcelable(configs[ActionConfig.THIRD], flags); + } + + public static final Parcelable.Creator<ButtonConfig> CREATOR = new Parcelable.Creator<ButtonConfig>() { + public ButtonConfig createFromParcel(Parcel in) { + return new ButtonConfig(in); + } + + public ButtonConfig[] newArray(int size) { + return new ButtonConfig[size]; + } + }; + + private ButtonConfig(Parcel in) { + tag = in.readString(); + configs[ActionConfig.PRIMARY] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + configs[ActionConfig.SECOND] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + configs[ActionConfig.THIRD] = (ActionConfig) in.readParcelable(Config.ActionConfig.class + .getClassLoader()); + } + } + + public static class ActionConfig implements Stringable, Comparable<ActionConfig>, Parcelable { + public static final int PRIMARY = 0; + public static final int SECOND = 1; + public static final int THIRD = 2; + + private String action = ActionHandler.SYSTEMUI_TASK_NO_ACTION; + private String label = ActionConstants.EMPTY; + private String iconUri = ActionConstants.EMPTY; + + // internal use only + private ActionConfig() { + } + + public static ActionConfig create(Context ctx) { + return new ActionConfig(ctx); + } + + public static ActionConfig create(Context ctx, String action) { + return new ActionConfig(ctx, action); + } + + public static ActionConfig create(Context ctx, String action, String iconUri) { + return new ActionConfig(ctx, action, iconUri); + } + + public ActionConfig(Context ctx) { + label = DUActionUtils.getFriendlyNameForUri(ctx, action); + } + + public ActionConfig(Context ctx, String action) { + this.action = action; + label = DUActionUtils.getFriendlyNameForUri(ctx, action); + } + + public ActionConfig(Context ctx, String action, String iconUri) { + this(ctx, action); + this.iconUri = iconUri; + } + + public String getAction() { + return action; + } + + public String getLabel() { + return label; + } + + public String getIconUri() { + if (!hasCustomIcon()) { + return action; + } else { + return iconUri; + } + } + + public boolean hasCustomIcon() { + return !TextUtils.equals(ActionConstants.EMPTY, iconUri); + } + + public void clearCustomIconIconUri() { + iconUri = ActionConstants.EMPTY; + } + + public boolean isSystemAction() { + return action.startsWith(ActionHandler.SYSTEM_PREFIX); + } + + public String getSystemActionIconName() { + if (action.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(action)) { + return ActionHandler.systemActions[i].mIconRes; + } + } + } + return null; + } + + public void setCustomImageUri(Uri uri) { + iconUri = "image$" + uri.toString(); + } + + public void setCustomIconUri(String type, String packageName, String iconName) { + StringBuilder b = new StringBuilder() + .append(type) + .append("$") + .append(packageName) + .append("$") + .append(iconName); + iconUri = b.toString(); + } + + public Drawable getDefaultIcon(Context ctx) { + return DUActionUtils.getDrawableForAction(ctx, action); + } + + /** + * Returns custom icon (if exists) + * @param ctx app's context + * @return drawable when custom icon exists, null otherwise + */ + public Drawable getCurrentCustomIcon(Context ctx) { + if (hasCustomIcon()) { + List<String> items = Arrays.asList(iconUri.split("\\$")); + String type = items.get(0); + if (type.equals("iconpack") && items.size() == 3) { + String packageName = items.get(1); + String iconName = items.get(2); + return DUActionUtils.getDrawable(ctx, iconName, packageName); + } else if (type.equals("image") && items.size() == 2) { + String uri = items.get(1); + return DUActionUtils.getDrawable(ctx, Uri.parse(uri)); + } + } + return null; + } + + public Drawable getCurrentIcon(Context ctx) { + + Drawable drawable = getCurrentCustomIcon(ctx); + + //If icon doesn't exist (or is not set) fallback to action one + if (drawable == null) { + drawable = DUActionUtils.getDrawableForAction(ctx, action); + } + + return drawable; + + } + + public boolean hasNoAction() { + return TextUtils.equals(action, ActionHandler.SYSTEMUI_TASK_NO_ACTION) + || TextUtils.equals(action, ActionConstants.EMPTY); + } + + public boolean isActionRecents() { + return TextUtils.equals(action, ActionHandler.SYSTEMUI_TASK_RECENTS); + } + + @Override + public int compareTo(ActionConfig another) { + int result = label.toString().compareToIgnoreCase(another.label.toString()); + return result; + } + + @Override + public String toDelimitedString() { + return action + ActionConstants.ACTION_DELIMITER + + label + ActionConstants.ACTION_DELIMITER + + iconUri + ActionConstants.ACTION_DELIMITER; + } + + @Override + public void fromList(Context ctx, List<String> items) { + ArrayList<String> actionStrings = new ArrayList<String>(); + actionStrings.addAll(items); + action = items.get(0); + label = DUActionUtils.getFriendlyNameForUri(ctx, action); + iconUri = items.get(2); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(action); + dest.writeString(label); + dest.writeString(iconUri); + } + + public static final Parcelable.Creator<ActionConfig> CREATOR = new Parcelable.Creator<ActionConfig>() { + public ActionConfig createFromParcel(Parcel in) { + return new ActionConfig(in); + } + + public ActionConfig[] newArray(int size) { + return new ActionConfig[size]; + } + }; + + private ActionConfig(Parcel in) { + action = in.readString(); + label = in.readString(); + iconUri = in.readString(); + } + } +} diff --git a/src/com/android/internal/utils/du/DUActionUtils.java b/src/com/android/internal/utils/du/DUActionUtils.java new file mode 100644 index 0000000..12a72e0 --- /dev/null +++ b/src/com/android/internal/utils/du/DUActionUtils.java @@ -0,0 +1,759 @@ +/* + * Copyright (C) 2014 The TeamEos 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. + * + * Helper functions mostly for device configuration and some utilities + * including a fun ViewGroup crawler and dpi conversion + * + */ + +package com.android.internal.utils.du; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +//import android.content.res.ThemeConfig; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.net.ConnectivityManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.ServiceManager; +import android.os.SystemProperties; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Display; +import android.view.IWindowManager; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.util.ArrayList; + +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.utils.du.ActionConstants.Defaults; +import com.android.internal.utils.du.Config.ActionConfig; +import com.android.internal.utils.du.Config.ButtonConfig; + +public final class DUActionUtils { + public static final String ANDROIDNS = "http://schemas.android.com/apk/res/android"; + public static final String PACKAGE_SYSTEMUI = "com.android.systemui"; + public static final String PACKAGE_ANDROID = "android"; + public static final String FORMAT_NONE = "none"; + public static final String FORMAT_FLOAT = "float"; + + public static final String ID = "id"; + public static final String DIMEN = "dimen"; + public static final String DIMEN_PIXEL = "dimen_pixel"; + public static final String FLOAT = "float"; + public static final String INT = "integer"; + public static final String DRAWABLE = "drawable"; + public static final String COLOR = "color"; + public static final String BOOL = "bool"; + public static final String STRING = "string"; + public static final String ANIM = "anim"; + + public static final int DUI_ICON_MAX_WIDTH = 512; + public static final int DUI_ICON_MAX_HEIGHT = 512; + + // 10 inch tablets + public static boolean isXLargeScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + // 7 inch "phablets" i.e. grouper + public static boolean isLargeScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + // normal phones + public static boolean isNormalScreen() { + int screenLayout = Resources.getSystem().getConfiguration().screenLayout & + Configuration.SCREENLAYOUT_SIZE_MASK; + return screenLayout == Configuration.SCREENLAYOUT_SIZE_NORMAL; + } + + public static boolean isLandscape(Context context) { + return Configuration.ORIENTATION_LANDSCAPE + == context.getResources().getConfiguration().orientation; + } + + public static boolean navigationBarCanMove() { + return Resources.getSystem().getConfiguration().smallestScreenWidthDp < 600; + } + + public static boolean hasNavbarByDefault(Context context) { + boolean needsNav = (Boolean)getValue(context, "config_showNavigationBar", BOOL, PACKAGE_ANDROID); + String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); + if ("1".equals(navBarOverride)) { + needsNav = false; + } else if ("0".equals(navBarOverride)) { + needsNav = true; + } + return needsNav; + } + + public static boolean deviceSupportsLte(Context ctx) { + final TelephonyManager tm = (TelephonyManager) + ctx.getSystemService(Context.TELEPHONY_SERVICE); +// return (tm.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) +// || tm.getLteOnGsmMode() != 0; + return tm.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE; + } + + public static boolean deviceSupportsDdsSupported(Context context) { + TelephonyManager tm = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + return tm.isMultiSimEnabled() + && tm.getMultiSimConfiguration() == TelephonyManager.MultiSimVariants.DSDA; + } + + public static boolean deviceSupportsMobileData(Context ctx) { + ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService( + Context.CONNECTIVITY_SERVICE); + return cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); + } + + public static boolean deviceSupportsBluetooth() { + return BluetoothAdapter.getDefaultAdapter() != null; + } + + public static boolean deviceSupportsNfc(Context context) { + PackageManager packageManager = context.getPackageManager(); + return packageManager.hasSystemFeature(PackageManager.FEATURE_NFC); + } + + public static boolean deviceSupportsFlashLight(Context context) { + CameraManager cameraManager = (CameraManager) context.getSystemService( + Context.CAMERA_SERVICE); + try { + String[] ids = cameraManager.getCameraIdList(); + for (String id : ids) { + CameraCharacteristics c = cameraManager.getCameraCharacteristics(id); + Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING); + if (flashAvailable != null + && flashAvailable + && lensFacing != null + && lensFacing == CameraCharacteristics.LENS_FACING_BACK) { + return true; + } + } + } catch (CameraAccessException | AssertionError e) { + // Ignore + } + return false; + } + + public static boolean deviceSupportsCompass(Context context) { + SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + return sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null + && sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null; + } + + public static boolean deviceSupportsDoze(Context context) { + String name = (String) getValue(context, "config_dozeComponent", + STRING, PACKAGE_ANDROID); + return !TextUtils.isEmpty(name); + } + + /** + * This method converts dp unit to equivalent pixels, depending on device + * density. + * + * @param dp A value in dp (density independent pixels) unit. Which we need + * to convert into pixels + * @param context Context to get resources and device specific display + * metrics + * @return A float value to represent px equivalent to dp depending on + * device density + */ + public static float convertDpToPixel(float dp, Context context) { + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + float px = dp * (metrics.densityDpi / 160f); + return px; + } + + public static int ConvertDpToPixelAsInt(float dp, Context context) { + float px = convertDpToPixel(dp, context); + if (px < 1) + px = 1; + return Math.round(px); + } + + public static int ConvertDpToPixelAsInt(int dp, Context context) { + float px = convertDpToPixel((float) dp, context); + if (px < 1) + px = 1; + return Math.round(px); + } + + /** + * This method converts device specific pixels to density independent + * pixels. + * + * @param px A value in px (pixels) unit. Which we need to convert into db + * @param context Context to get resources and device specific display + * metrics + * @return A float value to represent dp equivalent to px value + */ + public static float convertPixelsToDp(float px, Context context) { + Resources resources = context.getResources(); + DisplayMetrics metrics = resources.getDisplayMetrics(); + float dp = px / (metrics.densityDpi / 160f); + return dp; + } + + public static int dpToPx(Context context, int dp) { + return (int) ((dp * context.getResources().getDisplayMetrics().density) + 0.5); + } + + public static int pxToDp(Context context, int px) { + return (int) ((px / context.getResources().getDisplayMetrics().density) + 0.5); + } + + /* utility to iterate a viewgroup and return a list of child views */ + public static ArrayList<View> getAllChildren(View v) { + + if (!(v instanceof ViewGroup)) { + ArrayList<View> viewArrayList = new ArrayList<View>(); + viewArrayList.add(v); + return viewArrayList; + } + + ArrayList<View> result = new ArrayList<View>(); + + ViewGroup vg = (ViewGroup) v; + for (int i = 0; i < vg.getChildCount(); i++) { + + View child = vg.getChildAt(i); + + ArrayList<View> viewArrayList = new ArrayList<View>(); + viewArrayList.add(v); + viewArrayList.addAll(getAllChildren(child)); + + result.addAll(viewArrayList); + } + return result; + } + + /* utility to iterate a viewgroup and return a list of child views of type */ + public static <T extends View> ArrayList<T> getAllChildren(View root, Class<T> returnType) { + if (!(root instanceof ViewGroup)) { + ArrayList<T> viewArrayList = new ArrayList<T>(); + try { + viewArrayList.add(returnType.cast(root)); + } catch (Exception e) { + // handle all exceptions the same and silently fail + } + return viewArrayList; + } + ArrayList<T> result = new ArrayList<T>(); + ViewGroup vg = (ViewGroup) root; + for (int i = 0; i < vg.getChildCount(); i++) { + View child = vg.getChildAt(i); + ArrayList<T> viewArrayList = new ArrayList<T>(); + try { + viewArrayList.add(returnType.cast(root)); + } catch (Exception e) { + // handle all exceptions the same and silently fail + } + viewArrayList.addAll(getAllChildren(child, returnType)); + result.addAll(viewArrayList); + } + return result; + } + + public static void resolveAndUpdateButtonActions(Context ctx, Defaults defaults) { + if (ctx == null || defaults == null) { + return; + } + boolean configChanged = false; + final PackageManager pm = ctx.getPackageManager(); + ArrayList<ButtonConfig> configs = Config.getConfig(ctx, defaults); + ArrayList<ButtonConfig> buttonsToChange = new ArrayList<ButtonConfig>(); + buttonsToChange.addAll(configs); + for (int h = 0; h < configs.size(); h++) { + ButtonConfig button = configs.get(h); + for (int i = 0; i < 3; i++) { + ActionConfig action = button.getActionConfig(i); + final String task = action.getAction(); + if (task.startsWith(ActionHandler.SYSTEM_PREFIX)) { + continue; + } + String resolvedName = getFriendlyNameForUri(ctx, task); + if (resolvedName == null || TextUtils.equals(resolvedName, task)) { + // if resolved name is null or the full raw intent string is + // returned, we were unable to resolve + configChanged = true; + ActionConfig newAction = new ActionConfig(ctx, + ActionHandler.SYSTEMUI_TASK_NO_ACTION, action.getIconUri()); + ButtonConfig newButton = buttonsToChange.get(h); + newButton.setActionConfig(newAction, i); + buttonsToChange.remove(h); + buttonsToChange.add(h, newButton); + } + } + } + if (configChanged) { + Config.setConfig(ctx, defaults, buttonsToChange); + } + } + + public static Intent getIntent(String uri) { + if (uri == null || uri.startsWith(ActionHandler.SYSTEM_PREFIX)) { + return null; + } + + Intent intent = null; + try { + intent = Intent.parseUri(uri, 0); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return intent; + } + + public static Object getValue(Context context, String resName, String resType, String pkg) { + return getValue(context, resName, resType, null, pkg); + } + + public static Object getValue(Context context, String resName, String resType, String format, + String pkg) { + Resources res = getResourcesForPackage(context, pkg); + String tmp; + if (resType.equals(DIMEN_PIXEL)) { + tmp = DIMEN; + } else { + tmp = resType; + } + int id = res.getIdentifier(resName, tmp, pkg); + if (format != null) { // standard res + TypedValue typedVal = new TypedValue(); + res.getValue(id, typedVal, true); + if (format.equals(FORMAT_FLOAT)) { + return Float.valueOf(typedVal.getFloat()); + } + } else { // typed values + if (resType.equals(ID)) { + return Integer.valueOf(id); + } else if (resType.equals(DIMEN)) { + return Float.valueOf(res.getDimension(id)); + } else if (resType.equals(DIMEN_PIXEL)) { + return Integer.valueOf(res.getDimensionPixelSize(id)); + } else if (resType.equals(FLOAT)) { + return Float.valueOf(res.getFloat(id)); + } else if (resType.equals(INT)) { + return Integer.valueOf(res.getInteger(id)); + } else if (resType.equals(COLOR)) { + int rawColor = res.getColor(id); + return Integer.valueOf(Color.argb(Color.alpha(rawColor), Color.red(rawColor), + Color.green(rawColor), Color.blue(rawColor))); + } else if (resType.equals(BOOL)) { + return Boolean.valueOf(res.getBoolean(id)); + } else if (resType.equals(STRING)) { + return String.valueOf(res.getString(id)); + } else if (resType.equals(DRAWABLE)) { + return getDrawable(context, resName, pkg); + } + } + return null; + } + + public static void putValue(String key, Object val, String type, Bundle b) { + if (type.equals(ID) || type.equals(DIMEN_PIXEL) || type.equals(INT) || type.equals(COLOR)) { + b.putInt(key, (Integer) val); + } else if (type.equals(FLOAT) || type.equals(DIMEN)) { + b.putFloat(key, (Float) val); + } else if (type.equals(BOOL)) { + b.putBoolean(key, (Boolean) val); + } else if (type.equals(STRING)) { + b.putString(key, (String) val); + } + } + + public static int getIdentifier(Context context, String resName, String resType, String pkg) { + try { + Resources res = context.getPackageManager() + .getResourcesForApplication(pkg); + int ident = res.getIdentifier(resName, resType, pkg); + return ident; + } catch (Exception e) { + return -1; + } + } + + public static String getString(Context context, String resName, String pkg) { + return (String) getValue(context, resName, STRING, null, pkg); + } + + public static boolean getBoolean(Context context, String resName, String pkg) { + return (Boolean) getValue(context, resName, BOOL, null, pkg); + } + + public static int getInt(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, INT, null, pkg); + } + + public static int getColor(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, COLOR, null, pkg); + } + + public static int getId(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, ID, null, pkg); + } + + public static float getDimen(Context context, String resName, String pkg) { + return (Float) getValue(context, resName, DIMEN, null, pkg); + } + + public static int getDimenPixelSize(Context context, String resName, String pkg) { + return (Integer) getValue(context, resName, DIMEN_PIXEL, null, pkg); + } + + public static Drawable getDrawable(Context context, String drawableName, String pkg) { + return getDrawable(getResourcesForPackage(context, pkg), drawableName, pkg); + } + + public static Drawable getDrawable(Context context, Uri uri) { + //set inputs here so we can clean up them in the finally + InputStream inputStream = null; + + try { + //get the inputstream + inputStream = context.getContentResolver().openInputStream(uri); + + //get available bitmapfactory options + BitmapFactory.Options options = new BitmapFactory.Options(); + //query the bitmap to decode the stream but don't allocate pixels in memory yet + options.inJustDecodeBounds = true; + //decode the bitmap with calculated bounds + Bitmap b1 = BitmapFactory.decodeStream(inputStream, null, options); + //get raw height and width of the bitmap + int rawHeight = options.outHeight; + int rawWidth = options.outWidth; + + //check if the bitmap is big and we need to scale the quality to take less memory + options.inSampleSize = calculateInSampleSize(options, rawHeight, rawWidth); + + //We need to close and load again the inputstream to avoid null + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + inputStream = context.getContentResolver().openInputStream(uri); + + //decode the stream again, with the calculated SampleSize option, + //and allocate the memory. Also add some metrics options to take a proper density + options.inJustDecodeBounds = false; + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + options.inScreenDensity = metrics.densityDpi; + options.inTargetDensity = metrics.densityDpi; + options.inDensity = DisplayMetrics.DENSITY_DEFAULT; + b1 = BitmapFactory.decodeStream(inputStream, null, options); + return new BitmapDrawable(context.getResources(), b1); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + //clean up the system resources + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + //Automate the quality scaling process + public static int calculateInSampleSize(BitmapFactory.Options options, int height, int width) { + //set default inSampleSize scale factor (no scaling) + int inSampleSize = 1; + + //if img size is in 257-512 range, sample scale factor will be 4x + if (height > 256 || width > 256) { + inSampleSize = 4; + return inSampleSize; + //if img size is in 129-256 range, sample scale factor will be 2x + } else if (height > 128 || width > 128) { + inSampleSize = 2; + return inSampleSize; + } + //if img size is in 0-128 range, no need to scale it + return inSampleSize; + } + + /** + * Screen images based on desired dimensions before fully decoding + * + *@param ctx Calling context + *@param uri Image uri + *@param maxWidth maximum allowed image width + *@param maxHeight maximum allowed image height + */ + public static boolean isBitmapAllowedSize(Context ctx, Uri uri, int maxWidth, int maxHeight) { + InputStream inputStream = null; + try { + inputStream = ctx.getContentResolver().openInputStream(uri); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(inputStream, null, options); + if (options.outWidth <= maxWidth && options.outHeight <= maxHeight) { + return true; + } + } catch (Exception e) { + return false; + } finally { + try { + inputStream.close(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + return false; + } + + public static Drawable getDrawableFromComponent(PackageManager pm, String activity) { + Drawable d = null; + try { + Intent intent = Intent.parseUri(activity, 0); + ActivityInfo info = intent.resolveActivityInfo(pm, + PackageManager.GET_ACTIVITIES); + if (info != null) { + d = info.loadIcon(pm); + } + } catch (Exception e) { + e.printStackTrace(); + } + return d; + } + + public static String getFriendlyActivityName(PackageManager pm, Intent intent, + boolean labelOnly) { + ActivityInfo ai = intent.resolveActivityInfo(pm, PackageManager.GET_ACTIVITIES); + String friendlyName = null; + if (ai != null) { + friendlyName = ai.loadLabel(pm).toString(); + if (friendlyName == null && !labelOnly) { + friendlyName = ai.name; + } + } + return friendlyName != null || labelOnly ? friendlyName : intent.toUri(0); + } + + public static String getFriendlyShortcutName(PackageManager pm, Intent intent) { + String activityName = getFriendlyActivityName(pm, intent, true); + String name = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); + + if (activityName != null && name != null) { + return activityName + ": " + name; + } + return name != null ? name : intent.toUri(0); + } + + public static String getFriendlyNameForUri(Context ctx, String uri) { + if (uri == null) { + return null; + } + if (uri.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(uri)) { + return getString(ctx, ActionHandler.systemActions[i].mLabelRes, + ActionHandler.systemActions[i].mResPackage); + } + } + } else { + try { + Intent intent = Intent.parseUri(uri, 0); + if (Intent.ACTION_MAIN.equals(intent.getAction())) { + return getFriendlyActivityName(ctx.getPackageManager(), intent, false); + } + return getFriendlyShortcutName(ctx.getPackageManager(), intent); + } catch (URISyntaxException e) { + } + } + return uri; + } + + /** + * + * @param Target package resources + * @param drawableName + * @param Target package name + * @return the drawable if found, otherwise fall back to a green android guy + */ + public static Drawable getDrawable(Resources res, String drawableName, String pkg) { + try { + int resId = res.getIdentifier(drawableName, DRAWABLE, pkg); + Drawable icon = ImageHelper.getVector(res, resId, false); + if (icon == null) { + icon = res.getDrawable(resId); + } + return icon; + } catch (Exception e) { + return res.getDrawable( + com.android.internal.R.drawable.sym_def_app_icon); + } + } + + /** + * + * @param Target package resources + * @param drawableName + * @param Target package name + * @return the drawable if found, null otherwise. Useful for testing if a drawable is found + * in a theme overlay + */ + private static Drawable getMaybeNullDrawable(Resources res, String drawableName, String pkg) { + try { + int resId = res.getIdentifier(drawableName, DRAWABLE, pkg); + Drawable icon = ImageHelper.getVector(res, resId, false); + if (icon == null) { + icon = res.getDrawable(resId); + } + return icon; + } catch (Exception e) { + return null; + } + } + + public static Resources getResourcesForPackage(Context ctx, String pkg) { + try { + Resources res = ctx.getPackageManager() + .getResourcesForApplication(pkg); + return res; + } catch (Exception e) { + return ctx.getResources(); + } + } + + /** + * + * @param Context of the calling package + * @param the action we want a drawable for + * @return if a system action drawable is requested, we try to get the drawable + * from any current navigation overlay. if no overlay is found, get it + * from SystemUI. Return a component drawable if not a system action + */ + public static Drawable getDrawableForAction(Context context, String action) { + Drawable d = null; + + // this null check is probably no-op but let's be safe anyways + if (action == null || context == null) { + return d; + } + if (action.startsWith(ActionHandler.SYSTEM_PREFIX)) { + for (int i = 0; i < ActionHandler.systemActions.length; i++) { + if (ActionHandler.systemActions[i].mAction.equals(action)) { + // should always be SystemUI + String packageName = ActionHandler.systemActions[i].mResPackage; + Resources res = getResourcesForPackage(context, packageName); + String iconName = ActionHandler.systemActions[i].mIconRes; + d = getNavbarThemedDrawable(context, res, iconName); + if (d == null) { + d = getDrawable(res, iconName, packageName); + } + } + } + } else { + d = getDrawableFromComponent(context.getPackageManager(), action); + } + return d; + } + + /** + * + * @param calling package context, usually Settings for the custom action list adapter + * @param target package resources, usually SystemUI + * @param drawableName + * @return a navigation bar overlay themed action drawable if available, otherwise + * return drawable from SystemUI resources + */ + public static Drawable getNavbarThemedDrawable(Context context, Resources defRes, + String drawableName) { + if (context == null || defRes == null || drawableName == null) + return null; + + // TODO: turn on cmte support when it comes back + return getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); +/* + ThemeConfig themeConfig = context.getResources().getConfiguration().themeConfig; + + Drawable d = null; + if (themeConfig != null) { + try { + final String navbarThemePkgName = themeConfig.getOverlayForNavBar(); + final String sysuiThemePkgName = themeConfig.getOverlayForStatusBar(); + // Check if the same theme is applied for systemui, if so we can skip this + if (navbarThemePkgName != null && !navbarThemePkgName.equals(sysuiThemePkgName)) { + // Navbar theme and SystemUI (statusbar) theme packages are different + // But we can't assume navbar package has our drawable, so try navbar theme + // package first. If we fail, try the systemui (statusbar) package + // if we still fail, fall back to default package resource + Resources res = context.getPackageManager().getThemedResourcesForApplication( + PACKAGE_SYSTEMUI, navbarThemePkgName); + d = getMaybeNullDrawable(res, drawableName, PACKAGE_SYSTEMUI); + if (d == null) { + // drawable not found in overlay, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } else { + // no navbar overlay present, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } catch (PackageManager.NameNotFoundException e) { + // error thrown (unlikely), get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + } + if (d == null) { + // theme config likely null, get from default SystemUI res + d = getDrawable(defRes, drawableName, PACKAGE_SYSTEMUI); + } + return d; + */ + } +} diff --git a/src/com/android/internal/utils/du/DUPackageMonitor.java b/src/com/android/internal/utils/du/DUPackageMonitor.java new file mode 100644 index 0000000..e922986 --- /dev/null +++ b/src/com/android/internal/utils/du/DUPackageMonitor.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015-2016 The TeamEos Project + * + * Author: Randall Rushing <bigrushdog@teameos.org> + * + * 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. + * + * A simplified package monitor class with easy-to-use callbacks when a + * state changes. We register the receiver on background thread but post events + * to the UI thread + */ + +package com.android.internal.utils.du; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; + +import java.util.ArrayList; + +public class DUPackageMonitor extends com.android.internal.content.PackageMonitor { + private static final int MSG_PACKAGE_ADDED = 1; + private static final int MSG_PACKAGE_REMOVED = 2; + private static final int MSG_PACKAGE_CHANGED = 3; + + public static enum PackageState { + PACKAGE_REMOVED, + PACKAGE_ADDED, + PACKAGE_CHANGED + } + + public interface PackageChangedListener { + public void onPackageChanged(String pkg, PackageState state); + } + + private Handler mHandler; + + private ArrayList<PackageChangedListener> mListeners = new ArrayList<PackageChangedListener>(); + + public void register(Context context, Handler foreground) { + register(context, null, null, true); + + mHandler = new Handler(foreground.getLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_PACKAGE_ADDED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, PackageState.PACKAGE_ADDED); + } + break; + case MSG_PACKAGE_REMOVED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, + PackageState.PACKAGE_REMOVED); + } + break; + case MSG_PACKAGE_CHANGED: + for (PackageChangedListener listener : mListeners) { + listener.onPackageChanged((String) msg.obj, + PackageState.PACKAGE_CHANGED); + } + break; + } + } + }; + } + + public void addListener(PackageChangedListener listener) { + if (listener != null) { + mListeners.add(listener); + } + } + + public void removeListener(PackageChangedListener listener) { + if (listener != null) { + mListeners.remove(listener); + } + } + + /** + * Called when a package is really added (and not replaced). + */ + public void onPackageAdded(String packageName, int uid) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_ADDED, packageName); + mHandler.sendMessage(msg); + } + + /** + * Called when a package is really removed (and not replaced). + */ + public void onPackageRemoved(String packageName, int uid) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_REMOVED, packageName); + mHandler.sendMessage(msg); + } + + /** + * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED Intent.ACTION_PACKAGE_CHANGED} + * being received, informing you of changes to the enabled/disabled state of components in a + * package and/or of the overall package. + * + * @param packageName The name of the package that is changing. + * @param uid The user ID the package runs under. + * @param components Any components in the package that are changing. If the overall package is + * changing, this will contain an entry of the package name itself. + * @return Return true to indicate you care about this change, which will result in + * {@link #onSomePackagesChanged()} being called later. If you return false, no further + * callbacks will happen about this change. The default implementation returns true if + * this is a change to the entire package. + */ + public boolean onPackageChanged(String packageName, int uid, String[] components) { + Message msg = mHandler.obtainMessage(MSG_PACKAGE_CHANGED, packageName); + mHandler.sendMessage(msg); + return super.onPackageChanged(packageName, uid, components); + } +} diff --git a/src/com/android/internal/utils/du/DUSystemReceiver.java b/src/com/android/internal/utils/du/DUSystemReceiver.java new file mode 100644 index 0000000..1df571c --- /dev/null +++ b/src/com/android/internal/utils/du/DUSystemReceiver.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015-2016 The TeamEos Project + * + * Author: Randall Rushing <bigrushdog@teameos.org> + * + * 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. + * + * A BroadcastReceiver that filters out broadcasts from non-system + * related processes. Subclasses can allow additional broadcasting + * packages by overriding onExemptBroadcast(Context context, String packageName) + */ + +package com.android.internal.utils.du; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public abstract class DUSystemReceiver extends BroadcastReceiver { + + protected abstract void onSecureReceive(Context context, Intent intent); + + protected boolean onExemptBroadcast(Context context, String packageName) { + return false; + } + + @Override + public final void onReceive(Context context, Intent intent) { + if (context == null || intent == null) { + return; + } + if (isBroadcastFromSystem(context)) { + onSecureReceive(context, intent); + } + } + + private boolean isBroadcastFromSystem(Context context) { + String packageName = context.getPackageName(); + if (packageName == null + && android.os.Process.SYSTEM_UID == context.getApplicationInfo().uid) { + packageName = "android"; + } + if (packageName == null) { + return false; + } + if (packageName.equals("com.android.systemui") + || packageName.equals("com.android.keyguard") + || packageName.equals("com.android.settings") + || packageName.equals("android") + || context.getApplicationInfo().uid == android.os.Process.SYSTEM_UID) { + return true; + } + if (onExemptBroadcast(context, packageName)) { + return true; + } + return false; + } +} diff --git a/src/com/android/internal/utils/du/ImageHelper.java b/src/com/android/internal/utils/du/ImageHelper.java new file mode 100644 index 0000000..75d2605 --- /dev/null +++ b/src/com/android/internal/utils/du/ImageHelper.java @@ -0,0 +1,299 @@ +/* +* Copyright (C) 2013 SlimRoms Project +* Copyright (C) 2015 TeamEos Project +* Copyright (C) 2015-2016 The DirtyUnicorns 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 com.android.internal.utils.du; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.util.Xml; + +public class ImageHelper { + private static final int VECTOR_WIDTH = 512; + private static final int VECTOR_HEIGHT = 512; + + public static Drawable getColoredDrawable(Drawable d, int color) { + if (d == null) { + return null; + } + if (d instanceof VectorDrawable) { + d.setTint(color); + return d; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return new BitmapDrawable(grayscaleBitmap); + } + + public static Bitmap drawableToBitmap (Drawable drawable) { + if (drawable == null) { + return null; + } else if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return bitmap; + } + + public static Bitmap getColoredBitmap(Drawable d, int color) { + if (d == null) { + return null; + } + Bitmap colorBitmap = ((BitmapDrawable) d).getBitmap(); + Bitmap grayscaleBitmap = toGrayscale(colorBitmap); + Paint pp = new Paint(); + pp.setAntiAlias(true); + PorterDuffColorFilter frontFilter = + new PorterDuffColorFilter(color, Mode.MULTIPLY); + pp.setColorFilter(frontFilter); + Canvas cc = new Canvas(grayscaleBitmap); + final Rect rect = new Rect(0, 0, grayscaleBitmap.getWidth(), grayscaleBitmap.getHeight()); + cc.drawBitmap(grayscaleBitmap, rect, rect, pp); + return grayscaleBitmap; + } + + private static Bitmap toGrayscale(Bitmap bmpOriginal) { + int width, height; + height = bmpOriginal.getHeight(); + width = bmpOriginal.getWidth(); + + Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(bmpGrayscale); + Paint paint = new Paint(); + paint.setAntiAlias(true); + ColorMatrix cm = new ColorMatrix(); + final Rect rect = new Rect(0, 0, width, height); + cm.setSaturation(0); + + ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm); + paint.setColorFilter(f); + c.drawBitmap(bmpOriginal, rect, rect, paint); + return bmpGrayscale; + } + + public static Drawable resize(Context context, Drawable image, int size) { + if (image == null || context == null) { + return null; + } + if (image instanceof VectorDrawable) { + return image; + } else { + int newSize = DUActionUtils.dpToPx(context, size); + Bitmap bitmap = ((BitmapDrawable) image).getBitmap(); + Bitmap scaledBitmap = Bitmap.createBitmap(newSize, newSize, Config.ARGB_8888); + + float ratioX = newSize / (float) bitmap.getWidth(); + float ratioY = newSize / (float) bitmap.getHeight(); + float middleX = newSize / 2.0f; + float middleY = newSize / 2.0f; + + final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + paint.setAntiAlias(true); + + Matrix scaleMatrix = new Matrix(); + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY); + + Canvas canvas = new Canvas(scaledBitmap); + canvas.setMatrix(scaleMatrix); + canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, + middleY - bitmap.getHeight() / 2, paint); + return new BitmapDrawable(context.getResources(), scaledBitmap); + } + } + + public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + final int color = 0xff424242; + final Paint paint = new Paint(); + final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); + final RectF rectF = new RectF(rect); + final float roundPx = 24; + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRoundRect(rectF, roundPx, roundPx, paint); + paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, paint); + return output; + } + + public static Bitmap getCircleBitmap(Bitmap bitmap) { + if (bitmap == null) { + return null; + } + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + Bitmap output = Bitmap.createBitmap(width, height, + Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(shader); + + canvas.drawCircle(width/2, height/2, width/2, paint); + + return output; + } + + public static Drawable getVector(Resources res, int resId) { + return getVector(res, resId, 0, 0, false); + } + + public static Drawable getVector(Resources res, int resId, int width, int height) { + return getVector(res, resId, width, height, false); + } + + public static Drawable getVector(Resources res, int resId, boolean toBitmapDrawable) { + return getVector(res, resId, 0, 0, toBitmapDrawable); + } + + public static Drawable getVector(Resources res, int resId, int width, int height, + boolean toBitmapDrawable) { + if (width <= 0) { + width = VECTOR_WIDTH; + } + if (height <= 0) { + width = VECTOR_HEIGHT; + } + + VectorDrawable vectorDrawable = new VectorDrawable(); + vectorDrawable.setBounds(0, 0, width, height); + try { + XmlPullParser parser = res.getXml(resId); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + + if (type != XmlPullParser.START_TAG) { +// Log.e("ImageHelper VectorLoader", "No start tag found"); + } + + vectorDrawable.inflate(res, parser, attrs); + + if (!toBitmapDrawable) { + return vectorDrawable; + } + + return new BitmapDrawable(res, drawableToBitmap(vectorDrawable)); + } catch (Exception e) { +// Log.e("ImageHelper VectorLoader", "Error loading resource ID " + String.valueOf(resId) + " Try loading as a non vector"); + return null; + } + } + + /** + * @param context callers context + * @param uri Uri to handle + * @return A bitmap from the requested uri + * @throws IOException + * + * @Credit: StackOverflow + * http://stackoverflow.com/questions/35909008/pick-image + * -from-gallery-or-google-photos-failing + */ + public static Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException { + if (context == null || uri == null) { + return null; + } + ParcelFileDescriptor parcelFileDescriptor = + context.getContentResolver().openFileDescriptor(uri, "r"); + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } + + /** + * @param storageDir Desired location in storage as a File + * @param fileName Name of bitmap file to store + * @param bitmap the bitmap to store + * @return the Uri of the bitmap + */ + public static Uri addBitmapToStorage(File storageDir, String fileName, Bitmap bitmap) { + if (storageDir == null || fileName == null || bitmap == null) { + return null; + } + File imageFile = new File(storageDir, fileName); + try { + FileOutputStream fos = new FileOutputStream(imageFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + } catch (Exception e) { + return null; + } + return Uri.fromFile(imageFile); + } +} diff --git a/src/com/android/internal/utils/du/UserContentObserver.java b/src/com/android/internal/utils/du/UserContentObserver.java new file mode 100644 index 0000000..6d77b29 --- /dev/null +++ b/src/com/android/internal/utils/du/UserContentObserver.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 com.android.internal.utils.du; + +import android.app.ActivityManagerNative; +import android.app.IUserSwitchObserver; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.RemoteException; +import android.util.Log; + +/** + * Simple extension of ContentObserver that also listens for user switch events to call update + */ +public abstract class UserContentObserver extends ContentObserver { + private static final String TAG = "UserContentObserver"; + + private Runnable mUpdateRunnable; + + private IUserSwitchObserver mUserSwitchObserver = new IUserSwitchObserver.Stub() { + @Override + public void onUserSwitching(int newUserId, IRemoteCallback reply) { + } + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + mHandler.post(mUpdateRunnable); + } + @Override + public void onForegroundProfileSwitch(int newProfileId) { + } + @Override + public void onLockedBootComplete(int val) { + } + }; + + private Handler mHandler; + + public UserContentObserver(Handler handler) { + super(handler); + mHandler = handler; + mUpdateRunnable = new Runnable() { + @Override + public void run() { + update(); + } + }; + } + + protected void observe() { + try { + ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchObserver, TAG); + } catch (RemoteException e) { + Log.w(TAG, "Unable to register user switch observer!", e); + } + } + + protected void unobserve() { + try { + mHandler.removeCallbacks(mUpdateRunnable); + ActivityManagerNative.getDefault().unregisterUserSwitchObserver(mUserSwitchObserver); + } catch (RemoteException e) { + Log.w(TAG, "Unable to unregister user switch observer!", e); + } + } + + protected abstract void update(); + + @Override + public void onChange(boolean selfChange) { + update(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + update(); + } +} |
