[Sprint Samsung Galaxy S III] moonknightus:
This guide was written based on the Sprint Samsung Galaxy S3 ND8 KitKat stock ROM but with the proper modifications may work for other KitKat ROMs. Please don't ask me to modify this to work with ROM xyz or to compile this for you into a ROM. I won't do it.

Background Info

As you know, Android 4.4 has a heads-up notification feature built in but disabled with no way to enable it from settings. While there are various Xposed modules that will enable this for you as well as give some additional functionality (such as whitelisting), I personally don't use Xposed because it causes conflicts on my ROM so I decided to figure out how to enable it myself. Simply enabling Heads Up notifications caused all sorts of force closes any time an ongoing notification presented itself so that had to be worked out as well.

Requirements

apktool 2.0 beta 9 and the knowledge of how to use it. There are various guides on how to use apktool on XDA. Find one and read it. Make sure to read the information on the apktool site as well since some of the options in 2.0 may be different from the guides you find.

A text editor that supports Unix-style text files. I recommend Notepad++.

Files

You'll be working with the following apks:

SystemUI.apk

SecSettings.apk

SystemUI.apk

Decompile SystemUI.apk using apktool.

Open smali\com\android\systemui\statusbar\BaseStatusBar.smali in your preferred text editor.

Find .method protected shouldInterrupt(Landroid/service/notification/StatusBarNotification;)Z.

This method is of no use to us in it's current form and is what causes the force closes/ongoing notification issues so we are going to completely remove it and replace it with the following:

.method protected shouldInterrupt(Landroid/service/notification/StatusBarNotification;)Z
.locals 12
.param p1, "sbn" # Landroid/service/notification/StatusBarNotification;

.prologue
const/4 v8, 0x0

const/4 v9, 0x1

.line 1292
invoke-virtual {p1}, Landroid/service/notification/StatusBarNotification;->isOngoing()Z

move-result v4

if-nez v4, :cond_0

iget-object v10, p0, Lcom/android/systemui/statusbar/BaseStatusBar;->mPowerManager:Landroid/os/PowerManager;

invoke-virtual {v10}, Landroid/os/PowerManager;->isScreenOn()Z

move-result v1

:goto_0
return v1

:cond_0
move v1, v8

goto :goto_0
.end method

Save the file and recompile SystemUI using apktool. Don't forget to re-sign the APK (either use an APK signer if you have a ROM that has 3rd-party system app signatures enabled or copy the AndroidManifest.xml file and META-INF folder from the original to the new APK).

That's it for SystemUI.apk.

SecSettings.apk

Decompile SecSettings.apk with apktool.

First, we need to add the title and summary for the setting to enable Heads Up notifications. Open res\values\Strings.xml in your preferred text editor.

Add the following lines to the bottom of the file:

<string name="heads_up">Heads up notifications</string>
<string name="heads_up_desc">Enable heads up notifications</string>

Save the file and close it.

Next we need to add the setting entry. Open res\xml\display_settings.xml in your preferred text editor.

Add the following line underneath your preferred setting category (I chose to place it under "General" in my ROM):

<CheckBoxPreference android:title="@string/heads_up" android:key="heads_up_setting" android:widgetLayout="@touchwiz:layout/preference_widget_twcheckbox" />

Save the file and close it.

Finally, we will add the code to enable/disable Heads Up notifications. Open smali\com\android\settings\DisplaySettings.smali in your preferred text editor.

Find # instance fields and add the following line (on a new line) somewhere before # direct methods:

.field private mHeadsUp:Landroid/preference/CheckBoxPreference;

Find .method private updateState()V, then locate the section of code that looks similar to this:

:cond_0
invoke-direct {p0}, Lcom/android/settings/DisplaySettings;->updateInformativeScreenSummary()V

.line 1374
iget-object v3, p0, Lcom/android/settings/DisplaySettings;->mDisplayBatteryLevel:Landroid/preference/CheckBoxPreference;

invoke-virtual {p0}, Lcom/android/settings/DisplaySettings;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v0

const-string v4, "display_battery_percentage"

invoke-static {v0, v4, v2}, Landroid/provider/Settings$System;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v0

if-eqz v0, :cond_2

move v0, v1

:goto_0
invoke-virtual {v3, v0}, Landroid/preference/CheckBoxPreference;->setChecked(Z)V

