├── assets └── xposed_init ├── .gitignore ├── ic_launcher-web.png ├── res ├── drawable-hdpi │ ├── ic_launcher.png │ └── heads_up_window_bg.9.png ├── drawable-mdpi │ ├── ic_launcher.png │ └── heads_up_window_bg.9.png ├── drawable-xhdpi │ ├── ic_launcher.png │ └── heads_up_window_bg.9.png ├── drawable-xxhdpi │ ├── ic_launcher.png │ └── heads_up_window_bg.9.png ├── values-v11 │ └── styles.xml ├── values │ ├── entries.xml │ ├── styles.xml │ └── strings.xml ├── values-v14 │ └── styles.xml ├── layout │ └── blacklist_item.xml └── xml │ └── preferences.xml ├── .classpath ├── project.properties ├── proguard-project.txt ├── .project ├── AndroidManifest.xml └── src └── com └── mohammadag └── headsupenabler ├── SettingsActivity.java ├── SettingsHelper.java ├── Blacklist.java └── XposedMod.java /assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.mohammadag.headsupenabler.XposedMod -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | bin/ 3 | gen/ 4 | out/ 5 | *.apk 6 | *.iml 7 | -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/ic_launcher-web.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-hdpi/heads_up_window_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-hdpi/heads_up_window_bg.9.png -------------------------------------------------------------------------------- /res/drawable-mdpi/heads_up_window_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-mdpi/heads_up_window_bg.9.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/heads_up_window_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-xhdpi/heads_up_window_bg.9.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/heads_up_window_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ns130291/Xposed-Heads-Up-Notifications/master/res/drawable-xxhdpi/heads_up_window_bg.9.png -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/values/entries.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @string/pref_blacklist_title 5 | @string/pref_whitelist_title 6 | 7 | 8 | blacklist 9 | whitelist 10 | 11 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | HeadsUpEnabler 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /res/layout/blacklist_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 25 | 26 | 36 | 37 | 47 | 48 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Heads Up Notifications 4 | Copyright © 2014 – MohammadAG / GermainZ\nVisit support thread 5 | Loading… 6 | Notification delay 7 | Delay (in ms) before the Heads Up notification 8 | disappears. A reboot is required to apply this option 9 | Notification filtering 10 | Blacklist 11 | Ignore notifications from certain apps 12 | Whitelist 13 | Only show notifications from certain apps 14 | Ongoing notifications 15 | Enable heads up for ongoing (persistent) notifications 16 | Only when fullscreen 17 | Enable Heads Up only when in fullscreen apps 18 | Hide low priority notifications 19 | Hides notifications with low and minimum priority 20 | Only when unlocked 21 | Enable Heads Up only when phone is unlocked 22 | Remove Heads Up padding 23 | Remove the padding on the sides of the Heads Up notification, 24 | and have it show on top of the status bar. A reboot is required to apply this option 25 | Application icon 26 | 27 | -------------------------------------------------------------------------------- /src/com/mohammadag/headsupenabler/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.mohammadag.headsupenabler; 2 | 3 | import android.os.Bundle; 4 | import android.preference.ListPreference; 5 | import android.preference.Preference; 6 | import android.preference.PreferenceActivity; 7 | 8 | public class SettingsActivity extends PreferenceActivity { 9 | @SuppressWarnings("deprecation") 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | getPreferenceManager().setSharedPreferencesMode(MODE_WORLD_READABLE); 14 | addPreferencesFromResource(R.xml.preferences); 15 | 16 | final ListPreference notificationFilterTypePref = (ListPreference) findPreference("notification_filter_type"); 17 | final Preference notificationList = findPreference("notification_list"); 18 | 19 | if (notificationFilterTypePref.getValue().equals("blacklist")) { 20 | notificationFilterTypePref.setSummary(R.string.pref_blacklist_title); 21 | notificationList.setTitle(R.string.pref_blacklist_title); 22 | notificationList.setSummary(R.string.pref_blacklist_summary); 23 | } else { 24 | notificationFilterTypePref.setSummary(R.string.pref_whitelist_title); 25 | notificationList.setTitle(R.string.pref_whitelist_title); 26 | notificationList.setSummary(R.string.pref_whitelist_summary); 27 | } 28 | 29 | notificationFilterTypePref.setOnPreferenceChangeListener(new ListPreference.OnPreferenceChangeListener() { 30 | @Override 31 | public boolean onPreferenceChange(Preference preference, Object newValue) { 32 | if (newValue.equals("blacklist")) { 33 | notificationFilterTypePref.setSummary(R.string.pref_blacklist_title); 34 | notificationList.setTitle(R.string.pref_blacklist_title); 35 | notificationList.setSummary(R.string.pref_blacklist_summary); 36 | } else { 37 | notificationFilterTypePref.setSummary(R.string.pref_whitelist_title); 38 | notificationList.setTitle(R.string.pref_whitelist_title); 39 | notificationList.setSummary(R.string.pref_whitelist_summary); 40 | } 41 | return true; 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 11 | 12 | 13 | 17 | 18 | 22 | 23 | 27 | 28 | 32 | 33 | 37 | 38 | 44 | 45 | 51 | 52 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/com/mohammadag/headsupenabler/SettingsHelper.java: -------------------------------------------------------------------------------- 1 | package com.mohammadag.headsupenabler; 2 | 3 | import java.util.HashSet; 4 | 5 | import android.content.Context; 6 | import android.content.SharedPreferences; 7 | import de.robv.android.xposed.XSharedPreferences; 8 | 9 | public class SettingsHelper { 10 | private static final String PACKAGE_NAME = "com.mohammadag.headsupenabler"; 11 | private static final String PREFS = PACKAGE_NAME + "_preferences"; 12 | // called "blacklist" for compatibility with previous versions, but can be a whitelist too 13 | private static final String NOTIFICATION_FILTER_LIST = "blacklist"; 14 | private XSharedPreferences mXSharedPreferences = null; 15 | private SharedPreferences mSharedPreferences = null; 16 | private Context mContext = null; 17 | private HashSet mListItems; 18 | private String mListType; 19 | 20 | // Called from module's classes. 21 | public SettingsHelper() { 22 | mXSharedPreferences = new XSharedPreferences(PACKAGE_NAME, PREFS); 23 | mXSharedPreferences.makeWorldReadable(); 24 | } 25 | 26 | // Called from activities. 27 | @SuppressWarnings("deprecation") 28 | public SettingsHelper(Context context) { 29 | mSharedPreferences = context.getSharedPreferences(PREFS, Context.MODE_WORLD_READABLE); 30 | mContext = context; 31 | } 32 | 33 | public void reload() { 34 | mXSharedPreferences.reload(); 35 | mListItems = getListItems(); 36 | mListType = getListType(); 37 | } 38 | 39 | public void addListItem(String listItem) { 40 | mListItems.add(listItem); 41 | SharedPreferences.Editor prefEditor = mSharedPreferences.edit(); 42 | prefEditor.putStringSet(NOTIFICATION_FILTER_LIST, mListItems); 43 | prefEditor.apply(); 44 | } 45 | 46 | public void removeListItem(String listItem) { 47 | SharedPreferences.Editor prefEditor = mSharedPreferences.edit(); 48 | mListItems.remove(listItem); 49 | prefEditor.putStringSet(NOTIFICATION_FILTER_LIST, mListItems); 50 | prefEditor.apply(); 51 | } 52 | 53 | public boolean isListed(String s) { 54 | if (mListItems == null) 55 | mListItems = getListItems(); 56 | return mListItems.contains(s); 57 | } 58 | 59 | public boolean shouldIgnore(String s) { 60 | return mListType.equals("blacklist") ? isListed(s) : !isListed(s); 61 | } 62 | 63 | public HashSet getListItems() { 64 | HashSet set = new HashSet(); 65 | if (mSharedPreferences != null) 66 | set.addAll(mSharedPreferences.getStringSet(NOTIFICATION_FILTER_LIST, set)); 67 | else if (mXSharedPreferences != null) 68 | set.addAll(mXSharedPreferences.getStringSet(NOTIFICATION_FILTER_LIST, set)); 69 | return set; 70 | } 71 | 72 | public String getListType() { 73 | if (mSharedPreferences != null) 74 | return mSharedPreferences.getString("notification_filter_type", "blacklist"); 75 | else if (mXSharedPreferences != null) 76 | return mXSharedPreferences.getString("notification_filter_type", "blacklist"); 77 | return null; 78 | } 79 | 80 | public int getHeadsUpNotificationDecay() { 81 | return Integer.parseInt(mXSharedPreferences.getString("heads_up_notification_decay", "3700")); 82 | } 83 | 84 | public boolean isEnabledForOngoingNotifications() { 85 | return mXSharedPreferences.getBoolean("enabled_for_ongoing_notifications", false); 86 | } 87 | 88 | public boolean isEnabledOnlyWhenFullscreen() { 89 | return mXSharedPreferences.getBoolean("enabled_only_when_fullscreen", false); 90 | } 91 | 92 | public boolean isDisabledForLowPriority(){ 93 | return mXSharedPreferences.getBoolean("disabled_for_low_priority", false); 94 | } 95 | 96 | public boolean isEnabledOnlyWhenUnlocked() { 97 | return mXSharedPreferences.getBoolean("enabled_only_when_unlocked", false); 98 | } 99 | 100 | public boolean shouldRemovePadding() { 101 | return mXSharedPreferences.getBoolean("remove_padding", false); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/com/mohammadag/headsupenabler/Blacklist.java: -------------------------------------------------------------------------------- 1 | package com.mohammadag.headsupenabler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.List; 7 | 8 | import android.app.ProgressDialog; 9 | import android.content.Context; 10 | import android.content.pm.ApplicationInfo; 11 | import android.content.pm.PackageManager; 12 | import android.graphics.drawable.Drawable; 13 | import android.os.AsyncTask; 14 | import android.os.Bundle; 15 | import android.preference.PreferenceActivity; 16 | import android.view.LayoutInflater; 17 | import android.view.MenuItem; 18 | import android.view.View; 19 | import android.view.ViewGroup; 20 | import android.widget.ArrayAdapter; 21 | import android.widget.CheckBox; 22 | import android.widget.ImageView; 23 | import android.widget.TextView; 24 | 25 | public class Blacklist extends PreferenceActivity { 26 | private static SettingsHelper mSettingsHelper; 27 | private static BlacklistAdapter mAdapter; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | mSettingsHelper = new SettingsHelper(this); 33 | new LoadAppsInfoTask().execute(); 34 | if (mSettingsHelper.getListType().equals("blacklist")) 35 | getActionBar().setTitle(R.string.pref_blacklist_title); 36 | else 37 | getActionBar().setTitle(R.string.pref_whitelist_title); 38 | getActionBar().setDisplayHomeAsUpEnabled(true); 39 | } 40 | 41 | @Override 42 | public boolean onOptionsItemSelected(MenuItem item) { 43 | if (item.getItemId() == android.R.id.home) 44 | onBackPressed(); 45 | return true; 46 | } 47 | 48 | private static class AppInfo { 49 | String title; 50 | String summary; 51 | Drawable icon; 52 | boolean enabled; 53 | } 54 | 55 | private List loadApps(ProgressDialog dialog) { 56 | PackageManager packageManager = getPackageManager(); 57 | List packages = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); 58 | List apps = new ArrayList(); 59 | 60 | dialog.setMax(packages.size()); 61 | int i = 1; 62 | 63 | for (ApplicationInfo app : packages) { 64 | AppInfo appInfo = new AppInfo(); 65 | appInfo.title = (String) app.loadLabel(packageManager); 66 | appInfo.summary = app.packageName; 67 | appInfo.icon = app.loadIcon(packageManager); 68 | appInfo.enabled = mSettingsHelper.isListed(app.packageName); 69 | apps.add(appInfo); 70 | dialog.setProgress(i++); 71 | } 72 | 73 | Collections.sort(apps, new Comparator() { 74 | @Override 75 | public int compare(AppInfo appInfo1, AppInfo appinfo2) { 76 | return appInfo1.title.compareToIgnoreCase(appinfo2.title); 77 | } 78 | }); 79 | 80 | return apps; 81 | } 82 | 83 | private static class BlacklistAdapter extends ArrayAdapter { 84 | LayoutInflater mInflater; 85 | 86 | public BlacklistAdapter(Context context, List items) { 87 | super(context, 0, items); 88 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 89 | } 90 | 91 | private static class Holder { 92 | ImageView icon; 93 | TextView title; 94 | TextView summary; 95 | CheckBox checkbox; 96 | } 97 | 98 | @Override 99 | public View getView(int position, View convertView, ViewGroup parent) { 100 | final Holder holder; 101 | final AppInfo item = getItem(position); 102 | View view; 103 | 104 | if (convertView == null) { 105 | holder = new Holder(); 106 | view = mInflater.inflate(R.layout.blacklist_item, parent, false); 107 | holder.icon = (ImageView) view.findViewById(R.id.icon); 108 | holder.title = (TextView) view.findViewById(android.R.id.title); 109 | holder.summary = (TextView) view.findViewById(android.R.id.summary); 110 | holder.checkbox = (CheckBox) view.findViewById(R.id.checkbox); 111 | view.setTag(holder); 112 | } else { 113 | view = convertView; 114 | holder = (Holder) view.getTag(); 115 | } 116 | 117 | holder.title.setText(item.title); 118 | holder.summary.setText(item.summary); 119 | holder.icon.setImageDrawable(item.icon); 120 | 121 | holder.checkbox.setChecked(item.enabled); 122 | holder.checkbox.setOnClickListener(new View.OnClickListener() { 123 | @Override 124 | public void onClick(View v) { 125 | item.enabled = holder.checkbox.isChecked(); 126 | if (holder.checkbox.isChecked()) { 127 | mSettingsHelper.addListItem(item.summary); 128 | } else { 129 | mSettingsHelper.removeListItem(item.summary); 130 | } 131 | } 132 | }); 133 | 134 | return view; 135 | } 136 | 137 | } 138 | 139 | private class LoadAppsInfoTask extends AsyncTask { 140 | ProgressDialog dialog; 141 | List appInfos; 142 | 143 | @Override 144 | protected void onPreExecute() { 145 | dialog = new ProgressDialog(Blacklist.this); 146 | dialog.setMessage(getString(R.string.loading)); 147 | dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 148 | dialog.setCancelable(false); 149 | dialog.show(); 150 | } 151 | 152 | @Override 153 | protected Void doInBackground(Void... params) { 154 | appInfos = loadApps(dialog); 155 | return null; 156 | } 157 | 158 | @Override 159 | protected void onProgressUpdate(Void... progress) { 160 | mAdapter.notifyDataSetChanged(); 161 | } 162 | 163 | @Override 164 | protected void onPostExecute(Void void_) { 165 | super.onPostExecute(void_); 166 | mAdapter = new BlacklistAdapter(Blacklist.this, appInfos); 167 | setListAdapter(mAdapter); 168 | dialog.dismiss(); 169 | } 170 | 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/com/mohammadag/headsupenabler/XposedMod.java: -------------------------------------------------------------------------------- 1 | package com.mohammadag.headsupenabler; 2 | 3 | import static de.robv.android.xposed.XposedHelpers.callMethod; 4 | import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; 5 | import static de.robv.android.xposed.XposedHelpers.findClass; 6 | import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField; 7 | import static de.robv.android.xposed.XposedHelpers.getBooleanField; 8 | import static de.robv.android.xposed.XposedHelpers.getObjectField; 9 | import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField; 10 | import static de.robv.android.xposed.XposedHelpers.setBooleanField; 11 | import static de.robv.android.xposed.XposedHelpers.setObjectField; 12 | import static de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; 13 | 14 | import android.app.Notification; 15 | import android.content.Context; 16 | import android.content.res.XModuleResources; 17 | import android.graphics.PixelFormat; 18 | import android.os.PowerManager; 19 | import android.app.KeyguardManager; 20 | import android.service.notification.StatusBarNotification; 21 | import android.view.Gravity; 22 | import android.view.MotionEvent; 23 | import android.view.View; 24 | import android.view.WindowManager; 25 | 26 | import de.robv.android.xposed.IXposedHookInitPackageResources; 27 | import de.robv.android.xposed.IXposedHookLoadPackage; 28 | import de.robv.android.xposed.IXposedHookZygoteInit; 29 | import de.robv.android.xposed.XC_MethodHook; 30 | import de.robv.android.xposed.XC_MethodReplacement; 31 | import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam; 32 | 33 | public class XposedMod implements IXposedHookLoadPackage, IXposedHookInitPackageResources, IXposedHookZygoteInit { 34 | private static SettingsHelper mSettingsHelper; 35 | private static String MODULE_PATH; 36 | private int mStatusBarVisibility; 37 | 38 | @Override 39 | public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable { 40 | if (!lpparam.packageName.equals("com.android.systemui")) { 41 | return; 42 | } 43 | 44 | if (mSettingsHelper == null) { 45 | mSettingsHelper = new SettingsHelper(); 46 | } 47 | 48 | /* 49 | * Determine when to show a Heads Up notification. 50 | */ 51 | Class BaseStatusBar = findClass("com.android.systemui.statusbar.BaseStatusBar", lpparam.classLoader); 52 | findAndHookMethod(BaseStatusBar, "shouldInterrupt", StatusBarNotification.class, 53 | new XC_MethodReplacement() { 54 | @Override 55 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { 56 | StatusBarNotification n = (StatusBarNotification) param.args[0]; 57 | mSettingsHelper.reload(); 58 | PowerManager powerManager = (PowerManager) getObjectField(param.thisObject, "mPowerManager"); 59 | // mKeyguardManager missing in OmniRom 60 | //KeyguardManager keyguardManager = (KeyguardManager) getObjectField(param.thisObject, "mKeyguardManager"); 61 | Context ctx = (Context) getObjectField(param.thisObject, "mContext"); 62 | KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(Context.KEYGUARD_SERVICE); 63 | // Ignore if the notification is ongoing and we haven't enabled that in the settings 64 | return !(n.isOngoing() && !mSettingsHelper.isEnabledForOngoingNotifications()) 65 | // Ignore if we're not in a fullscreen app and the "only when fullscreen" setting is 66 | // enabled 67 | && !(!((mStatusBarVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == View.SYSTEM_UI_FLAG_FULLSCREEN) 68 | && mSettingsHelper.isEnabledOnlyWhenFullscreen()) 69 | // Ignore if phone is locked and the "only when unlocked" setting is enabled 70 | && !(keyguardManager.isKeyguardLocked() && mSettingsHelper.isEnabledOnlyWhenUnlocked()) 71 | // Screen must be on 72 | && powerManager.isScreenOn() 73 | // Ignore blacklisted/non whitelisted packages 74 | && !mSettingsHelper.isListed(n.getPackageName()) 75 | // Check if low priority 76 | && !(mSettingsHelper.isDisabledForLowPriority() 77 | && !(n.getNotification().priority > Notification.PRIORITY_LOW)); 78 | } 79 | } 80 | ); 81 | 82 | /* 83 | * Stop breaking the normal notification ordering. 84 | * The current AOSP implementation considers the interruption state (= has it been shown to the user using a 85 | * Heads Up?) more important than the notification's score when comparing notifications (to set the order). 86 | * This breaks the normal order of notifications, so we're nuking the method that sets the interruption state. 87 | */ 88 | Class NotificationDataEntry = findClass("com.android.systemui.statusbar.NotificationData$Entry", 89 | lpparam.classLoader); 90 | findAndHookMethod(NotificationDataEntry, "setInterruption", XC_MethodReplacement.DO_NOTHING); 91 | 92 | /* 93 | * Enable one handed expansion for the Heads Up view. 94 | */ 95 | Class HeadsUpNotificationView = findClass("com.android.systemui.statusbar.policy.HeadsUpNotificationView", 96 | lpparam.classLoader); 97 | findAndHookMethod(HeadsUpNotificationView, "onAttachedToWindow", new XC_MethodHook() { 98 | @Override 99 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 100 | setAdditionalInstanceField(getObjectField(param.thisObject, "mExpandHelper"), "headsUp", true); 101 | } 102 | }); 103 | 104 | Class ExpandHelper = findClass("com.android.systemui.ExpandHelper", lpparam.classLoader); 105 | findAndHookMethod(ExpandHelper, "onInterceptTouchEvent", MotionEvent.class, new XC_MethodHook() { 106 | @Override 107 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 108 | int action = ((MotionEvent) param.args[0]).getAction(); 109 | Object headsUp = getAdditionalInstanceField(param.thisObject, "headsUp"); 110 | if (headsUp != null && (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { 111 | setObjectField(param.thisObject, "mWatchingForPull", true); 112 | } 113 | } 114 | }); 115 | 116 | /* 117 | * Monitor status bar visibility changes. 118 | */ 119 | Class PhoneStatusBar = findClass("com.android.systemui.statusbar.phone.PhoneStatusBar", lpparam.classLoader); 120 | findAndHookMethod(PhoneStatusBar, "setSystemUiVisibility", int.class, int.class, new XC_MethodHook() { 121 | @Override 122 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 123 | mStatusBarVisibility = (Integer) param.args[0]; 124 | } 125 | }); 126 | 127 | /* 128 | * Make the Heads Up notification show on top of the status bar by setting the y position to 0. 129 | * It could allow for other changes (e.g. change the gravity) in the future as well. 130 | */ 131 | findAndHookMethod(PhoneStatusBar, "addHeadsUpView", new XC_MethodReplacement() { 132 | @Override 133 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { 134 | Context context = (Context) getObjectField(param.thisObject, "mContext"); 135 | WindowManager windowManager = (WindowManager) getObjectField(param.thisObject, "mWindowManager"); 136 | View headsUpNotificationView = (View) getObjectField(param.thisObject, "mHeadsUpNotificationView"); 137 | int animation = context.getResources().getIdentifier("Animation_StatusBar_HeadsUp", "style", 138 | "com.android.systemui"); 139 | 140 | WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 141 | WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT, 142 | WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar! 143 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 144 | | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 145 | | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 146 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 147 | | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 148 | | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 149 | PixelFormat.TRANSLUCENT 150 | ); 151 | lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 152 | lp.gravity = Gravity.TOP; 153 | if (mSettingsHelper.shouldRemovePadding()) 154 | lp.y = 0; 155 | else 156 | lp.y = (Integer) callMethod(param.thisObject, "getStatusBarHeight"); 157 | lp.setTitle("Heads Up"); 158 | lp.packageName = context.getPackageName(); 159 | lp.windowAnimations = animation; 160 | 161 | windowManager.addView(headsUpNotificationView, lp); 162 | return null; 163 | } 164 | }); 165 | 166 | /* 167 | * Enable Heads Up on startup. 168 | */ 169 | findAndHookMethod(PhoneStatusBar, "start", new XC_MethodHook() { 170 | @Override 171 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 172 | if (!getBooleanField(param.thisObject, "mUseHeadsUp")) { 173 | setBooleanField(param.thisObject, "mUseHeadsUp", true); 174 | callMethod(param.thisObject, "addHeadsUpView"); 175 | } 176 | } 177 | }); 178 | 179 | } 180 | 181 | @Override 182 | public void initZygote(StartupParam startupParam) throws Throwable { 183 | MODULE_PATH = startupParam.modulePath; 184 | } 185 | 186 | @Override 187 | public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { 188 | if (!resparam.packageName.equals("com.android.systemui")) 189 | return; 190 | 191 | if (mSettingsHelper == null) { 192 | mSettingsHelper = new SettingsHelper(); 193 | } 194 | 195 | 196 | // Set the delay before the Heads Up notification is hidden. 197 | resparam.res.setReplacement("com.android.systemui", "integer", "heads_up_notification_decay", 198 | mSettingsHelper.getHeadsUpNotificationDecay()); 199 | 200 | // Replace the Heads Up notification's background to remove the padding. 201 | if (mSettingsHelper.shouldRemovePadding()) { 202 | XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res); 203 | resparam.res.setReplacement("com.android.systemui", "drawable", "heads_up_window_bg", 204 | modRes.fwd(R.drawable.heads_up_window_bg)); 205 | } 206 | } 207 | 208 | } 209 | --------------------------------------------------------------------------------