Android Oreo tweaks (#217)

* display notifications on Android 8.0 Oreo and higher using required notification channels
* fix daemon background service implementation for Android Oreo
This commit is contained in:
Akinwale Ariwodola 2018-08-06 11:51:55 +01:00 committed by GitHub
parent 338cd20132
commit 88fdc1b503
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1466 additions and 1376 deletions

6
Vagrantfile vendored
View file

@ -65,7 +65,7 @@ Vagrant.configure("2") do |config|
mkdir -p cd $HOME/.buildozer/android/platform/
wget -q 'https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip' -P $HOME/.buildozer/android/platform/
wget -q 'https://dl.google.com/android/android-sdk_r23-linux.tgz' -P $HOME/.buildozer/android/platform/
wget -q 'https://dl.google.com/android/repository/android-23_r02.zip' -P $HOME/.buildozer/android/platform/
wget -q 'https://dl.google.com/android/repository/platform-26_r02.zip' -P $HOME/.buildozer/android/platform/
wget -q 'https://dl.google.com/android/repository/build-tools_r26.0.1-linux.zip' -P $HOME/.buildozer/android/platform/
unzip -qq $HOME/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip -d $HOME/.buildozer/android/platform/
rm $HOME/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip
@ -73,8 +73,8 @@ Vagrant.configure("2") do |config|
rm $HOME/.buildozer/android/platform/android-sdk_r23-linux.tgz
mv $HOME/.buildozer/android/platform/android-sdk-linux $HOME/.buildozer/android/platform/android-sdk-23
unzip -qq $HOME/.buildozer/android/platform/android-23_r02.zip -d $HOME/.buildozer/android/platform/android-sdk-23/platforms
rm $HOME/.buildozer/android/platform/android-23_r02.zip
mv $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-6.0 $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-23
rm $HOME/.buildozer/android/platform/platform-26_r02.zip
mv $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-8.0.0 $HOME/.buildozer/android/platform/android-sdk-23/platforms/android-26
mkdir -p $HOME/.buildozer/android/platform/android-sdk-23/build-tools
unzip -qq $HOME/.buildozer/android/platform/build-tools_r26.0.1-linux.zip -d $HOME/.buildozer/android/platform/android-sdk-23/build-tools
rm $HOME/.buildozer/android/platform/build-tools_r26.0.1-linux.zip

2620
app/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
import React from 'react';
import { SETTINGS } from 'lbry-redux';
import { Text, View, ScrollView, Switch } from 'react-native';
import { Text, View, ScrollView, Switch, NativeModules } from 'react-native';
import PageHeader from '../../component/pageHeader';
import settingsStyle from '../../styles/settings';
@ -17,9 +17,6 @@ class SettingsPage extends React.PureComponent {
setClientSetting
} = this.props;
// If no true / false value set, default to true
const actualKeepDaemonRunning = (keepDaemonRunning === undefined || keepDaemonRunning === null) ? true : keepDaemonRunning;
return (
<View>
<PageHeader title={"Settings"}
@ -50,7 +47,12 @@ class SettingsPage extends React.PureComponent {
<Text style={settingsStyle.description}>Enable this option for quicker app launch and to keep the synchronisation with the blockchain up to date.</Text>
</View>
<View style={settingsStyle.switchContainer}>
<Switch value={actualKeepDaemonRunning} onValueChange={(value) => setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value)} />
<Switch value={keepDaemonRunning} onValueChange={(value) => {
setClientSetting(SETTINGS.KEEP_DAEMON_RUNNING, value);
if (NativeModules.DaemonServiceControl) {
NativeModules.DaemonServiceControl.setKeepDaemonRunning(value);
}
}} />
</View>
</View>
</ScrollView>

View file

@ -1,8 +1,22 @@
import { ACTIONS } from 'lbry-redux';
import { AsyncStorage } from 'react-native';
import { ACTIONS, SETTINGS } from 'lbry-redux';
getAsyncStorageItem = key => {
return AsyncStorage.getItem(key).then(value => {
if (['true', 'false'].indexOf(value) > -1) {
return value === 'true';
}
return value;
});
};
const reducers = {};
const defaultState = {
clientSettings: {}
clientSettings: {
backgroundPlayEnabled: getAsyncStorageItem(SETTINGS.BACKGROUND_PLAY_ENABLED),
keepDaemonRunning: getAsyncStorageItem(SETTINGS.KEEP_DAEMON_RUNNING),
showNsfw: getAsyncStorageItem(SETTINGS.SHOW_NSFW)
}
};
reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
@ -10,6 +24,7 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
const clientSettings = Object.assign({}, state.clientSettings);
clientSettings[key] = value;
AsyncStorage.setItem(key, String(value));
return Object.assign({}, state, {
clientSettings,

View file

@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git@v0.20.4#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap)
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
android.gradle_dependencies = com.android.support:appcompat-v7:26.0.1, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
# (str) python-for-android branch to use, defaults to master
#p4a.branch = stable

View file

@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git@v0.20.4#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap)
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
android.gradle_dependencies = com.android.support:appcompat-v7:26.0.1, com.facebook.react:react-native:0.55.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+
# (str) python-for-android branch to use, defaults to master
#p4a.branch = stable

View file

@ -36,7 +36,7 @@ version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma seperated e.g. requirements = sqlite3,kivy
requirements = openssl, sqlite3, hostpython2, android, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.15#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
requirements = openssl, sqlite3, hostpython2, android, distro, pyjnius, certifi==2018.4.16, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, seccure==0.3.1.3, attrs==18.1.0, pyasn1, pyasn1-modules, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, keyring==10.4.0, git+https://github.com/lbryio/lbryschema.git@v0.0.16#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/lbryio/lbry.git@v0.20.4#egg=lbrynet, asn1crypto, cryptography==2.2.2, pyopenssl==17.4.0, treq==17.8.0, funcsigs, mock, pbr, unqlite
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
@ -86,10 +86,10 @@ fullscreen = 0
#android.presplash_color = #FFFFFF
# (list) Permissions
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
android.permissions = ACCESS_NETWORK_STATE,BLUETOOTH,INTERNET,READ_EXTERNAL_STORAGE,READ_PHONE_STATE,WRITE_EXTERNAL_STORAGE
# (int) Android API to use
android.api = 23
android.api = 26
# (int) Minimum API required
android.minapi = 21

View file

@ -11,6 +11,9 @@ buildscript {
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/react/node_modules/react-native/android"

View file

@ -2,12 +2,15 @@ package io.lbry.browser;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.Context;
import android.os.Bundle;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.io.File;
@ -30,13 +33,43 @@ import org.renpy.android.ResourceManager;
*/
public class LbrynetService extends PythonService {
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.DAEMON_NOTIFICATION_CHANNEL";
public static String TAG = "LbrynetService";
public static LbrynetService serviceInstance;
@Override
public boolean canDisplayNotification() {
return false;
return true;
}
@Override
protected void doStartForeground(Bundle extras) {
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = "The LBRY Browser daemon service is running in the background.";
Context context = getApplicationContext();
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "LBRY Browser", NotificationManager.IMPORTANCE_LOW);
channel.setDescription("LBRY Broswer daemon service notification channel");
channel.setShowBadge(false);
notificationManager.createNotificationChannel(channel);
Intent contextIntent = new Intent(context, MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
Notification notification = builder.setContentTitle(serviceTitle)
.setContentText(serviceDescription)
.setContentIntent(pIntent)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_lbry)
.setOngoing(true)
.build();
startForeground(1, notification);
}
@Override

View file

@ -51,6 +51,8 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
public static final String DEVICE_ID_KEY = "deviceId";
public static final String SETTING_KEEP_DAEMON_RUNNING = "keepDaemonRunning";
/**
* Flag which indicates whether or not the service is running. Will be updated in the
* onResume method.
@ -218,7 +220,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
protected void onDestroy() {
// check service running setting and end it here
SharedPreferences sp = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
boolean shouldKeepDaemonRunning = sp.getBoolean("keepDaemonRunning", true);
boolean shouldKeepDaemonRunning = sp.getBoolean(SETTING_KEEP_DAEMON_RUNNING, true);
if (!shouldKeepDaemonRunning) {
serviceRunning = isServiceRunning(LbrynetService.class);
if (serviceRunning) {

View file

@ -3,12 +3,14 @@ package io.lbry.browser.reactmodules;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.SharedPreferences;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import io.lbry.browser.LbrynetService;
import io.lbry.browser.MainActivity;
import io.lbry.browser.ServiceHelper;
public class DaemonServiceControlModule extends ReactContextBaseJavaModule {
@ -33,4 +35,14 @@ public class DaemonServiceControlModule extends ReactContextBaseJavaModule {
public void stopService() {
ServiceHelper.stop(context, LbrynetService.class);
}
@ReactMethod
public void setKeepDaemonRunning(boolean value) {
if (context != null) {
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean(MainActivity.SETTING_KEEP_DAEMON_RUNNING, value);
editor.commit();
}
}
}

View file

@ -1,10 +1,13 @@
package io.lbry.browser.reactmodules;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
@ -34,6 +37,10 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
private static final String GROUP_DOWNLOADS = "io.lbry.browser.GROUP_DOWNLOADS";
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.DOWNLOADS_NOTIFICATION_CHANNEL";
private static boolean channelCreated = false;
public static final String NOTIFICATION_ID_KEY = "io.lbry.browser.notificationId";
public static final int GROUP_ID = 0;
@ -54,6 +61,18 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
return "LbryDownloadManager";
}
private void createNotificationChannel() {
// Only applies to Android 8.0 Oreo (API Level 26) or higher
if (!channelCreated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "LBRY Downloads", NotificationManager.IMPORTANCE_LOW);
channel.setDescription("LBRY file downloads");
notificationManager.createNotificationChannel(channel);
}
}
private void createNotificationGroup() {
if (!groupCreated) {
Intent intent = new Intent(context, NotificationDeletedReceiver.class);
@ -61,7 +80,7 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, GROUP_ID, intent, 0);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
builder.setContentTitle("Active downloads")
.setContentText("Active downloads")
.setSmallIcon(R.drawable.ic_file_download_black_24dp)
@ -84,10 +103,11 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
@ReactMethod
public void startDownload(String id, String fileName) {
createNotificationChannel();
createNotificationGroup();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
// The file URI is used as the unique ID
builder.setContentIntent(getLaunchPendingIntent(id))
.setContentTitle(String.format("Downloading %s...", fileName))
@ -114,6 +134,7 @@ public class DownloadManagerModule extends ReactContextBaseJavaModule {
return;
}
createNotificationChannel();
createNotificationGroup();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = builders.get(notificationId);

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB