diff --git a/.gitignore b/.gitignore index f91ff2b..afbdab3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,216 +1,6 @@ -################# -## Eclipse -################# - -*.pydevproject -.project -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# PDT-specific -.buildpath - - -################# -## Visual Studio -################# - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# User-specific files -*.suo -*.user -*.sln.docstates - -# Build results - -[Dd]ebug/ -[Rr]elease/ -x64/ -build/ -[Bb]in/ -[Oo]bj/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -*_i.c -*_p.c -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.log -*.scc - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opensdf -*.sdf -*.cachefile - -# Visual Studio profiler -*.psess -*.vsp -*.vspx - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -*.ncrunch* -.*crunch*.local.xml - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.Publish.xml -*.pubxml -*.publishproj - -# NuGet Packages Directory -## TODO: If you have NuGet Package Restore enabled, uncomment the next line -#packages/ - -# Windows Azure Build Output -csx -*.build.csdef - -# Windows Store app package directory -AppPackages/ - -# Others -sql/ -*.Cache -ClientBin/ -[Ss]tyle[Cc]op.* -~$* -*~ -*.dbmdl -*.[Pp]ublish.xml -*.pfx -*.publishsettings - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file to a newer -# Visual Studio version. Backup files are not needed, because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -App_Data/*.mdf -App_Data/*.ldf - -############# -## Windows detritus -############# - -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Mac crap +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries .DS_Store - - -############# -## Python -############# - -*.py[cod] - -# Packages -*.egg -*.egg-info -dist/ -build/ -eggs/ -parts/ -var/ -sdist/ -develop-eggs/ -.installed.cfg - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.tox - -#Translations -*.mo - -#Mr Developer -.mr.developer.cfg +/build diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..3fe6a85 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +AndroidTVNotification \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..217af47 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..9069a2f --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..59436c9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e0dfc01 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..def6a6a --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/AndroidTVNotification.iml b/AndroidTVNotification.iml new file mode 100644 index 0000000..2a02201 --- /dev/null +++ b/AndroidTVNotification.iml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3e4a15 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# AndroidTV Notification +Notifications for Android TV! + +This is a multi-faceted project that will: +* Popup a message when a new notification is issued +* Allow users to access local notifications on Android TV +* Connect a mobile device via Bluetooth to Android TV and mirror notifications +* Utilize a library to send various types of data to Android TV, which is received in a custom way through a receiver + +### Changelog +#### 17 Jan + Fixed lists, improved output + +#### 16 Jan + Started project diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/AndroidTV_A1.apk b/app/AndroidTV_A1.apk new file mode 100644 index 0000000..62a9484 Binary files /dev/null and b/app/AndroidTV_A1.apk differ diff --git a/app/app.iml b/app/app.iml new file mode 100644 index 0000000..c6c55c4 --- /dev/null +++ b/app/app.iml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..298646a --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + applicationId "com.felkertech.n.tvnotification" + minSdkVersion 18 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:21.0.3' +} diff --git a/app/manifest-merger-release-report.txt b/app/manifest-merger-release-report.txt new file mode 100644 index 0000000..ef9d649 --- /dev/null +++ b/app/manifest-merger-release-report.txt @@ -0,0 +1,127 @@ +-- Merging decision tree log --- +manifest +ADDED from AndroidManifest.xml:2:1 + xmlns:android + ADDED from AndroidManifest.xml:2:11 + package + ADDED from AndroidManifest.xml:3:5 + INJECTED from AndroidManifest.xml:0:0 + INJECTED from AndroidManifest.xml:0:0 + android:versionName + INJECTED from AndroidManifest.xml:0:0 + INJECTED from AndroidManifest.xml:0:0 + android:versionCode + INJECTED from AndroidManifest.xml:0:0 + INJECTED from AndroidManifest.xml:0:0 +uses-feature#android.hardware.gps +ADDED from AndroidManifest.xml:5:5 + android:required + ADDED from AndroidManifest.xml:5:55 + android:name + ADDED from AndroidManifest.xml:5:19 +uses-feature#android.hardware.location +ADDED from AndroidManifest.xml:6:5 + android:required + ADDED from AndroidManifest.xml:6:60 + android:name + ADDED from AndroidManifest.xml:6:19 +uses-feature#android.hardware.location.gps +ADDED from AndroidManifest.xml:7:5 + android:required + ADDED from AndroidManifest.xml:7:64 + android:name + ADDED from AndroidManifest.xml:7:19 +uses-feature#android.hardware.touchscreen +ADDED from AndroidManifest.xml:8:5 + android:required + ADDED from AndroidManifest.xml:8:63 + android:name + ADDED from AndroidManifest.xml:8:19 +uses-feature#android.hardware.telephony +ADDED from AndroidManifest.xml:9:5 + android:required + ADDED from AndroidManifest.xml:9:61 + android:name + ADDED from AndroidManifest.xml:9:19 +uses-feature#android.hardware.camera +ADDED from AndroidManifest.xml:10:5 + android:required + ADDED from AndroidManifest.xml:10:58 + android:name + ADDED from AndroidManifest.xml:10:19 +uses-feature#android.hardware.nfc +ADDED from AndroidManifest.xml:11:5 + android:required + ADDED from AndroidManifest.xml:11:55 + android:name + ADDED from AndroidManifest.xml:11:19 +uses-feature#android.hardware.microphone +ADDED from AndroidManifest.xml:12:5 + android:required + ADDED from AndroidManifest.xml:12:62 + android:name + ADDED from AndroidManifest.xml:12:19 +uses-feature#android.hardware.wifi +ADDED from AndroidManifest.xml:13:5 + android:required + ADDED from AndroidManifest.xml:13:56 + android:name + ADDED from AndroidManifest.xml:13:19 +uses-feature#android.hardware.bluetooth +ADDED from AndroidManifest.xml:14:5 + android:required + ADDED from AndroidManifest.xml:14:61 + android:name + ADDED from AndroidManifest.xml:14:19 +application +ADDED from AndroidManifest.xml:16:5 +MERGED from com.android.support:appcompat-v7:21.0.3:16:5 +MERGED from com.android.support:support-v4:21.0.3:16:5 + android:label + ADDED from AndroidManifest.xml:19:9 + android:allowBackup + ADDED from AndroidManifest.xml:17:9 + android:icon + ADDED from AndroidManifest.xml:18:9 + android:theme + ADDED from AndroidManifest.xml:20:9 +activity#com.felkertech.n.tvnotification.MainActivity +ADDED from AndroidManifest.xml:21:9 + android:label + ADDED from AndroidManifest.xml:23:13 + android:name + ADDED from AndroidManifest.xml:22:13 +intent-filter#android.intent.action.MAIN+android.intent.category.LAUNCHER +ADDED from AndroidManifest.xml:24:13 +action#android.intent.action.MAIN +ADDED from AndroidManifest.xml:25:17 + android:name + ADDED from AndroidManifest.xml:25:25 +category#android.intent.category.LAUNCHER +ADDED from AndroidManifest.xml:27:17 + android:name + ADDED from AndroidManifest.xml:27:27 +service#com.felkertech.n.tvnotification.NotificationListener +ADDED from AndroidManifest.xml:30:9 + android:label + ADDED from AndroidManifest.xml:31:13 + android:permission + ADDED from AndroidManifest.xml:32:13 + android:name + ADDED from AndroidManifest.xml:30:18 +intent-filter#android.service.notification.NotificationListenerService +ADDED from AndroidManifest.xml:33:13 +action#android.service.notification.NotificationListenerService +ADDED from AndroidManifest.xml:34:17 + android:name + ADDED from AndroidManifest.xml:34:25 +uses-sdk +INJECTED from AndroidManifest.xml:0:0 reason: use-sdk injection requested +MERGED from com.android.support:appcompat-v7:21.0.3:15:5 +MERGED from com.android.support:support-v4:21.0.3:15:5 + android:targetSdkVersion + INJECTED from AndroidManifest.xml:0:0 + INJECTED from AndroidManifest.xml:0:0 + android:minSdkVersion + INJECTED from AndroidManifest.xml:0:0 + INJECTED from AndroidManifest.xml:0:0 diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..da4ccd1 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\N\AppData\Local\Android\android-studio\sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/felkertech/n/tvnotification/ApplicationTest.java b/app/src/androidTest/java/com/felkertech/n/tvnotification/ApplicationTest.java new file mode 100644 index 0000000..d030721 --- /dev/null +++ b/app/src/androidTest/java/com/felkertech/n/tvnotification/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.felkertech.n.tvnotification; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5f8afd2 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/felkertech/n/tvnotification/MainActivity.java b/app/src/main/java/com/felkertech/n/tvnotification/MainActivity.java new file mode 100644 index 0000000..8351828 --- /dev/null +++ b/app/src/main/java/com/felkertech/n/tvnotification/MainActivity.java @@ -0,0 +1,85 @@ +package com.felkertech.n.tvnotification; + +import android.app.Activity; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.Date; + +public class MainActivity extends Activity { + + private TextView txtView; + private NotificationReceiver nReceiver; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + txtView = (TextView) findViewById(R.id.textView); + nReceiver = new NotificationReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + registerReceiver(nReceiver,filter); + Toast.makeText(this, "V5", Toast.LENGTH_SHORT).show(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(nReceiver); + } + + public void buttonClicked(View v){ + Intent i; + switch(v.getId()) { + case R.id.btnCreateNotify: + Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + PendingIntent pendIntent = PendingIntent.getActivity(this, 0, intent, 0); + NotificationManager nManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationCompat.Builder ncomp = new NotificationCompat.Builder(this); + ncomp.setContentTitle("My Notification"); + ncomp.setContentText("Notification Listener Service Example"); + ncomp.setSmallIcon(R.drawable.ic_launcher); + ncomp.setAutoCancel(true); + ncomp.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setColor(getResources().getColor(R.color.material_deep_teal_500)) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setWhen(new Date().getTime()-100000) + .addAction(R.drawable.abc_ic_menu_paste_mtrl_am_alpha, "Go to Settings", pendIntent); + nManager.notify((int)System.currentTimeMillis(),ncomp.build()); + return; + case R.id.btnClearNotify: + i = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); + i.putExtra("command","clearall"); + sendBroadcast(i); + return; + case R.id.btnListNotify: + i = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); + i.putExtra("command","list"); + sendBroadcast(i); + return; + case R.id.btnSettings: + i =new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + startActivity(i); + + } + } + + class NotificationReceiver extends BroadcastReceiver{ + @Override + public void onReceive(Context context, Intent intent) { + String temp = intent.getStringExtra("notification_event") + "\n" + txtView.getText(); + txtView.setText(temp); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/felkertech/n/tvnotification/NotificationListener.java b/app/src/main/java/com/felkertech/n/tvnotification/NotificationListener.java new file mode 100644 index 0000000..a247669 --- /dev/null +++ b/app/src/main/java/com/felkertech/n/tvnotification/NotificationListener.java @@ -0,0 +1,108 @@ +package com.felkertech.n.tvnotification; + +/** + * Created by N on 1/16/2015. + */ +import android.app.Notification; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Build; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import com.felkertech.n.tvnotification.services.Chathead; +import com.felkertech.n.tvnotification.utils.Alert; +import com.felkertech.n.tvnotification.utils.Popup; + +import java.util.ArrayList; + +public class NotificationListener extends NotificationListenerService { + + private String TAG = "tvnotification::"+this.getClass().getSimpleName(); + private NLServiceReceiver nlservicereciver; + @Override + public void onCreate() { + super.onCreate(); + nlservicereciver = new NLServiceReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_SERVICE_EXAMPLE"); + registerReceiver(nlservicereciver,filter); + } + + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceiver(nlservicereciver); + } + + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + + Log.i(TAG,"********** onNotificationPosted"); + Log.i(TAG,"ID :" + sbn.getId() + " " + sbn.getNotification().tickerText + " " + sbn.getPackageName()); + Intent i = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + i.putExtra("notification_event","onNotificationPosted :" + sbn.getPackageName() + "n"); + sendBroadcast(i); + Log.d(TAG, ""+sbn.getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString()); + Popup p = new Popup(sbn); + Log.d(TAG, p.category+"-C"); + Log.d(TAG, p.toString()); + Alert note = p.publicAlert(); + Log.d(TAG, note.toString()); + Intent popup = new Intent(getApplicationContext(), Chathead.class); + popup.putExtra("VALUE", note.toBundle()); + startService(popup); + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn) { + Log.i(TAG,"********** onNOtificationRemoved"); + Log.i(TAG,"ID :" + sbn.getId() + "t" + sbn.getNotification().tickerText +"t" + sbn.getPackageName()); + Intent i = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + i.putExtra("notification_event","onNotificationRemoved :" + sbn.getPackageName() + "n"); + + sendBroadcast(i); + } + + class NLServiceReceiver extends BroadcastReceiver{ + + @Override + public void onReceive(Context context, Intent intent) { + if(intent.getStringExtra("command").equals("clearall")){ + NotificationListener.this.cancelAllNotifications(); + } + else if(intent.getStringExtra("command").equals("list")){ + Intent i1 = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + i1.putExtra("notification_event","====================="); + sendBroadcast(i1); + int i=1; + ArrayList p = new ArrayList<>(); + try { + for (StatusBarNotification sbn : getActiveNotifications()) { + Popup pp = new Popup(sbn, i); + p.add(pp); + Intent i2 = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + String output = pp.toString(); + i2.putExtra("notification_event", output); + sendBroadcast(i2); + i++; + } + } catch (Exception e) { + Intent i2 = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + String output = e.getMessage(); + i2.putExtra("notification_event", output); + sendBroadcast(i2); + } + Intent i3 = new Intent("com.felkertech.n.tvnotification.NOTIFICATION_LISTENER_EXAMPLE"); + i3.putExtra("notification_event","===== Notification List ===="); + sendBroadcast(i3); + + } + + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/felkertech/n/tvnotification/services/Chathead.java b/app/src/main/java/com/felkertech/n/tvnotification/services/Chathead.java new file mode 100644 index 0000000..45f8921 --- /dev/null +++ b/app/src/main/java/com/felkertech/n/tvnotification/services/Chathead.java @@ -0,0 +1,236 @@ +package com.felkertech.n.tvnotification.services; + +import android.app.IntentService; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.drawable.BitmapDrawable; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.text.Layout; +import android.util.Log; +import android.view.Display; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.felkertech.n.tvnotification.R; +import com.felkertech.n.tvnotification.utils.Alert; + +/** + * Created by N on 2/8/2015. + */ +public class Chathead extends Service { + + private WindowManager windowManager; + private View chatHead; + private Alert alert; + /**Total length of time the chathead service will be present**/ + private static final int POPUP_DISPLAY_TIME = 5000; + /**How long is a 'step' for animation purposes?**/ + private static final int FRAME = 33; // + private static final String TAG = "tvnotification::Chathead"; + + /* + * Here are the different types of styles + */ + private PopupParams STYLE_HONEYCOMB = new PopupParams(500, 500, 64, R.layout.popup_honeycomb, new PopupParams.PopupHandler() { + @Override + public void animateIn(int ANIMATION_OPEN, Handler loop) { + chatHead.setAlpha(chatHead.getAlpha()+1f/(ANIMATION_OPEN/FRAME)); + if(chatHead.getAlpha() < 1) + loop.sendEmptyMessageDelayed(0, FRAME); + } + + @Override + public void animateOut(int ANIMATION_OUT, Handler loop) { + chatHead.setAlpha(chatHead.getAlpha()-1f/(ANIMATION_OUT/FRAME)); + if(chatHead.getAlpha() > 0) + loop.sendEmptyMessageDelayed(0, FRAME); + } + + @Override + public void populate() { + try { + Context p = createPackageContext(alert.getPackageName(), 0); + try { + Bitmap icon = BitmapFactory.decodeResource(p.getResources(), + alert.getIcon()); + ((ImageView) chatHead.findViewById(R.id.notification_icon)).setImageBitmap(icon); + } catch(Exception e) { + Log.d(TAG, "Alert icon not found"); + } + try { +// if(alert.getIcon() != 0) +// ((ImageView) chatHead.findViewById(R.id.notification_icon)).setImageBitmap(alert.getBitmap()); + } catch(Exception e) { + Log.d(TAG, "Alert bitmap not found"); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + + try { + ((TextView) chatHead.findViewById(R.id.notification_title)).setText(alert.getTitle() + ""); + } catch(Exception e) { + Log.d(TAG, "Alert title not found"); + } + try { //For large devices + ((TextView) chatHead.findViewById(R.id.notification_subtext)).setText(alert.getText()+""); + } catch(Exception ignored) { + + } + } + }); + + /** + * Creates an IntentService. Invoked by your subclass's constructor. + * + * @param name Used to name the worker thread, important only for debugging. + */ + public Chathead(String name) { + //super(name); + } + public Chathead() { + //super("Nope"); + } + + @Override public IBinder onBind(Intent intent) { + // Not used + return null; + } + + + protected void onHandleIntent(Intent intent) { + + } + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + int a = super.onStartCommand(intent,flags,startId); + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + Log.d(TAG, intent.toString()); + Log.d(TAG, "Incoming"); + alert = new Alert(intent.getBundleExtra("VALUE")); + Log.d(TAG, alert.toString()+""); + Log.d(TAG, alert.getTitle()+""); + post(); + return a; + } + + public void post() { + + windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); + Log.d(TAG, "Posting"); + //TODO Get SettingsManager, pick the proper layout, use a class for this and a few other attributes + PopupParams style; + style = STYLE_HONEYCOMB; + chatHead = LayoutInflater.from(this).inflate(style.res, null); + style.populatePopup.sendEmptyMessageDelayed(0, 0); + + WindowManager.LayoutParams params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_PHONE, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + + params.gravity = Gravity.TOP | Gravity.LEFT; + Display display = windowManager.getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + int width = size.x; + int height = size.y; + params.x = 0; + //TODO this fnc doesn't work, pass in height + params.y = height-24-dpToPx(style.layoutHeight); + Log.d(TAG, dpToPx(style.layoutHeight)+""); + chatHead.setMinimumWidth(width); + chatHead.setMinimumHeight(dpToPx(style.layoutHeight)); + chatHead.setAlpha((float) 0.02); + + windowManager.addView(chatHead, params); + Handler killr = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message in) { + stopSelf(); + } + }; + //TODO Intro/Outro animations + killr.sendEmptyMessageDelayed(0, POPUP_DISPLAY_TIME); + + style.animateIn.sendEmptyMessageDelayed(0, FRAME); + style.animateOut.sendEmptyMessageDelayed(0, POPUP_DISPLAY_TIME-style.ANIMATION_CLOSE-FRAME); + + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(TAG, "Destroying head"); + if (chatHead != null) windowManager.removeView(chatHead); + } + + public int dpToPx(int px) { + final float scale = getApplicationContext().getResources().getDisplayMetrics().density; + int pixels = (int) (px * scale + 0.5f); + return pixels; + } + + /** + * This class holds many different functions for a variety of popup styles + */ + public static class PopupParams { + /**The length of time when the popup will animate in**/ + private static int ANIMATION_OPEN = 500; + /**The length of time when the popup will animate out**/ + private static int ANIMATION_CLOSE = 500; // + private Handler animateIn; + private Handler animateOut; + private int layoutHeight = 64; + private Handler populatePopup; + private PopupHandler interfacer; + private int res; + + public interface PopupHandler { + public void animateIn(int in_duration, Handler loop); + public void animateOut(int out_duration, Handler loop); + public void populate(); + } + public PopupParams(int in_duration, int out_duration, int height, int resource, final PopupHandler ph) { + ANIMATION_OPEN = in_duration; + ANIMATION_CLOSE = out_duration; + layoutHeight = height; + res = resource; + animateIn = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message in) { + ph.animateIn(ANIMATION_OPEN, this); + } + }; + animateOut = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message in) { + ph.animateOut(ANIMATION_CLOSE, this); + } + }; + populatePopup = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message in) { + ph.populate(); + } + }; + } + } +} diff --git a/app/src/main/java/com/felkertech/n/tvnotification/utils/Alert.java b/app/src/main/java/com/felkertech/n/tvnotification/utils/Alert.java new file mode 100644 index 0000000..a325562 --- /dev/null +++ b/app/src/main/java/com/felkertech/n/tvnotification/utils/Alert.java @@ -0,0 +1,110 @@ +package com.felkertech.n.tvnotification.utils; + +import android.app.Notification; +import android.graphics.Bitmap; +import android.os.Bundle; + +/** + * Created by N on 1/16/2015. + */ +public class Alert { + private String packageName; + private String title; + private String text; + private int color; + private int priority; + private String category; + private int visibility; + private int icon; + private Bitmap bitmap; + private Notification.Action[] actions; + + public Alert(String packageName, String title, String text, int color, int priority, String category, int visibility, Notification.Action[] actions, int icon, Bitmap bitmap) { + this.packageName = packageName; + this.title = title; + this.text = text; + this.color = color; + this.priority = priority; + this.category = category; + this.visibility = visibility; + this.actions = actions; + this.icon = icon; + this.bitmap = bitmap; + } + public Alert(Bundle stuff) { + packageName = stuff.getString("PACKAGE_NAME"); + title = stuff.getString("TITLE"); + text = stuff.getString("TEXT"); + color = stuff.getInt("COLOR"); + priority = stuff.getInt("PRIORITY"); + category = stuff.getString("CATEGORY"); + visibility = stuff.getInt("VISIBILITY"); +// stuff b.putParcelable("ACTIONS", actions); + icon = stuff.getInt("ICON"); + bitmap = stuff.getParcelable("BITMAP"); + } + + public String getTitle() { + return title; + } + + public String getText() { + return text; + } + + public int getColor() { + return color; + } + + public int getPriority() { + return priority; + } + + public String getCategory() { + return category; + } + + public int getVisibility() { + return visibility; + } + + public int getIcon() { + return icon; + } + + public Bitmap getBitmap() { + return bitmap; + } + + public String getPackageName() { + return packageName; + } + + public Notification.Action[] getActions() { + return actions; + } + public boolean hasActions() { + if(actions == null) + return false; + return actions.length > 0; + } + //TODO Actions are currently not supported + public Bundle toBundle() { + Bundle b = new Bundle(); + b.putString("PACKAGE_NAME", packageName); + b.putString("TITLE", title); + b.putString("TEXT", text); + b.putInt("COLOR", color); + b.putInt("PRIORITY", priority); + b.putString("CATEGORY", category); + b.putInt("VISIBILITY", visibility); +// b.putParcelable("ACTIONS", actions); + b.putInt("ICON", icon); + b.putParcelable("BITMAP", bitmap); + return b; + } + @Override + public String toString() { + return "Alert {title="+title+", text="+text+", color="+color+", priority="+priority+", category="+category+", visibility="+visibility+", icon="+icon+"}"; + } +} diff --git a/app/src/main/java/com/felkertech/n/tvnotification/utils/Popup.java b/app/src/main/java/com/felkertech/n/tvnotification/utils/Popup.java new file mode 100644 index 0000000..e181401 --- /dev/null +++ b/app/src/main/java/com/felkertech/n/tvnotification/utils/Popup.java @@ -0,0 +1,158 @@ +package com.felkertech.n.tvnotification.utils; + +import android.app.Notification; +import android.graphics.Bitmap; +import android.os.Build; +import android.service.notification.StatusBarNotification; +import android.util.Log; + +import com.felkertech.n.tvnotification.R; + +/** + * Created by N on 1/16/2015. + */ +public class Popup { + StatusBarNotification statusBarNotification; + Notification generic; + Notification pub; + Notification.Action[] actions; + public static final String TAG = "tvnotification::Popup"; + + //Attributes + int order; + int priority; + int color; + public String category; + public String title; + String publicTitle; + String bigText; + String publicBigText; + int visibility; + String pack; + int icon; + Bitmap largeIcon; + + //Constants + final static int PRIORITY_DEFAULT = 0; + final static int PRIORITY_HIGH = 1; + final static int PRIORITY_LOW = -1; + final static int PRIORITY_MAX = 2; + final static int PRIORITY_MIN = -2; + + final static int VISIBILITY_PRIVATE = 0; + final static int VISIBILITY_PUBLIC = 1; + final static int VISIBILITY_SECRET = -1; + + public Popup(StatusBarNotification sbn) { + this(sbn, 1); + } + public Popup(StatusBarNotification sbn, int o) { + generic = sbn.getNotification(); + priority = PRIORITY_DEFAULT; + visibility = VISIBILITY_PRIVATE; + color = R.color.accent_material_dark; + category = "status"; + order = o; + pack = sbn.getPackageName(); + icon = generic.icon; + largeIcon = generic.largeIcon; + title = "5"; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + pub = sbn.getNotification().publicVersion; + try { + priority = generic.priority; + } catch(Exception ignored) {} + try { + visibility = generic.visibility; + } catch (Exception ignored) {} + try { + color = generic.color; + } catch(Exception ignored) {} + try { + category = generic.category; + } catch(Exception ignored) {} + } + //TODO Try/Catch all + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + try { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if(generic.extras.containsKey(Notification.EXTRA_BIG_TEXT)) { + bigText = generic.extras.getCharSequence(Notification.EXTRA_BIG_TEXT).toString(); + publicBigText = bigText; + } + } + if(generic.extras.containsKey(Notification.EXTRA_TITLE)) { + title = generic.extras.getCharSequence(Notification.EXTRA_TITLE).toString(); + publicTitle = title; + Log.d(TAG, title); + } +// Log.d("tvnotification", title+" "+bigText); + if(bigText == null && generic.extras.containsKey(Notification.EXTRA_TEXT)) { + bigText = generic.extras.getCharSequence(Notification.EXTRA_TEXT).toString(); + publicBigText = bigText; + } +// Log.d("tvnotification", title+" "+bigText); + } catch(Exception e) { + Log.d(TAG, "KK "+e.getMessage()); + } + + try { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && pub != null) { + Log.d("tvnotification", title+"w "+bigText); + if(pub.extras.containsKey(Notification.EXTRA_BIG_TEXT)) { + publicBigText = pub.extras.getCharSequence(Notification.EXTRA_BIG_TEXT).toString(); + } + Log.d("tvnotification", title+"x "+bigText); + if(pub.extras.containsKey(Notification.EXTRA_TITLE)) { + publicTitle = pub.extras.getCharSequence(Notification.EXTRA_TITLE).toString(); + } + Log.d("tvnotification", title+"y "+bigText); + if(publicBigText == null && pub.extras.containsKey(Notification.EXTRA_TEXT)) { + publicBigText = pub.extras.getCharSequence(Notification.EXTRA_TEXT).toString(); + } + Log.d("tvnotification", title+"z "+bigText); + } + } catch(Exception e) { + Log.d(TAG, "LP "+e.getMessage()); + } + try { + actions = generic.actions; + } catch(Exception e) {} + Log.d(TAG, "Done building "+title); + } + Log.d(TAG, "Done building 2"+title); + } + public String toString() { + Log.d(TAG, title+"a "+bigText); + String s = order+" "+pack+"\n"+title+"; Priority "+priority+"; Category "+category+"\n"+bigText+"\n"; + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (hasActions()) { + for (Notification.Action a : actions) { + s = s + "Action " + a.title + "\n"; + } + } + } + return s+"\n\n"; + } + public boolean hasActions() { + if(actions == null) + return false; + return actions.length > 0; + } + + public String getTitle() { + return title; + } + + /** + * A popup is a large-scale class that contains what may be seen as conflicting info. There's + * a public title, and a private title. A public notification (maybe) and a generic note. + * This is a lot of info to manage. + * The Alert class is a compiled version of these data. Whichever algorithm is used to determine + * what should be displayed, an Alert can be formed with just that data. + * @return Alert object + */ + public Alert publicAlert() { + return new Alert(pack, publicTitle, publicBigText, color, priority, category, visibility, actions, icon, largeIcon); + } +} diff --git a/app/src/main/res/drawable-hdpi/Thumbs.db b/app/src/main/res/drawable-hdpi/Thumbs.db new file mode 100644 index 0000000..b462b2d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/Thumbs.db differ diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..96a442e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..359047d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..71c6d76 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..4df1894 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/layout-w480dp/popup_honeycomb.xml b/app/src/main/res/layout-w480dp/popup_honeycomb.xml new file mode 100644 index 0000000..33ee43f --- /dev/null +++ b/app/src/main/res/layout-w480dp/popup_honeycomb.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..85748ab --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,69 @@ + +