This repository has been archived by the owner on Oct 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bf695c3
Showing
43 changed files
with
1,514 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Nextlit | ||
[Jump to source](app/src/main/java/eu/biqqles/nextlit/) | ||
|
||
**Nextlit** is a simple app that activates the [LP5523](http://www.ti.com/product/LP5523)-controlled segmented LEDs on the back of the [Nextbit Robin](https://en.wikipedia.org/wiki/Nextbit_Robin) and employs them as a fancy notification light. It's something I've seen quite a few people asking for, and after playing around with the kernel interface for a bit I realised it is pretty simple to utilise them for purposes beyond their rather limited use in the stock ROM. | ||
|
||
Nextlit is very much a work in progress. Functionality is pretty basic at the moment, and it's currently more of a technology demonstrator than anything. The app allows you to preview the five patterns programmed into the LP5523 by Nextbit, and select one to activate when a notification is received. | ||
|
||
If you want to use the app for any more than just previewing the available patterns, you'll need to give it notification access. The application does not read or care about the contents of notifications; the only thing is does is count them. The notification service is not persistent between reboots, so you'll need to manually re-enable it in the app if you reboot. Currently the service makes no attempt to reduce battery drain, but it doesn't seem too bad in my limited testing. | ||
|
||
Make sure to turn off battery pulse in Settings if your ROM supports it and you want to see the LEDs while charging. | ||
|
||
The app needs root in order to write to `/sys`. It'll work on all LOS and AOSP -based ROMs, and *should* work on stock, though I haven't tested it. | ||
|
||
### To do | ||
- minimise battery drain | ||
- custom patterns (I think I know how to do this, but needs testing) | ||
- customisation: show only when screen off, app blacklist, etc. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
apply plugin: 'com.android.application' | ||
|
||
android { | ||
compileSdkVersion 26 | ||
defaultConfig { | ||
applicationId "eu.biqqles.nextlit" | ||
minSdkVersion 23 | ||
targetSdkVersion 25 | ||
versionCode 1 | ||
versionName '1.0' | ||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||
} | ||
buildTypes { | ||
release { | ||
minifyEnabled false | ||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||
} | ||
} | ||
productFlavors { | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation fileTree(include: ['*.jar'], dir: 'libs') | ||
implementation 'com.android.support:appcompat-v7:26.1.0' | ||
implementation 'com.android.support.constraint:constraint-layout:1.0.2' | ||
implementation 'com.android.support:design:26.1.0' | ||
testImplementation 'junit:junit:4.12' | ||
androidTestImplementation 'com.android.support.test:runner:1.0.1' | ||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Add project specific ProGuard rules here. | ||
# You can control the set of applied configuration files using the | ||
# proguardFiles setting in build.gradle. | ||
# | ||
# For more details, see | ||
# http://developer.android.com/guide/developing/tools/proguard.html | ||
|
||
# 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 *; | ||
#} | ||
|
||
# Uncomment this to preserve the line number information for | ||
# debugging stack traces. | ||
#-keepattributes SourceFile,LineNumberTable | ||
|
||
# If you keep the line number information, uncomment this to | ||
# hide the original source file name. | ||
#-renamesourcefileattribute SourceFile |
26 changes: 26 additions & 0 deletions
26
app/src/androidTest/java/eu/biqqles/nextlit/ExampleInstrumentedTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package eu.biqqles.nextlit; | ||
|
||
import android.content.Context; | ||
import android.support.test.InstrumentationRegistry; | ||
import android.support.test.runner.AndroidJUnit4; | ||
|
||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
|
||
import static org.junit.Assert.*; | ||
|
||
/** | ||
* Instrumented test, which will execute on an Android device. | ||
* | ||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> | ||
*/ | ||
@RunWith(AndroidJUnit4.class) | ||
public class ExampleInstrumentedTest { | ||
@Test | ||
public void useAppContext() throws Exception { | ||
// Context of the app under test. | ||
Context appContext = InstrumentationRegistry.getTargetContext(); | ||
|
||
assertEquals("eu.biqqles.nextlit", appContext.getPackageName()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="eu.biqqles.nextlit"> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:icon="@mipmap/ic_launcher" | ||
android:label="@string/app_name" | ||
android:roundIcon="@mipmap/ic_launcher_round" | ||
android:supportsRtl="true" | ||
android:theme="@style/AppTheme"> | ||
<activity | ||
android:name=".MainActivity" | ||
android:label="@string/app_name" | ||
android:screenOrientation="portrait" | ||
android:theme="@style/AppTheme.NoActionBar"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
<service android:name=".NotificationLightsService" | ||
android:label="@string/app_name" | ||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> | ||
<intent-filter> | ||
<action android:name="android.service.notification.NotificationListenerService" /> | ||
</intent-filter> | ||
</service> | ||
</application> | ||
|
||
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
|
||
/* | ||
* Copyright © 2018 biqqles. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
package eu.biqqles.nextlit; | ||
|
||
import android.util.Log; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.DataOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStreamReader; | ||
import java.text.MessageFormat; | ||
|
||
|
||
class LedControl { | ||
// Various methods to control the LP5523 which drives the Robin's segmented LEDs, using sysfs. | ||
private static final String PATTERN_FILE = "/sys/class/leds/lp5523:channel0/device/led_pattern"; | ||
private Process su = acquireRoot(); | ||
|
||
LedControl() throws IOException { | ||
// Check we have access to /sys (and therefore indirectly that we have root access), | ||
// otherwise throw an exception. | ||
String result = execCommand("ls /sys\n", true); | ||
if (result == null) { | ||
throw new IOException(); | ||
} | ||
} | ||
|
||
void setPattern(int pattern) { | ||
// Sets a predefined pattern programmed by Nextbit. | ||
// For descriptions of the six patterns available (with 0 being clear), see arrays.xml. | ||
clearPattern(); | ||
echoToFile(Integer.toString(pattern), PATTERN_FILE); | ||
} | ||
|
||
int getPattern() { | ||
// Gets the currently set pattern. | ||
String cmd = MessageFormat.format("cat {0}\n", PATTERN_FILE); | ||
try { | ||
return Integer.parseInt(execCommand(cmd, true)); | ||
} catch (NumberFormatException ex) { | ||
return 0; | ||
} | ||
} | ||
|
||
void clearPattern() { | ||
// Clears current pattern and set all leds to LOW. | ||
echoToFile("0", PATTERN_FILE); | ||
} | ||
|
||
private Process acquireRoot() { | ||
// Create a Process with root privileges. | ||
// It's worth noting that writing / reading from files with root privileges can only be | ||
// done through shell commands, and not Java itself. | ||
try { | ||
return Runtime.getRuntime().exec("su"); | ||
} catch (IOException e) { | ||
return null; | ||
} | ||
} | ||
|
||
private void echoToFile(String data, String path) { | ||
// Writes <data> to a file at <path>. | ||
String cmd = MessageFormat.format("echo \"{0}\" > {1}\n", data, path); | ||
execCommand(cmd, false); | ||
} | ||
|
||
private String execCommand(String command, boolean getOutput) { | ||
// Executes a shell command, with root privileges, and optionally returns the result. | ||
try { | ||
DataOutputStream dos = new DataOutputStream(su.getOutputStream()); | ||
BufferedReader br = new BufferedReader(new InputStreamReader(su.getInputStream())); | ||
dos.writeBytes(command); | ||
dos.flush(); | ||
if (getOutput) { | ||
return br.readLine(); | ||
} | ||
} catch (IOException ioe) { | ||
Log.e("NEXTLIT", MessageFormat.format("Failed to execute {} with error {}", | ||
command, ioe)); | ||
} | ||
return null; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
|
||
/* | ||
* Copyright © 2018 biqqles. | ||
* | ||
* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
*/ | ||
|
||
package eu.biqqles.nextlit; | ||
|
||
import android.content.Intent; | ||
import android.content.SharedPreferences; | ||
import android.os.Bundle; | ||
import android.provider.Settings; | ||
import android.service.notification.NotificationListenerService; | ||
import android.support.design.widget.Snackbar; | ||
import android.support.v7.app.AppCompatActivity; | ||
import android.support.v7.widget.Toolbar; | ||
import android.view.Menu; | ||
import android.view.MenuItem; | ||
import android.view.View; | ||
import android.widget.AdapterView; | ||
import android.widget.CompoundButton; | ||
import android.widget.Spinner; | ||
import android.widget.Switch; | ||
import android.widget.Toast; | ||
import android.widget.ToggleButton; | ||
|
||
import java.io.IOException; | ||
|
||
|
||
public class MainActivity extends AppCompatActivity { | ||
LedControl ledcontrol; | ||
SharedPreferences prefs; | ||
|
||
@Override | ||
protected void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
setContentView(R.layout.activity_main); | ||
Toolbar toolbar = findViewById(R.id.toolbar); | ||
setSupportActionBar(toolbar); | ||
|
||
prefs = this.getSharedPreferences("nextlit", MODE_PRIVATE); | ||
|
||
try { | ||
ledcontrol = new LedControl(); | ||
} catch (IOException ioe) { | ||
Toast.makeText(this, "App requires root access, closing", Toast.LENGTH_LONG).show(); | ||
finish(); | ||
} | ||
|
||
final Spinner patternSpinner = findViewById(R.id.patternSpinner); | ||
final ToggleButton previewButton = findViewById(R.id.previewButton); | ||
|
||
patternSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { | ||
public void onItemSelected(AdapterView adapterView, View view, int i, long l) { | ||
ledcontrol.clearPattern(); | ||
previewButton.setChecked(false); | ||
// update preferences | ||
// spinner index starts at 0 but patterns start at 1 (0 = clear) | ||
prefs.edit().putInt("predef_pattern", i + 1).apply(); | ||
} | ||
|
||
public void onNothingSelected(AdapterView<?> adapterView) { | ||
} | ||
}); | ||
|
||
patternSpinner.setSelection(prefs.getInt("predef_pattern", 1) - 1); | ||
|
||
previewButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { | ||
@Override | ||
public void onCheckedChanged(CompoundButton button, boolean checked) { | ||
if (checked) { | ||
String patternName = patternSpinner.getSelectedItem().toString(); | ||
int patternNumber = patternSpinner.getSelectedItemPosition(); | ||
|
||
ledcontrol.clearPattern(); | ||
ledcontrol.setPattern(patternNumber + 1); | ||
|
||
View view = findViewById(R.id.mainLayout); | ||
Snackbar.make(view, "Previewing " + patternName, Snackbar.LENGTH_LONG).show(); | ||
} else { | ||
ledcontrol.clearPattern(); | ||
} | ||
} | ||
}); | ||
|
||
} | ||
|
||
@Override | ||
public boolean onCreateOptionsMenu(Menu menu) { | ||
getMenuInflater().inflate(R.menu.menu_main, menu); | ||
|
||
// add listener to start/stop service when switch toggled | ||
final MenuItem item = menu.findItem(R.id.switch_item); | ||
item.setActionView(R.layout.actionbar_switch_layout); | ||
Switch serviceSwitch = item.getActionView().findViewById(R.id.serviceSwitch); | ||
|
||
serviceSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { | ||
@Override | ||
public void onCheckedChanged(CompoundButton button, boolean checked) { | ||
if (checked) { | ||
// take user to Notification Access | ||
String enabledNotificationListeners = Settings.Secure.getString( | ||
getContentResolver(), "enabled_notification_listeners"); | ||
if (!enabledNotificationListeners.contains(getPackageName())) { | ||
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS)); | ||
Toast.makeText(MainActivity.this, "Enable Nextlit", Toast.LENGTH_SHORT).show(); | ||
} | ||
startService(new Intent(MainActivity.this, NotificationListenerService.class)); | ||
Toast.makeText(MainActivity.this, "Service started", Toast.LENGTH_SHORT).show(); | ||
} | ||
else { | ||
ledcontrol.clearPattern(); | ||
stopService(new Intent(MainActivity.this, NotificationListenerService.class)); | ||
Toast.makeText(MainActivity.this, "Service stopped", Toast.LENGTH_SHORT).show(); | ||
} | ||
} | ||
}); | ||
return super.onCreateOptionsMenu(menu); | ||
} | ||
} | ||
|
||
|
||
|
||
|
||
|
Oops, something went wrong.