Your variables (v#'s), conditionals (cond_#'s), gotos (goto_#'s), and .line #'s may differ. Under the line invoke-virtual {v3, v0}, Landroid/preference/CheckBoxPreference;->setChecked(Z)V, add:

iget-object v3, p0, Lcom/android/settings/DisplaySettings;->mHeadsUp:Landroid/preference/CheckBoxPreference;

invoke-virtual {p0}, Lcom/android/settings/DisplaySettings;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v0

const-string v4, "heads_up_enabled"

invoke-static {v0, v4, v2}, Landroid/provider/Settings$Global;->getInt(Landroid/content/ContentResolver;Ljava/lang/String;I)I

move-result v0

if-eqz v0, :cond_8

move v0, v1

:goto_6
invoke-virtual {v3, v0}, Landroid/preference/CheckBoxPreference;->setChecked(Z)V

You'll want to change the variables to match the ones in the prior code section if they are different for you. Also make sure to change the conditional and goto labels to one hexadecimal numeral higher than the highest one already present in the method (for example, in stock ND8, the highest conditional is cond_7 so we choose cond_8 for the conditional here and the highest goto is goto_5 so we chose goto_6).

Scroll down to the end of the method and add the following above .end method:

:cond_8
move v0, v2

goto/16 :goto_6

Make sure to change v2 to whatever variable is defined at the beginning of the method as 0x0 (ie. const/4 v2, 0x0 in this case).

Find .method public onCreate(Landroid/os/Bundle;)V, then locate the section of code that looks similar to the following:

const-string v0, "display_battery_level"

invoke-virtual {p0, v0}, Lcom/android/settings/DisplaySettings;->findPreference(Ljava/lang/CharSequence;)Landroid/preference/Preference;

move-result-object v0

check-cast v0, Landroid/preference/CheckBoxPreference;

iput-object v0, p0, Lcom/android/settings/DisplaySettings;->mDisplayBatteryLevel:Landroid/preference/CheckBoxPreference;

Again, your variables (v#'s) may differ. Beneath the line iput-object v0, p0, Lcom/android/settings/DisplaySettings;->mDisplayBatteryLevel:Landroid/preference/CheckBoxPreference;, add the following:

const-string v0, "heads_up_setting"

invoke-virtual {p0, v0}, Lcom/android/settings/DisplaySettings;->findPreference(Ljava/lang/CharSequence;)Landroid/preference/Preference;

move-result-object v0

check-cast v0, Landroid/preference/CheckBoxPreference;

iput-object v0, p0, Lcom/android/settings/DisplaySettings;->mHeadsUp:Landroid/preference/CheckBoxPreference;

Make sure to change the variable #'s to match the ones in the prior code section.

Find .method public onPreferenceTreeClick(Landroid/preference/PreferenceScreen;Landroid/preference/Preference;)Z and locate the section of code that looks similar to:

:cond_a
iget-object v0, p0, Lcom/android/settings/DisplaySettings;->mDisplayBatteryLevel:Landroid/preference/CheckBoxPreference;

invoke-virtual {p2, v0}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_c

.line 1530
iget-object v0, p0, Lcom/android/settings/DisplaySettings;->mDisplayBatteryLevel:Landroid/preference/CheckBoxPreference;

invoke-virtual {v0}, Landroid/preference/CheckBoxPreference;->isChecked()Z

move-result v0

.line 1531
invoke-virtual {p0}, Lcom/android/settings/DisplaySettings;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v1

const-string v4, "display_battery_percentage"

if-eqz v0, :cond_b

move v3, v2

:cond_b
invoke-static {v1, v4, v3}, Landroid/provider/Settings$System;->putInt(Landroid/content/ContentResolver;Ljava/lang/String;I)Z

goto :goto_3

.line 1533
:cond_c

As usual, your variables (v#'s), conditionals (cond_#'s), gotos (goto_#'s), and .line #'s may differ. Under :cond_c, add the following:

iget-object v0, p0, Lcom/android/settings/DisplaySettings;->mHeadsUp:Landroid/preference/CheckBoxPreference;

invoke-virtual {p2, v0}, Ljava/lang/Object;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_2b

.line 1530
iget-object v0, p0, Lcom/android/settings/DisplaySettings;->mHeadsUp:Landroid/preference/CheckBoxPreference;

invoke-virtual {v0}, Landroid/preference/CheckBoxPreference;->isChecked()Z

move-result v0

.line 1531
invoke-virtual {p0}, Lcom/android/settings/DisplaySettings;->getContentResolver()Landroid/content/ContentResolver;

move-result-object v1

const-string v4, "heads_up_enabled"

if-eqz v0, :cond_2c

move v3, v2

:cond_2c
invoke-static {v1, v4, v3}, Landroid/provider/Settings$Global;->putInt(Landroid/content/ContentResolver;Ljava/lang/String;I)Z

goto/16 :goto_3

:cond_2b

Again, you'll want to change the variables to match the ones in the prior code section if they are different for you and make sure to change the conditional labels to one hexadecimal numeral higher than the highest one already present in the method. Change the goto/16 :goto_3 to match the one in the previous code section.

Save the file and close it.

That's it for SecSettings.apk. Recompile and re-sign it.

Install both files to your phone using your preferred method. You should already know this but I'll say it anyway, THESE FILES CANNOT BE INSTALLED LIKE NORMAL SIDE-LOADED APKs. I prefer to reboot to recovery and use ADB to push the files to the system, but you can use whatever method is easiest for you (ie. Aroma file manager, flashable zip, etc.).