4
.gitignore
vendored
|
@ -15,3 +15,7 @@ src/main/assets/index.android.bundle.meta
|
|||
lbry-android.keystore
|
||||
p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json
|
||||
.gitsecret/keys/random_seed
|
||||
|
||||
p4a/*.apk
|
||||
p4a/*.aar
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ stages:
|
|||
- deploy
|
||||
- release
|
||||
|
||||
build arm64 apk:
|
||||
build arm64 aar:
|
||||
stage: build
|
||||
image: lbry/android-base:latest
|
||||
before_script:
|
||||
|
@ -13,14 +13,11 @@ build arm64 apk:
|
|||
- git submodule update --init --force --recursive
|
||||
artifacts:
|
||||
paths:
|
||||
- bin/browser-*-release__arm64.apk
|
||||
- bin/lbrysdk-*-release__arm64.aar
|
||||
expire_in: 1 week
|
||||
script:
|
||||
- export PATH=/usr/bin:$PATH
|
||||
- echo "$PGP_PRIVATE_KEY" | gpg --batch --import
|
||||
- cd app
|
||||
- npm install
|
||||
- cd ..
|
||||
- wget -q 'https://eu.crystax.net/download/crystax-ndk-10.3.2-linux-x86_64.tar.xz' -P ~/.buildozer/android/
|
||||
- tar -xf ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz -C ~/.buildozer/android/
|
||||
- rm -rf ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
|
||||
|
@ -31,11 +28,11 @@ build arm64 apk:
|
|||
- git secret reveal
|
||||
- mv buildozer.spec.arm64.ci buildozer.spec
|
||||
- mkdir -p $CI_PROJECT_DIR/src/main/assets/blockchain && wget -q https://headers.lbry.io/blockchain_headers_latest -O $CI_PROJECT_DIR/src/main/assets/blockchain/headers
|
||||
- "./release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered"
|
||||
- cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release__arm64.apk
|
||||
- cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk /dev/null
|
||||
- "./build-release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered"
|
||||
- cp $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release.aar $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release__arm64.aar
|
||||
- cp $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release.aar /dev/null
|
||||
|
||||
build arm apk:
|
||||
build arm aar:
|
||||
stage: build2
|
||||
image: lbry/android-base:latest
|
||||
before_script:
|
||||
|
@ -44,15 +41,12 @@ build arm apk:
|
|||
- git submodule update --init --force --recursive
|
||||
artifacts:
|
||||
paths:
|
||||
- bin/browser-*-release__arm.apk
|
||||
- bin/lbrysdk-*-release__arm.aar
|
||||
expire_in: 1 week
|
||||
script:
|
||||
- export PATH=/usr/bin:$PATH
|
||||
- echo "$PGP_PRIVATE_KEY" | gpg --batch --import
|
||||
- cd app
|
||||
- npm install
|
||||
- cd ..
|
||||
- wget -q 'https://eu.crystax.net/download/crystax-ndk-10.3.2-linux-x86_64.tar.xz' -P ~/.buildozer/android/
|
||||
- wget -q 'https://eu.crystax.net/download/crystax-ndk-10.3.2-linux-x86_64.tar.xz' -P ~/.f.ozer/android/
|
||||
- tar -xf ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz -C ~/.buildozer/android/
|
||||
- rm -rf ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
|
||||
- ln -s ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21 ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
|
||||
|
@ -63,40 +57,38 @@ build arm apk:
|
|||
- git secret reveal
|
||||
- mv buildozer.spec.arm.ci buildozer.spec
|
||||
- mkdir -p $CI_PROJECT_DIR/src/main/assets/blockchain && wget -q https://headers.lbry.io/blockchain_headers_latest -O $CI_PROJECT_DIR/src/main/assets/blockchain/headers
|
||||
- "./release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered"
|
||||
- cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release__arm.apk
|
||||
- cp $CI_PROJECT_DIR/bin/browser-$BUILD_VERSION-release.apk /dev/null
|
||||
- "./build-release.sh | grep -Fv -e 'working:' -e 'copy' -e 'Compiling' --line-buffered"
|
||||
- cp $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release.aar $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release__arm.aar
|
||||
- cp $CI_PROJECT_DIR/bin/lbrysdk-$BUILD_VERSION-release.aar /dev/null
|
||||
|
||||
deploy build.lbry.io:
|
||||
image: python:latest
|
||||
stage: deploy
|
||||
dependencies:
|
||||
- build arm apk
|
||||
- build arm64 apk
|
||||
- build arm aar
|
||||
- build arm64 aar
|
||||
before_script:
|
||||
- pip install awscli
|
||||
- export BUILD_VERSION=$(cat $CI_PROJECT_DIR/src/main/python/main.py | grep --color=never -oP '([0-9]+\.?)+')
|
||||
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
||||
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
||||
- export BUILD_APK_FILENAME__32=lbrysdk-$BUILD_VERSION-release__arm.aar
|
||||
- export BUILD_APK_FILENAME__64=lbrysdk-$BUILD_VERSION-release__arm64.aar
|
||||
script:
|
||||
- aws s3 cp bin/$BUILD_APK_FILENAME__64 s3://build.lbry.io/android/build-${CI_PIPELINE_IID}_commit-${CI_COMMIT_SHA:0:7}/$BUILD_APK_FILENAME__64
|
||||
- aws s3 cp bin/$BUILD_APK_FILENAME__32 s3://build.lbry.io/android/build-${CI_PIPELINE_IID}_commit-${CI_COMMIT_SHA:0:7}/$BUILD_APK_FILENAME__32
|
||||
- aws s3 cp bin/$BUILD_APK_FILENAME__64 s3://build.lbry.io/android/push.apk
|
||||
|
||||
release apk:
|
||||
release aar:
|
||||
image: python:latest
|
||||
stage: release
|
||||
only:
|
||||
- tags
|
||||
dependencies:
|
||||
- build arm apk
|
||||
- build arm64 apk
|
||||
- build arm aar
|
||||
- build arm64 aar
|
||||
before_script:
|
||||
- pip install awscli githubrelease
|
||||
- export BUILD_VERSION=$(cat $CI_PROJECT_DIR/src/main/python/main.py | grep --color=never -oP '([0-9]+\.?)+')
|
||||
- export BUILD_APK_FILENAME__32=browser-$BUILD_VERSION-release__arm.apk
|
||||
- export BUILD_APK_FILENAME__64=browser-$BUILD_VERSION-release__arm64.apk
|
||||
- export BUILD_APK_FILENAME__32=lbrysdk-$BUILD_VERSION-release__arm.aar
|
||||
- export BUILD_APK_FILENAME__64=lbrysdk-$BUILD_VERSION-release__arm64.aar
|
||||
script:
|
||||
- githubrelease release lbryio/lbry-android create $CI_COMMIT_TAG --publish bin/$BUILD_APK_FILENAME__64 bin/$BUILD_APK_FILENAME__32
|
||||
- githubrelease release lbryio/lbry-android edit $CI_COMMIT_TAG --draft
|
||||
- aws s3 cp bin/$BUILD_APK_FILENAME__64 s3://build.lbry.io/android/latest.apk
|
||||
- githubrelease release lbryio/lbry-android-sdk create $CI_COMMIT_TAG --publish bin/$BUILD_APK_FILENAME__64 bin/$BUILD_APK_FILENAME__32
|
||||
- githubrelease release lbryio/lbry-android-sdk edit $CI_COMMIT_TAG --draft
|
||||
|
|
|
@ -11,14 +11,10 @@ RUN dpkg --add-architecture i386 && \
|
|||
autoconf autogen automake libtool libffi-dev build-essential \
|
||||
ccache git libncurses5:i386 libstdc++6:i386 \
|
||||
libgtk2.0-0:i386 libpangox-1.0-0:i386 libpangoxft-1.0-0:i386 libidn11:i386
|
||||
RUN npm install -g npm@latest
|
||||
RUN npm install -g yarn react-native-cli && \
|
||||
pip2 install --upgrade cython setuptools && \
|
||||
RUN pip2 install --upgrade cython setuptools && \
|
||||
pip2 install git+https://github.com/lbryio/buildozer.git@master && \
|
||||
ln -s /src/scripts/build-docker.sh /usr/local/bin/build && \
|
||||
adduser lbry-android --gecos GECOS --shell /bin/bash --disabled-password --home /home/lbry-android && \
|
||||
mkdir /home/lbry-android/.npm-packages && \
|
||||
echo "prefix=/home/lbry-android/.npm-packages" > /home/lbry-android/.npmrc && \
|
||||
chown -R lbry-android:lbry-android /home/lbry-android && \
|
||||
mkdir /src && \
|
||||
chown lbry-android:lbry-android /src && \
|
||||
|
|
2
build-debug.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
buildozer android debug
|
3
build-release.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
buildozer android release
|
||||
|
5
build.sh
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
cd app
|
||||
react-native bundle --platform android --dev false --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
cd ..
|
||||
buildozer android debug
|
|
@ -9,6 +9,9 @@ package.name = browser
|
|||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = io.lbry
|
||||
|
||||
# Package type for Android ('application' for APK or 'library' for AAR)
|
||||
package.type = library
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = ./src/main/python
|
||||
|
||||
|
@ -148,7 +151,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = androidx.legacy:legacy-support-v4:1.0.0, androidx.media:media:1.0.0, androidx.appcompat:appcompat:1.0.0, com.facebook.react:react-native:0.61.5, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-analytics:17.2.1, com.google.android.gms:play-services-base:17.1.0, androidx.exifinterface:exifinterface:1.0.0, com.facebook.fresco:animated-base-support:1.3.0, com.facebook.fresco:animated-gif:1.10.0, com.google.firebase:firebase-messaging:20.1.0
|
||||
android.gradle_dependencies = androidx.appcompat:appcompat:1.0.2
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
|
@ -9,6 +9,9 @@ package.name = browser
|
|||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = io.lbry
|
||||
|
||||
# Package type for Android ('application' for APK or 'library' for AAR)
|
||||
package.type = library
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = ./src/main/python
|
||||
|
||||
|
@ -148,7 +151,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = androidx.legacy:legacy-support-v4:1.0.0, androidx.media:media:1.0.0, androidx.appcompat:appcompat:1.0.0, com.facebook.react:react-native:0.61.5, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-analytics:17.2.1, com.google.android.gms:play-services-base:17.1.0, androidx.exifinterface:exifinterface:1.0.0, com.facebook.fresco:animated-base-support:1.3.0, com.facebook.fresco:animated-gif:1.10.0, com.google.firebase:firebase-messaging:20.1.0
|
||||
android.gradle_dependencies = androidx.appcompat:appcompat:1.0.2
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
|
@ -9,6 +9,9 @@ package.name = browser
|
|||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = io.lbry
|
||||
|
||||
# Package type for Android ('application' for APK or 'library' for AAR)
|
||||
package.type = library
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = ./src/main/python
|
||||
|
||||
|
@ -148,7 +151,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = androidx.legacy:legacy-support-v4:1.0.0, androidx.media:media:1.0.0, androidx.appcompat:appcompat:1.0.0, com.facebook.react:react-native:0.61.5, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-analytics:17.2.1, com.google.android.gms:play-services-base:17.1.0, androidx.exifinterface:exifinterface:1.0.0, com.facebook.fresco:animated-base-support:1.3.0, com.facebook.fresco:animated-gif:1.10.0, com.google.firebase:firebase-messaging:20.1.0
|
||||
android.gradle_dependencies = androidx.appcompat:appcompat:1.0.2
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
|
@ -9,6 +9,9 @@ package.name = browser
|
|||
# (str) Package domain (needed for android/ios packaging)
|
||||
package.domain = io.lbry
|
||||
|
||||
# Package type for Android ('application' for APK or 'library' for AAR)
|
||||
package.type = library
|
||||
|
||||
# (str) Source code where the main.py live
|
||||
source.dir = ./src/main/python
|
||||
|
||||
|
@ -148,7 +151,7 @@ android.react_src = ./app
|
|||
|
||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||
# bootstrap)
|
||||
android.gradle_dependencies = androidx.legacy:legacy-support-v4:1.0.0, androidx.media:media:1.0.0, androidx.appcompat:appcompat:1.0.0, com.facebook.react:react-native:0.61.5, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-analytics:17.2.1, com.google.android.gms:play-services-base:17.1.0, androidx.exifinterface:exifinterface:1.0.0, com.facebook.fresco:animated-base-support:1.3.0, com.facebook.fresco:animated-gif:1.10.0, com.google.firebase:firebase-messaging:20.1.0
|
||||
android.gradle_dependencies = androidx.appcompat:appcompat:1.0.2
|
||||
|
||||
# (str) python-for-android branch to use, defaults to master
|
||||
#p4a.branch = stable
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
cd app
|
||||
react-native bundle --platform android --dev true --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
cd ..
|
||||
buildozer android debug deploy
|
||||
|
|
@ -404,13 +404,8 @@ main.py that loads it.''')
|
|||
android_api=android_api,
|
||||
build_tools_version=build_tools_version)
|
||||
|
||||
render('settings.gradle', 'settings.gradle')
|
||||
|
||||
render('gradle.properties', 'gradle.properties')
|
||||
|
||||
## google-services.json for firebase
|
||||
render('google-services.json', 'google-services.json')
|
||||
|
||||
# copy icon drawables
|
||||
for folder in ('drawable-hdpi', 'drawable-mdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'):
|
||||
shutil.copy(
|
||||
|
|
|
@ -1,177 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Replace org.libsdl.app with the identifier of your game below, e.g.
|
||||
com.gamemaker.game
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="{{ args.package }}"
|
||||
android:versionCode="{{ args.numeric_version }}"
|
||||
android:versionName="{{ args.version }}"
|
||||
android:installLocation="auto">
|
||||
|
||||
<supports-screens
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:anyDensity="true"
|
||||
{% if args.min_sdk_version >= 9 %}
|
||||
android:xlargeScreens="true"
|
||||
{% endif %}
|
||||
/>
|
||||
android:versionName="{{ args.version }}">
|
||||
|
||||
<!-- Android 2.3.3 -->
|
||||
<uses-sdk android:minSdkVersion="{{ args.min_sdk_version }}" android:targetSdkVersion="{{ android_api }}" />
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
{% for perm in args.permissions %}
|
||||
{% if '.' in perm %}
|
||||
<uses-permission android:name="{{ perm }}" />
|
||||
{% else %}
|
||||
<uses-permission android:name="android.permission.{{ perm }}" />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if args.wakelock %}
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
{% endif %}
|
||||
|
||||
{% if args.billing_pubkey %}
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
{% endif %}
|
||||
|
||||
<!-- Create a Java class extending SDLActivity and place it in a
|
||||
directory under src matching the package, e.g.
|
||||
src/com/gamemaker/game/MyGame.java
|
||||
|
||||
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
|
||||
in the XML below.
|
||||
|
||||
An example Java class can be found in README-android.txt
|
||||
-->
|
||||
<application android:label="@string/app_name"
|
||||
android:icon="@drawable/icon"
|
||||
android:allowBackup="true"
|
||||
android:theme="@style/LbryAppTheme"
|
||||
android:hardwareAccelerated="true"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_lbry" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_color"
|
||||
android:resource="@color/lbryGreen" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="@string/default_notification_channel_id"/>
|
||||
|
||||
{% for m in args.meta_data %}
|
||||
<meta-data android:name="{{ m.split('=', 1)[0] }}" android:value="{{ m.split('=', 1)[-1] }}"/>{% endfor %}
|
||||
<meta-data android:name="wakelock" android:value="{% if args.wakelock %}1{% else %}0{% endif %}"/>
|
||||
|
||||
<!--activity android:name="io.lbry.lbrynet.ServiceControlActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
||||
android:screenOrientation="{{ args.orientation }}"
|
||||
-->
|
||||
|
||||
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
|
||||
|
||||
<activity android:name="io.lbry.browser.MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/LbryAppTheme"
|
||||
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
||||
android:screenOrientation="{{ args.orientation }}"
|
||||
android:launchMode="singleInstance"
|
||||
>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="lbry" />
|
||||
</intent-filter>
|
||||
|
||||
{% if args.launcher %}
|
||||
<intent-filter>
|
||||
<action android:name="org.kivy.LAUNCH" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="{{ url_scheme }}" />
|
||||
</intent-filter>
|
||||
{% else %}
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
{% endif %}
|
||||
|
||||
{%- if args.intent_filters -%}
|
||||
{{- args.intent_filters -}}
|
||||
{%- endif -%}
|
||||
</activity>
|
||||
|
||||
<receiver android:name="io.lbry.browser.receivers.NotificationDeletedReceiver" />
|
||||
|
||||
{% if args.launcher %}
|
||||
<activity android:name="org.kivy.android.launcher.ProjectChooser"
|
||||
android:icon="@drawable/icon"
|
||||
android:label="@string/app_name">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
{% endif %}
|
||||
|
||||
{% if service or args.launcher %}
|
||||
<service android:name="org.kivy.android.PythonService"
|
||||
android:process=":pythonservice" />
|
||||
{% endif %}
|
||||
{% for name in service_names %}
|
||||
<service android:name="{{ args.package }}.Service{{ name|capitalize }}"
|
||||
android:process=":service_{{ name }}" />
|
||||
{% endfor %}
|
||||
<application>
|
||||
<service android:name="{{ args.package }}.LbrynetService"
|
||||
android:process=":service_lbrynet" />
|
||||
<service android:name="{{ args.package }}.LbrynetTestRunnerService"
|
||||
android:process=":service_lbrynet_testrunner" />
|
||||
|
||||
<service
|
||||
android:name="{{ args.package }}.LbrynetMessagingService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
{% if args.billing_pubkey %}
|
||||
<service android:name="org.kivy.android.billing.BillingReceiver"
|
||||
android:process=":pythonbilling" />
|
||||
<receiver android:name="org.kivy.android.billing.BillingReceiver"
|
||||
android:process=":pythonbillingreceiver">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.vending.billing.IN_APP_NOTIFY" />
|
||||
<action android:name="com.android.vending.billing.RESPONSE_CODE" />
|
||||
<action android:name="com.android.vending.billing.PURCHASE_STATE_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
{% endif %}
|
||||
|
||||
<provider
|
||||
android:name="io.lbry.browser.LocalFileProvider"
|
||||
android:authorities="io.lbry.browser.fileprovider"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
android:process=":service_lbrynet"
|
||||
android:exported="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
@ -7,7 +7,6 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||
classpath 'com.google.gms:google-services:4.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,29 +16,13 @@ allprojects {
|
|||
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"
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/react/node_modules/jsc-android/dist")
|
||||
}
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: false,
|
||||
entryFile: "index.js"
|
||||
]
|
||||
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion {{ android_api }}
|
||||
|
@ -49,7 +32,6 @@ android {
|
|||
targetSdkVersion {{ android_api }}
|
||||
versionCode {{ args.numeric_version }} * 10 + 2
|
||||
versionName '{{ args.version }}'
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
multiDexEnabled true
|
||||
|
||||
ndk {
|
||||
|
@ -99,9 +81,6 @@ ext {
|
|||
buildToolsVersion = '{{ build_tools_version }}'
|
||||
minSdkVersion = {{ args.min_sdk_version }}
|
||||
targetSdkVersion = {{ android_api }}
|
||||
supportLibVersion = '28.0.0'
|
||||
googlePlayServicesVersion = '16.1.0'
|
||||
googlePlayServicesVisionVersion = '17.0.2'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -116,17 +95,6 @@ subprojects {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':@react-native-community_async-storage')
|
||||
compile project(':react-native-camera')
|
||||
compile project(':react-native-exception-handler')
|
||||
compile project(':react-native-fast-image')
|
||||
compile project(':react-native-fs')
|
||||
compile project(':react-native-gesture-handler')
|
||||
compile project(':react-native-reanimated')
|
||||
compile project(':react-native-snackbar')
|
||||
compile project(':react-native-video')
|
||||
compile project(':react-native-webview')
|
||||
compile project(':rn-fetch-blob')
|
||||
{%- for aar in aars %}
|
||||
compile(name: '{{ aar }}', ext: 'aar')
|
||||
{%- endfor -%}
|
||||
|
@ -135,16 +103,4 @@ dependencies {
|
|||
compile '{{ depend }}'
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "$rootDir/react/node_modules/hermes-engine/android/";
|
||||
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true
|
||||
|
|
|
@ -7,7 +7,6 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.4.2'
|
||||
classpath 'com.google.gms:google-services:4.2.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,29 +16,13 @@ allprojects {
|
|||
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"
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url("$rootDir/react/node_modules/jsc-android/dist")
|
||||
}
|
||||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
project.ext.react = [
|
||||
enableHermes: false,
|
||||
entryFile: "index.js"
|
||||
]
|
||||
|
||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion {{ android_api }}
|
||||
|
@ -47,9 +30,8 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion {{ args.min_sdk_version }}
|
||||
targetSdkVersion {{ android_api }}
|
||||
versionCode {{ args.numeric_version }} * 10 + 1
|
||||
versionCode {{ args.numeric_version }} * 10 + 2
|
||||
versionName '{{ args.version }}'
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
multiDexEnabled true
|
||||
|
||||
ndk {
|
||||
|
@ -99,9 +81,6 @@ ext {
|
|||
buildToolsVersion = '{{ build_tools_version }}'
|
||||
minSdkVersion = {{ args.min_sdk_version }}
|
||||
targetSdkVersion = {{ android_api }}
|
||||
supportLibVersion = '28.0.0'
|
||||
googlePlayServicesVersion = '16.1.0'
|
||||
googlePlayServicesVisionVersion = '17.0.2'
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
@ -116,17 +95,6 @@ subprojects {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':@react-native-community_async-storage')
|
||||
compile project(':react-native-camera')
|
||||
compile project(':react-native-exception-handler')
|
||||
compile project(':react-native-fast-image')
|
||||
compile project(':react-native-fs')
|
||||
compile project(':react-native-gesture-handler')
|
||||
compile project(':react-native-reanimated')
|
||||
compile project(':react-native-snackbar')
|
||||
compile project(':react-native-video')
|
||||
compile project(':react-native-webview')
|
||||
compile project(':rn-fetch-blob')
|
||||
{%- for aar in aars %}
|
||||
compile(name: '{{ aar }}', ext: 'aar')
|
||||
{%- endfor -%}
|
||||
|
@ -135,16 +103,4 @@ dependencies {
|
|||
compile '{{ depend }}'
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
if (enableHermes) {
|
||||
def hermesPath = "$rootDir/react/node_modules/hermes-engine/android/";
|
||||
|
||||
debugImplementation files(hermesPath + "hermes-debug.aar")
|
||||
releaseImplementation files(hermesPath + "hermes-release.aar")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "861521963586",
|
||||
"firebase_url": "https://lbry-mobile-builds-debug.firebaseio.com",
|
||||
"project_id": "lbry-mobile-builds-debug",
|
||||
"storage_bucket": "lbry-mobile-builds-debug.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:861521963586:android:592958d248940ab2",
|
||||
"android_client_info": {
|
||||
"package_name": "io.lbry.browser"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "861521963586-60cmvg5nmnrqkrc11a7bpmpv5ra2d50q.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyC7A3BYcIdZP9-Q-VNHoexYJWgZA7WzsPI"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "861521963586-60cmvg5nmnrqkrc11a7bpmpv5ra2d50q.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
rootProject.name = 'browser'
|
||||
include ':@react-native-community_async-storage'
|
||||
project(':@react-native-community_async-storage').projectDir = new File(rootProject.projectDir, './react/node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-camera'
|
||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-camera/android')
|
||||
include ':react-native-exception-handler'
|
||||
project(':react-native-exception-handler').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-exception-handler/android')
|
||||
include ':react-native-fast-image'
|
||||
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-fast-image/android')
|
||||
include ':react-native-fs'
|
||||
project(':react-native-fs').projectDir = new File(settingsDir, './react/node_modules/react-native-fs/android')
|
||||
include ':react-native-gesture-handler'
|
||||
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-gesture-handler/android')
|
||||
include ':react-native-reanimated'
|
||||
project(':react-native-reanimated').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-reanimated/android')
|
||||
include ':react-native-snackbar'
|
||||
project(':react-native-snackbar').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-snackbar/android')
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-video/android-exoplayer')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, './react/node_modules/react-native-webview/android')
|
||||
include ':rn-fetch-blob'
|
||||
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, './react/node_modules/rn-fetch-blob/android')
|
|
@ -868,9 +868,8 @@ class ToolchainCL(object):
|
|||
# gradle output apks somewhere else
|
||||
# and don't have version in file
|
||||
apk_dir = join(dist.dist_dir,
|
||||
"build", "outputs", "apk",
|
||||
args.build_mode)
|
||||
apk_glob = "*-{}.apk"
|
||||
"build", "outputs", "aar")
|
||||
apk_glob = "*-{}.aar"
|
||||
apk_add_version = True
|
||||
|
||||
else:
|
||||
|
@ -884,14 +883,14 @@ class ToolchainCL(object):
|
|||
output = shprint(ant, args.build_mode, _tail=20,
|
||||
_critical=True, _env=env)
|
||||
apk_dir = join(dist.dist_dir, "bin")
|
||||
apk_glob = "*-*-{}.apk"
|
||||
apk_glob = "*-*-{}.aar"
|
||||
apk_add_version = False
|
||||
|
||||
self.hook("after_apk_assemble")
|
||||
|
||||
info_main('# Copying APK to current directory')
|
||||
info_main('# Copying android package to current directory')
|
||||
|
||||
apk_re = re.compile(r'.*Package: (.*\.apk)$')
|
||||
apk_re = re.compile(r'.*Package: (.*\.aar)$')
|
||||
apk_file = None
|
||||
for line in reversed(output.splitlines()):
|
||||
m = apk_re.match(line)
|
||||
|
@ -900,7 +899,7 @@ class ToolchainCL(object):
|
|||
break
|
||||
|
||||
if not apk_file:
|
||||
info_main('# APK filename not found in build output. Guessing...')
|
||||
info_main('# AAR not found in build output. Guessing...')
|
||||
if args.build_mode == "release":
|
||||
suffixes = ("release", "release-unsigned")
|
||||
else:
|
||||
|
@ -909,20 +908,20 @@ class ToolchainCL(object):
|
|||
apks = glob.glob(join(apk_dir, apk_glob.format(suffix)))
|
||||
if apks:
|
||||
if len(apks) > 1:
|
||||
info('More than one built APK found... guessing you '
|
||||
info('More than one built AAR found... guessing you '
|
||||
'just built {}'.format(apks[-1]))
|
||||
apk_file = apks[-1]
|
||||
break
|
||||
else:
|
||||
raise BuildInterruptingException('Couldn\'t find the built APK')
|
||||
raise BuildInterruptingException('Couldn\'t find the built AAR')
|
||||
|
||||
info_main('# Found APK file: {}'.format(apk_file))
|
||||
info_main('# Found AAR file: {}'.format(apk_file))
|
||||
if apk_add_version:
|
||||
info('# Add version number to APK')
|
||||
info('# Add version number to AAR')
|
||||
apk_name, apk_suffix = basename(apk_file).split("-", 1)
|
||||
apk_file_dest = "{}-{}-{}".format(
|
||||
apk_name, build_args.version, apk_suffix)
|
||||
info('# APK renamed to {}'.format(apk_file_dest))
|
||||
info('# AAR renamed to {}'.format(apk_file_dest))
|
||||
shprint(sh.cp, apk_file, apk_file_dest)
|
||||
else:
|
||||
shprint(sh.cp, apk_file, './')
|
||||
|
|
16
release.sh
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
cd app
|
||||
react-native bundle --platform android --dev false --entry-file src/index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||
cd ..
|
||||
cp src/main/assets/index.android.bundle /dev/null
|
||||
version=$(cat src/main/python/main.py | grep --color=never -oP '([0-9]+\.?)+')
|
||||
buildozer android release <<< y
|
||||
jarsigner -verbose -sigalg SHA1withRSA \
|
||||
-digestalg SHA1 \
|
||||
-keystore lbry-android.keystore \
|
||||
-storepass $KEYSTORE_PASSWORD \
|
||||
bin/browser-$version-release-unsigned.apk lbry-android > /dev/null \
|
||||
&& mv bin/browser-$version-release-unsigned.apk bin/browser-$version-release-signed.apk
|
||||
~/.buildozer/android/platform/android-sdk-23/build-tools/28.0.3/zipalign -v 4 \
|
||||
bin/browser-$version-release-signed.apk bin/browser-$version-release.apk > /dev/null \
|
||||
&& rm bin/browser-$version-release-signed.apk
|
|
@ -3,7 +3,7 @@ set -e
|
|||
|
||||
exe() { ( echo "## $*"; $*; ) }
|
||||
|
||||
ANDROID_SDK_LICENSE=/home/lbry-android/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license
|
||||
ANDROID_SDK_LICENSE=/home/lbry-android-sdk/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license
|
||||
## VERSION and REPO variables are optional:
|
||||
## Use 'none' as a way to detect that none was provided by the user:
|
||||
VERSION=${VERSION:-none}
|
||||
|
@ -49,7 +49,7 @@ if [ $VERSION != "none" ] || [ $REPO != "none" ]; then
|
|||
VERSION=master
|
||||
fi
|
||||
if [ $REPO == "none" ]; then
|
||||
REPO="https://github.com/lbryio/lbry-android.git"
|
||||
REPO="https://github.com/lbryio/lbry-android-sdk.git"
|
||||
fi
|
||||
|
||||
## Clone from $REPO and checkout $VERSION:
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
(
|
||||
ANDROID_STUDIO_SDK=${ANDROID_STUDIO_SDK:-$HOME/Android/Sdk}
|
||||
LBRY_ANDROID_HOME=${LBRY_ANDROID_HOME:-$HOME/git/vendor/lbryio/lbry-android}
|
||||
LBRY_ANDROID_HOME=${LBRY_ANDROID_HOME:-$HOME/git/vendor/lbryio/lbry-android-sdk}
|
||||
LBRY_ANDROID_BUILDOZER_HOME=${LBRY_ANDROID_BUILDOZER_HOME:-$LBRY_ANDROID_HOME/.buildozer}
|
||||
LBRY_ANDROID_BUILDOZER_DOWNLOADS=${LBRY_ANDROID_BUILDOZER_DOWNLOADS:-$LBRY_ANDROID_HOME/.buildozer-downloads}
|
||||
LBRY_ANDROID_REPO=${LBRY_ANDROID_REPO:-https://www.github.com/lbryio/lbry-android}
|
||||
LBRY_ANDROID_REPO=${LBRY_ANDROID_REPO:-https://www.github.com/lbryio/lbry-android-sdk}
|
||||
LBRY_ANDROID_IMAGE=${LBRY_ANDROID_IMAGE:-lbry-android:local}
|
||||
|
||||
## Logger utility:
|
||||
|
@ -65,10 +65,10 @@
|
|||
mkdir -p $LBRY_ANDROID_BUILDOZER_DOWNLOADS
|
||||
exe sudo docker run --rm -it \
|
||||
-v $LBRY_ANDROID_HOME:/src \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android/.buildozer/ \
|
||||
-v $LBRY_ANDROID_BUILDOZER_DOWNLOADS:/home/lbry-android/.buildozer-downloads/ \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android-sdk/.buildozer/ \
|
||||
-v $LBRY_ANDROID_BUILDOZER_DOWNLOADS:/home/lbry-android-sdk/.buildozer-downloads/ \
|
||||
$LBRY_ANDROID_IMAGE \
|
||||
/home/lbry-android/bin/setup
|
||||
/home/lbry-android-sdk/bin/setup
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -96,9 +96,9 @@
|
|||
mkdir -p $LBRY_ANDROID_HOME/.gradle
|
||||
exe sudo docker run --rm -it \
|
||||
-v $LBRY_ANDROID_HOME:/src \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android/.buildozer/ \
|
||||
-v $LBRY_ANDROID_HOME/.gradle:/home/lbry-android/.gradle/ \
|
||||
-v $ANDROID_SDK_LICENSE:/home/lbry-android/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android-sdk/.buildozer/ \
|
||||
-v $LBRY_ANDROID_HOME/.gradle:/home/lbry-android-sdk/.gradle/ \
|
||||
-v $ANDROID_SDK_LICENSE:/home/lbry-android-sdk/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license \
|
||||
$LBRY_ANDROID_IMAGE
|
||||
}
|
||||
|
||||
|
@ -122,9 +122,9 @@
|
|||
clean() {
|
||||
exe sudo docker run --rm -it \
|
||||
-v $LBRY_ANDROID_HOME:/src \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android/.buildozer/ \
|
||||
-v $LBRY_ANDROID_HOME/.gradle:/home/lbry-android/.gradle/ \
|
||||
-v $ANDROID_SDK_LICENSE:/home/lbry-android/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license \
|
||||
-v $LBRY_ANDROID_BUILDOZER_HOME:/home/lbry-android-sdk/.buildozer/ \
|
||||
-v $LBRY_ANDROID_HOME/.gradle:/home/lbry-android-sdk/.gradle/ \
|
||||
-v $ANDROID_SDK_LICENSE:/home/lbry-android-sdk/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license \
|
||||
$LBRY_ANDROID_IMAGE /bin/bash -c "cd /src && buildozer android clean"
|
||||
}
|
||||
|
||||
|
@ -164,4 +164,3 @@
|
|||
echo "## - Builds the lbry-android apk"
|
||||
fi
|
||||
)
|
||||
|
||||
|
|
|
@ -1,157 +0,0 @@
|
|||
package io.lbry.browser;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.google.firebase.messaging.FirebaseMessagingService;
|
||||
import com.google.firebase.messaging.RemoteMessage;
|
||||
|
||||
import io.lbry.browser.reactmodules.UtilityModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LbrynetMessagingService extends FirebaseMessagingService {
|
||||
|
||||
private static final String TAG = "LbrynetMessagingService";
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.LBRY_ENGAGEMENT_CHANNEL";
|
||||
|
||||
private static final String TYPE_SUBSCRIPTION = "subscription";
|
||||
|
||||
private static final String TYPE_REWARD = "reward";
|
||||
|
||||
private static final String TYPE_INTERESTS = "interests";
|
||||
|
||||
private static final String TYPE_CREATOR = "creator";
|
||||
|
||||
private FirebaseAnalytics firebaseAnalytics;
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(RemoteMessage remoteMessage) {
|
||||
Log.d(TAG, "From: " + remoteMessage.getFrom());
|
||||
if (firebaseAnalytics == null) {
|
||||
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
|
||||
}
|
||||
|
||||
Map<String, String> payload = remoteMessage.getData();
|
||||
if (payload != null) {
|
||||
String type = payload.get("type");
|
||||
String url = payload.get("target");
|
||||
String title = payload.get("title");
|
||||
String body = payload.get("body");
|
||||
String name = payload.get("name"); // notification name
|
||||
if (type != null && getEnabledTypes().indexOf(type) > -1 && body != null && body.trim().length() > 0) {
|
||||
// only log the receive event for valid notifications received
|
||||
if (firebaseAnalytics != null) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("name", name);
|
||||
firebaseAnalytics.logEvent("lbry_notification_receive", bundle);
|
||||
}
|
||||
|
||||
sendNotification(title, body, type, url, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewToken(String token) {
|
||||
Log.d(TAG, "Refreshed token: " + token);
|
||||
|
||||
// If you want to send messages to this application instance or
|
||||
// manage this apps subscriptions on the server side, send the
|
||||
// Instance ID token to your app server.
|
||||
sendRegistrationToServer(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist token to third-party servers.
|
||||
*
|
||||
* Modify this method to associate the user's FCM InstanceID token with any server-side account
|
||||
* maintained by your application.
|
||||
*
|
||||
* @param token The new token.
|
||||
*/
|
||||
private void sendRegistrationToServer(String token) {
|
||||
// TODO: Implement this method to send token to your app server.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show a simple notification containing the received FCM message.
|
||||
*
|
||||
* @param messageBody FCM message body received.
|
||||
*/
|
||||
private void sendNotification(String title, String messageBody, String type, String url, String name) {
|
||||
//Intent intent = new Intent(this, MainActivity.class);
|
||||
//intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (url == null) {
|
||||
if (TYPE_REWARD.equals(type)) {
|
||||
url = "lbry://?rewards";
|
||||
} else {
|
||||
// default to home page
|
||||
url = "lbry://?discover";
|
||||
}
|
||||
}
|
||||
|
||||
Intent launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
launchIntent.putExtra("notification_name", name);
|
||||
launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, launchIntent, PendingIntent.FLAG_ONE_SHOT);
|
||||
|
||||
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
NotificationCompat.Builder notificationBuilder =
|
||||
new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
||||
.setColor(ContextCompat.getColor(this, R.color.lbryGreen))
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.setContentTitle(title)
|
||||
.setContentText(messageBody)
|
||||
.setAutoCancel(true)
|
||||
.setSound(defaultSoundUri)
|
||||
.setContentIntent(pendingIntent);
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
// Since android Oreo notification channel is needed.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, "LBRY Engagement", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
notificationManager.notify(9898, notificationBuilder.build());
|
||||
}
|
||||
|
||||
public List<String> getEnabledTypes() {
|
||||
SharedPreferences sp = getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
List<String> enabledTypes = new ArrayList<String>();
|
||||
|
||||
if (sp.getBoolean(UtilityModule.RECEIVE_SUBSCRIPTION_NOTIFICATIONS, true)) {
|
||||
enabledTypes.add(TYPE_SUBSCRIPTION);
|
||||
}
|
||||
if (sp.getBoolean(UtilityModule.RECEIVE_REWARD_NOTIFICATIONS, true)) {
|
||||
enabledTypes.add(TYPE_REWARD);
|
||||
}
|
||||
if (sp.getBoolean(UtilityModule.RECEIVE_INTERESTS_NOTIFICATIONS, true)) {
|
||||
enabledTypes.add(TYPE_INTERESTS);
|
||||
}
|
||||
if (sp.getBoolean(UtilityModule.RECEIVE_CREATOR_NOTIFICATIONS, true)) {
|
||||
enabledTypes.add(TYPE_CREATOR);
|
||||
}
|
||||
|
||||
return enabledTypes;
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
package io.lbry.browser;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.kivy.android.PythonService;
|
||||
import org.renpy.android.AssetExtract;
|
||||
import org.renpy.android.ResourceManager;
|
||||
|
||||
public class LbrynetTestRunnerService extends PythonService {
|
||||
|
||||
public static String TAG = "LbrynetTestRunnerService";
|
||||
|
||||
public static LbrynetTestRunnerService serviceInstance;
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// Extract files
|
||||
File app_root_file = new File(getAppRoot());
|
||||
unpackData("private", app_root_file);
|
||||
|
||||
if (intent == null) {
|
||||
intent = ServiceHelper.buildIntent(
|
||||
getApplicationContext(), "", LbrynetTestRunnerService.class, "testrunnerservice");
|
||||
}
|
||||
|
||||
serviceInstance = this;
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
serviceInstance = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void broadcastTestRunnerOutput(String output) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(ServiceControlActivity.TEST_RUNNER_OUTPUT);
|
||||
intent.putExtra("output", output);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public void unpackData(final String resource, File target) {
|
||||
Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName());
|
||||
|
||||
// The version of data in memory and on disk.
|
||||
ResourceManager resourceManager = new ResourceManager(getApplicationContext());
|
||||
String data_version = resourceManager.getString(resource + "_version");
|
||||
String disk_version = null;
|
||||
|
||||
Log.v(TAG, "Data version is " + data_version);
|
||||
|
||||
// If no version, no unpacking is necessary.
|
||||
if (data_version == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the current disk version, if any.
|
||||
String filesDir = target.getAbsolutePath();
|
||||
String disk_version_fn = filesDir + "/" + resource + ".version";
|
||||
|
||||
try {
|
||||
byte buf[] = new byte[64];
|
||||
InputStream is = new FileInputStream(disk_version_fn);
|
||||
int len = is.read(buf);
|
||||
disk_version = new String(buf, 0, len);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
disk_version = "";
|
||||
}
|
||||
|
||||
// If the disk data is out of date, extract it and write the
|
||||
// version file.
|
||||
// if (! data_version.equals(disk_version)) {
|
||||
if (! data_version.equals(disk_version)) {
|
||||
Log.v(TAG, "Extracting " + resource + " assets.");
|
||||
|
||||
recursiveDelete(target);
|
||||
target.mkdirs();
|
||||
|
||||
AssetExtract ae = new AssetExtract(getApplicationContext());
|
||||
if (!ae.extractTar(resource + ".mp3", target.getAbsolutePath())) {
|
||||
//toastError("Could not extract " + resource + " data.");
|
||||
Log.e(TAG, "Could not extract " + resource + " data.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Write .nomedia.
|
||||
new File(target, ".nomedia").createNewFile();
|
||||
|
||||
// Write version file.
|
||||
FileOutputStream os = new FileOutputStream(disk_version_fn);
|
||||
os.write(data_version.getBytes());
|
||||
os.close();
|
||||
} catch (Exception e) {
|
||||
Log.w("python", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void recursiveDelete(File f) {
|
||||
if (f.isDirectory()) {
|
||||
for (File r : f.listFiles()) {
|
||||
recursiveDelete(r);
|
||||
}
|
||||
}
|
||||
f.delete();
|
||||
}
|
||||
|
||||
public String getAppRoot() {
|
||||
String app_root = getApplicationContext().getFilesDir().getAbsolutePath() + "/app";
|
||||
return app_root;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package io.lbry.browser;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
public class LocalFileProvider extends FileProvider {
|
||||
|
||||
}
|
|
@ -1,875 +0,0 @@
|
|||
package io.lbry.browser;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.Manifest;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.azendoo.reactnativesnackbar.SnackbarPackage;
|
||||
import com.brentvatne.react.ReactVideoPackage;
|
||||
import com.dylanvann.fastimage.FastImageViewPackage;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.ReadableNativeArray;
|
||||
import com.facebook.react.bridge.ReadableNativeMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
import com.facebook.react.shell.MainReactPackage;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.reactnativecommunity.asyncstorage.AsyncStoragePackage;
|
||||
import com.reactnativecommunity.webview.RNCWebViewPackage;
|
||||
import com.rnfs.RNFSPackage;
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
||||
import com.swmansion.gesturehandler.react.RNGestureHandlerPackage;
|
||||
import com.swmansion.reanimated.ReanimatedPackage;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
|
||||
import io.lbry.browser.reactpackages.LbryReactPackage;
|
||||
import io.lbry.browser.reactmodules.BackgroundMediaModule;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.reactnative.camera.RNCameraPackage;
|
||||
|
||||
public class MainActivity extends FragmentActivity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
|
||||
|
||||
private static Activity currentActivity = null;
|
||||
|
||||
private static final int OVERLAY_PERMISSION_REQ_CODE = 101;
|
||||
|
||||
private static final int STORAGE_PERMISSION_REQ_CODE = 201;
|
||||
|
||||
private static final int PHONE_STATE_PERMISSION_REQ_CODE = 202;
|
||||
|
||||
private static final int RECEIVE_SMS_PERMISSION_REQ_CODE = 203;
|
||||
|
||||
public static final int DOCUMENT_PICKER_RESULT_CODE = 301;
|
||||
|
||||
private BroadcastReceiver notificationsReceiver;
|
||||
|
||||
private BroadcastReceiver smsReceiver;
|
||||
|
||||
private BroadcastReceiver stopServiceReceiver;
|
||||
|
||||
private BroadcastReceiver downloadEventReceiver;
|
||||
|
||||
private FirebaseAnalytics firebaseAnalytics;
|
||||
|
||||
private ReactRootView mReactRootView;
|
||||
|
||||
private ReactInstanceManager mReactInstanceManager;
|
||||
|
||||
public static final String SHARED_PREFERENCES_NAME = "LBRY";
|
||||
|
||||
public static final String SALT_KEY = "salt";
|
||||
|
||||
public static final String DEVICE_ID_KEY = "deviceId";
|
||||
|
||||
public static final String SOURCE_NOTIFICATION_ID_KEY = "sourceNotificationId";
|
||||
|
||||
public static final String SETTING_KEEP_DAEMON_RUNNING = "keepDaemonRunning";
|
||||
|
||||
public static List<Integer> downloadNotificationIds = new ArrayList<Integer>();
|
||||
|
||||
/**
|
||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||
* onResume method.
|
||||
*/
|
||||
private boolean serviceRunning;
|
||||
|
||||
private boolean receivedStopService;
|
||||
|
||||
private PermissionListener permissionListener;
|
||||
|
||||
protected String getMainComponentName() {
|
||||
return "LBRYApp";
|
||||
}
|
||||
|
||||
public static LaunchTiming CurrentLaunchTiming;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
CurrentLaunchTiming = new LaunchTiming(new Date());
|
||||
super.onCreate(savedInstanceState);
|
||||
currentActivity = this;
|
||||
|
||||
SoLoader.init(this, false);
|
||||
|
||||
// Register the stop service receiver (so that we close the activity if the user requests the service to stop)
|
||||
registerStopReceiver();
|
||||
|
||||
// Register SMS receiver for handling verification texts
|
||||
registerSmsReceiver();
|
||||
|
||||
// Register the receiver to emit download events
|
||||
registerDownloadEventReceiver();
|
||||
|
||||
// Start the daemon service if it is not started
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
CurrentLaunchTiming.setColdStart(true);
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
checkNotificationOpenIntent(getIntent());
|
||||
|
||||
mReactRootView = new RNGestureHandlerEnabledRootView(this);
|
||||
mReactInstanceManager = ReactInstanceManager.builder()
|
||||
.setApplication(getApplication())
|
||||
.setCurrentActivity(this)
|
||||
.setBundleAssetName("index.android.bundle")
|
||||
.setJSMainModulePath("index")
|
||||
.addPackage(new MainReactPackage())
|
||||
.addPackage(new AsyncStoragePackage())
|
||||
.addPackage(new FastImageViewPackage())
|
||||
.addPackage(new RNCWebViewPackage())
|
||||
.addPackage(new ReactVideoPackage())
|
||||
.addPackage(new ReanimatedPackage())
|
||||
.addPackage(new RNCameraPackage())
|
||||
.addPackage(new RNFetchBlobPackage())
|
||||
.addPackage(new RNFSPackage())
|
||||
.addPackage(new RNGestureHandlerPackage())
|
||||
.addPackage(new SnackbarPackage())
|
||||
.addPackage(new LbryReactPackage())
|
||||
.setUseDeveloperSupport(BuildConfig.DEBUG)
|
||||
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||
.build();
|
||||
mReactRootView.startReactApplication(mReactInstanceManager, "LBRYApp", null);
|
||||
|
||||
registerNotificationsReceiver();
|
||||
|
||||
setContentView(mReactRootView);
|
||||
}
|
||||
|
||||
private void checkNotificationOpenIntent(Intent intent) {
|
||||
if (intent != null) {
|
||||
String notificationName = intent.getStringExtra("notification_name");
|
||||
if (notificationName != null) {
|
||||
logNotificationOpen(notificationName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logNotificationOpen(String name) {
|
||||
if (firebaseAnalytics == null) {
|
||||
firebaseAnalytics = FirebaseAnalytics.getInstance(this);
|
||||
}
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("name", name);
|
||||
firebaseAnalytics.logEvent("lbry_notification_open", bundle);
|
||||
}
|
||||
|
||||
private void registerDownloadEventReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_EVENT);
|
||||
downloadEventReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String downloadAction = intent.getStringExtra("action");
|
||||
String uri = intent.getStringExtra("uri");
|
||||
String outpoint = intent.getStringExtra("outpoint");
|
||||
String fileInfoJson = intent.getStringExtra("file_info");
|
||||
|
||||
|
||||
if (uri == null || outpoint == null || (fileInfoJson == null && !"abort".equals(downloadAction))) {
|
||||
return;
|
||||
}
|
||||
|
||||
String eventName = null;
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("uri", uri);
|
||||
params.putString("outpoint", outpoint);
|
||||
|
||||
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||
if ("abort".equals(downloadAction)) {
|
||||
eventName = "onDownloadAborted";
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject json = new JSONObject(fileInfoJson);
|
||||
WritableMap fileInfo = JSONObjectToMap(json);
|
||||
params.putMap("fileInfo", fileInfo);
|
||||
|
||||
if (DownloadManager.ACTION_UPDATE.equals(downloadAction)) {
|
||||
double progress = intent.getDoubleExtra("progress", 0);
|
||||
params.putDouble("progress", progress);
|
||||
eventName = "onDownloadUpdated";
|
||||
} else {
|
||||
eventName = (DownloadManager.ACTION_START.equals(downloadAction)) ? "onDownloadStarted" : "onDownloadCompleted";
|
||||
}
|
||||
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
};
|
||||
registerReceiver(downloadEventReceiver, intentFilter);
|
||||
}
|
||||
|
||||
private void registerStopReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(LbrynetService.ACTION_STOP_SERVICE);
|
||||
stopServiceReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
MainActivity.this.receivedStopService = true;
|
||||
MainActivity.this.finish();
|
||||
}
|
||||
};
|
||||
registerReceiver(stopServiceReceiver, intentFilter);
|
||||
}
|
||||
|
||||
private void registerNotificationsReceiver() {
|
||||
// Background media receiver
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BackgroundMediaModule.ACTION_PLAY);
|
||||
filter.addAction(BackgroundMediaModule.ACTION_PAUSE);
|
||||
notificationsReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext != null) {
|
||||
if (BackgroundMediaModule.ACTION_PLAY.equals(action)) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onBackgroundPlayPressed", null);
|
||||
}
|
||||
if (BackgroundMediaModule.ACTION_PAUSE.equals(action)) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onBackgroundPausePressed", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
registerReceiver(notificationsReceiver, filter);
|
||||
}
|
||||
|
||||
public void registerSmsReceiver() {
|
||||
if (!hasPermission(Manifest.permission.RECEIVE_SMS, this)) {
|
||||
// don't create the receiver if we don't have the read sms permission
|
||||
return;
|
||||
}
|
||||
|
||||
IntentFilter smsFilter = new IntentFilter();
|
||||
smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
|
||||
smsReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// Get the message
|
||||
Bundle bundle = intent.getExtras();
|
||||
if (bundle != null) {
|
||||
Object[] pdus = (Object[]) bundle.get("pdus");
|
||||
if (pdus != null && pdus.length > 0) {
|
||||
SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdus[0]);
|
||||
String text = sms.getMessageBody();
|
||||
if (text == null || text.trim().length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve verification code from the text message if it contains
|
||||
// the strings "lbry", "verification code" and the colon (following the expected format)
|
||||
text = text.toLowerCase();
|
||||
if (text.indexOf("lbry") > -1 && text.indexOf("verification code") > -1 && text.indexOf(":") > -1) {
|
||||
String code = text.substring(text.lastIndexOf(":") + 1).trim();
|
||||
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext != null) {
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("code", code);
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onVerificationCodeReceived", params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
registerReceiver(smsReceiver, smsFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
// SYSTEM_ALERT_WINDOW permission not granted...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (requestCode == DOCUMENT_PICKER_RESULT_CODE) {
|
||||
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||
if (reactContext != null) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
Uri fileUri = data.getData();
|
||||
String filePath = getRealPathFromURI_API19(this, fileUri);
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("path", filePath);
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onDocumentPickerFilePicked", params);
|
||||
} else if (resultCode == RESULT_CANCELED) {
|
||||
// user canceled or request failed
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onDocumentPickerCanceled", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Activity getActivity() {
|
||||
Activity activity = new Activity();
|
||||
activity = currentActivity;
|
||||
return activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
|
||||
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
|
||||
switch (requestCode) {
|
||||
case STORAGE_PERMISSION_REQ_CODE:
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
if (BuildConfig.DEBUG && !Settings.canDrawOverlays(this)) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + getPackageName()));
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
|
||||
}
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onStoragePermissionGranted", null);
|
||||
}
|
||||
} else {
|
||||
// Permission not granted
|
||||
/*Toast.makeText(this,
|
||||
"LBRY requires access to your device storage to be able to download files and media." +
|
||||
" Please enable the storage permission and restart the app.", Toast.LENGTH_LONG).show();*/
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onStoragePermissionRefused", null);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PHONE_STATE_PERMISSION_REQ_CODE:
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// Permission granted. Emit an onPhoneStatePermissionGranted event
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onPhoneStatePermissionGranted", null);
|
||||
}
|
||||
} else {
|
||||
// Permission not granted. Simply show a message.
|
||||
Toast.makeText(this,
|
||||
"No permission granted to read your device state. Rewards cannot be claimed.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_SMS_PERMISSION_REQ_CODE:
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// Permission granted. Emit an onPhoneStatePermissionGranted event
|
||||
if (reactContext != null) {
|
||||
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit("onReceiveSmsPermissionGranted", null);
|
||||
}
|
||||
|
||||
// register the receiver
|
||||
if (smsReceiver == null) {
|
||||
registerSmsReceiver();
|
||||
}
|
||||
} else {
|
||||
// Permission not granted. Simply show a message.
|
||||
Toast.makeText(this,
|
||||
"No permission granted to receive your SMS messages. You may have to enter the verification code manually.",
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (permissionListener != null) {
|
||||
permissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
public static String acquireDeviceId(Context context) {
|
||||
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String id = null;
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
id = telephonyManager.getImei(); // GSM
|
||||
if (id == null) {
|
||||
id = telephonyManager.getMeid(); // CDMA
|
||||
}
|
||||
} else {
|
||||
id = telephonyManager.getDeviceId();
|
||||
}
|
||||
} catch (SecurityException ex) {
|
||||
// Maybe the permission was not granted? Try to acquire permission
|
||||
checkPhoneStatePermission(context);
|
||||
} catch (Exception ex) {
|
||||
// id could not be obtained. Display a warning that rewards cannot be claimed.
|
||||
}
|
||||
|
||||
if (id == null || id.trim().length() == 0) {
|
||||
Toast.makeText(context, "Rewards cannot be claimed because we could not identify your device.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
SharedPreferences sp = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(DEVICE_ID_KEY, id);
|
||||
editor.commit();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDefaultOnBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostPause(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
SharedPreferences sp = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (!serviceRunning) {
|
||||
ServiceHelper.start(this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostResume(this, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// check service running setting and end it here
|
||||
SharedPreferences sp = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
boolean shouldKeepDaemonRunning = sp.getBoolean(SETTING_KEEP_DAEMON_RUNNING, true);
|
||||
if (!shouldKeepDaemonRunning) {
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
if (serviceRunning) {
|
||||
ServiceHelper.stop(this, LbrynetService.class);
|
||||
}
|
||||
}
|
||||
|
||||
if (notificationsReceiver != null) {
|
||||
unregisterReceiver(notificationsReceiver);
|
||||
notificationsReceiver = null;
|
||||
}
|
||||
|
||||
if (smsReceiver != null) {
|
||||
unregisterReceiver(smsReceiver);
|
||||
smsReceiver = null;
|
||||
}
|
||||
|
||||
if (downloadEventReceiver != null) {
|
||||
unregisterReceiver(downloadEventReceiver);
|
||||
downloadEventReceiver = null;
|
||||
}
|
||||
|
||||
if (stopServiceReceiver != null) {
|
||||
unregisterReceiver(stopServiceReceiver);
|
||||
stopServiceReceiver = null;
|
||||
}
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationManager.cancel(BackgroundMediaModule.NOTIFICATION_ID);
|
||||
notificationManager.cancel(DownloadManager.DOWNLOAD_NOTIFICATION_GROUP_ID);
|
||||
if (downloadNotificationIds != null) {
|
||||
for (int i = 0; i < downloadNotificationIds.size(); i++) {
|
||||
notificationManager.cancel(downloadNotificationIds.get(i));
|
||||
}
|
||||
}
|
||||
if (receivedStopService || !isServiceRunning(LbrynetService.class)) {
|
||||
notificationManager.cancelAll();
|
||||
}
|
||||
super.onDestroy();
|
||||
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onHostDestroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onBackPressed();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) {
|
||||
permissionListener = listener;
|
||||
ActivityCompat.requestPermissions(this, permissions, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
if (mReactInstanceManager != null) {
|
||||
mReactInstanceManager.onNewIntent(intent);
|
||||
}
|
||||
|
||||
if (intent != null) {
|
||||
int sourceNotificationId = intent.getIntExtra(SOURCE_NOTIFICATION_ID_KEY, -1);
|
||||
if (sourceNotificationId > -1) {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
|
||||
notificationManager.cancel(sourceNotificationId);
|
||||
}
|
||||
|
||||
checkNotificationOpenIntent(intent);
|
||||
}
|
||||
|
||||
super.onNewIntent(intent);
|
||||
}
|
||||
|
||||
private static void checkPermission(String permission, int requestCode, String rationale, Context context, boolean forceRequest) {
|
||||
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Should we show an explanation?
|
||||
if (!forceRequest && ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission)) {
|
||||
Toast.makeText(context, rationale, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
ActivityCompat.requestPermissions((Activity) context, new String[] { permission }, requestCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPermission(String permission, int requestCode, String rationale, Context context) {
|
||||
checkPermission(permission, requestCode, rationale, context, false);
|
||||
}
|
||||
|
||||
public static boolean hasPermission(String permission, Context context) {
|
||||
return (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
public static void checkPhoneStatePermission(Context context) {
|
||||
// Request read phone state permission
|
||||
checkPermission(Manifest.permission.READ_PHONE_STATE,
|
||||
PHONE_STATE_PERMISSION_REQ_CODE,
|
||||
"LBRY requires optional access to be able to identify your device for rewards. " +
|
||||
"You cannot claim rewards without this permission.",
|
||||
context,
|
||||
true);
|
||||
}
|
||||
|
||||
public static void checkReceiveSmsPermission(Context context) {
|
||||
// Request read phone state permission
|
||||
checkPermission(Manifest.permission.RECEIVE_SMS,
|
||||
RECEIVE_SMS_PERMISSION_REQ_CODE,
|
||||
"LBRY requires access to be able to read a verification text message for rewards.",
|
||||
context,
|
||||
true);
|
||||
}
|
||||
|
||||
public static void checkStoragePermission(Context context) {
|
||||
// Request read phone state permission
|
||||
checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
STORAGE_PERMISSION_REQ_CODE,
|
||||
"LBRY requires access to your device storage to be able to download files and media.",
|
||||
context,
|
||||
true);
|
||||
}
|
||||
|
||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(serviceInfo.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static WritableMap JSONObjectToMap(JSONObject jsonObject) throws JSONException {
|
||||
WritableMap map = Arguments.createMap();
|
||||
Iterator<String> keys = jsonObject.keys();
|
||||
while(keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
Object value = jsonObject.get(key);
|
||||
if (value instanceof JSONArray) {
|
||||
map.putArray(key, JSONArrayToList((JSONArray) value));
|
||||
} else if (value instanceof JSONObject) {
|
||||
map.putMap(key, JSONObjectToMap((JSONObject) value));
|
||||
} else if (value instanceof Boolean) {
|
||||
map.putBoolean(key, (Boolean) value);
|
||||
} else if (value instanceof Integer) {
|
||||
map.putInt(key, (Integer) value);
|
||||
} else if (value instanceof Double) {
|
||||
map.putDouble(key, (Double) value);
|
||||
} else if (value instanceof String) {
|
||||
map.putString(key, (String) value);
|
||||
} else {
|
||||
map.putString(key, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static WritableArray JSONArrayToList(JSONArray jsonArray) throws JSONException {
|
||||
WritableArray array = Arguments.createArray();
|
||||
for(int i = 0; i < jsonArray.length(); i++) {
|
||||
Object value = jsonArray.get(i);
|
||||
if (value instanceof JSONArray) {
|
||||
array.pushArray(JSONArrayToList((JSONArray) value));
|
||||
} else if (value instanceof JSONObject) {
|
||||
array.pushMap(JSONObjectToMap((JSONObject) value));
|
||||
} else if (value instanceof Boolean) {
|
||||
array.pushBoolean((Boolean) value);
|
||||
} else if (value instanceof Integer) {
|
||||
array.pushInt((Integer) value);
|
||||
} else if (value instanceof Double) {
|
||||
array.pushDouble((Double) value);
|
||||
} else if (value instanceof String) {
|
||||
array.pushString((String) value);
|
||||
} else {
|
||||
array.pushString(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://gist.github.com/HBiSoft/15899990b8cd0723c3a894c1636550a8
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
|
||||
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
|
||||
// DocumentProvider
|
||||
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||
// ExternalStorageProvider
|
||||
if (isExternalStorageDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
// This is for checking Main Memory
|
||||
if ("primary".equalsIgnoreCase(type)) {
|
||||
if (split.length > 1) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
} else {
|
||||
return Environment.getExternalStorageDirectory() + "/";
|
||||
}
|
||||
// This is for checking SD Card
|
||||
} else {
|
||||
return "storage" + "/" + docId.replace(":", "/");
|
||||
}
|
||||
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
String fileName = getFilePath(context, uri);
|
||||
if (fileName != null) {
|
||||
return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
|
||||
}
|
||||
|
||||
String id = DocumentsContract.getDocumentId(uri);
|
||||
if (id.startsWith("raw:")) {
|
||||
id = id.replaceFirst("raw:", "");
|
||||
File file = new File(id);
|
||||
if (file.exists())
|
||||
return id;
|
||||
}
|
||||
|
||||
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
final String[] selectionArgs = new String[]{
|
||||
split[1]
|
||||
};
|
||||
|
||||
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||
}
|
||||
}
|
||||
// MediaStore (and general)
|
||||
else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||
|
||||
// Return the remote address
|
||||
if (isGooglePhotosUri(uri))
|
||||
return uri.getLastPathSegment();
|
||||
|
||||
return getDataColumn(context, uri, null, null);
|
||||
}
|
||||
// File
|
||||
else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||
return uri.getPath();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||
Cursor cursor = null;
|
||||
final String column = "_data";
|
||||
final String[] projection = {
|
||||
column
|
||||
};
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(column);
|
||||
return cursor.getString(index);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static String getFilePath(Context context, Uri uri) {
|
||||
Cursor cursor = null;
|
||||
final String[] projection = { MediaStore.MediaColumns.DISPLAY_NAME };
|
||||
|
||||
try {
|
||||
cursor = context.getContentResolver().query(uri, projection, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
final int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
return cursor.getString(index);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is ExternalStorageProvider.
|
||||
*/
|
||||
public static boolean isExternalStorageDocument(Uri uri) {
|
||||
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is DownloadsProvider.
|
||||
*/
|
||||
public static boolean isDownloadsDocument(Uri uri) {
|
||||
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is MediaProvider.
|
||||
*/
|
||||
public static boolean isMediaDocument(Uri uri) {
|
||||
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param uri The Uri to check.
|
||||
* @return Whether the Uri authority is Google Photos.
|
||||
*/
|
||||
public static boolean isGooglePhotosUri(Uri uri) {
|
||||
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||
}
|
||||
|
||||
public static class LaunchTiming {
|
||||
private Date start;
|
||||
private boolean coldStart;
|
||||
|
||||
public LaunchTiming(Date start) {
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public Date getStart() {
|
||||
return start;
|
||||
}
|
||||
public void setStart(Date start) {
|
||||
this.start = start;
|
||||
}
|
||||
public boolean isColdStart() {
|
||||
return coldStart;
|
||||
}
|
||||
public void setColdStart(boolean coldStart) {
|
||||
this.coldStart = coldStart;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
package io.lbry.browser;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class ServiceControlActivity extends Activity {
|
||||
|
||||
public static ServiceControlActivity activityInstance;
|
||||
|
||||
private IntentFilter intentFilter;
|
||||
|
||||
public static String TEST_RUNNER_OUTPUT = "io.lbry.browser.TEST_RUNNER_OUTPUT";
|
||||
|
||||
/**
|
||||
* Flag which indicates whether or not the service is running. Will be updated in the
|
||||
* onResume method.
|
||||
*/
|
||||
private boolean serviceRunning;
|
||||
|
||||
/**
|
||||
* Button which will start or stop the service.
|
||||
*/
|
||||
private Button startStopButton;
|
||||
|
||||
private Button runTestsButton;
|
||||
|
||||
private TextView testRunnerOutput;
|
||||
|
||||
/**
|
||||
* Service status text.
|
||||
*/
|
||||
private TextView serviceStatusText;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_service_control);
|
||||
intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(TEST_RUNNER_OUTPUT);
|
||||
|
||||
startStopButton = (Button) findViewById(R.id.btn_start_stop);
|
||||
runTestsButton = (Button) findViewById(R.id.btn_run_tests);
|
||||
serviceStatusText = (TextView) findViewById(R.id.text_service_status);
|
||||
testRunnerOutput = (TextView) findViewById(R.id.test_runner_output);
|
||||
|
||||
startStopButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (serviceRunning) {
|
||||
ServiceHelper.stop(ServiceControlActivity.this, LbrynetService.class);
|
||||
} else {
|
||||
ServiceHelper.start(ServiceControlActivity.this, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
updateServiceStatus();
|
||||
}
|
||||
});
|
||||
|
||||
runTestsButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
boolean testsRunning = isServiceRunning(LbrynetTestRunnerService.class);
|
||||
if (!testsRunning) {
|
||||
ServiceHelper.start(
|
||||
ServiceControlActivity.this, "", LbrynetTestRunnerService.class, "testrunnerservice");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
registerReceiver(testRunnerOutputReceiver, intentFilter);
|
||||
|
||||
activityInstance = this;
|
||||
serviceRunning = isServiceRunning(LbrynetService.class);
|
||||
updateServiceStatus();
|
||||
}
|
||||
|
||||
private BroadcastReceiver testRunnerOutputReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TEST_RUNNER_OUTPUT.equals(intent.getAction())) {
|
||||
String output = intent.getStringExtra("output");
|
||||
updateTestRunnerOutput(output);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
unregisterReceiver(testRunnerOutputReceiver);
|
||||
// set the activity instance to null on pause in order to prevent NullPointerException
|
||||
// if the activity shuts down prematurely, for example
|
||||
activityInstance = null;
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
public void updateServiceStatus() {
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (serviceRunning) {
|
||||
startStopButton.setText(R.string.stop);
|
||||
serviceStatusText.setText(R.string.running);
|
||||
serviceStatusText.setTextColor(getResources().getColor(R.color.green));
|
||||
} else {
|
||||
startStopButton.setText(R.string.start);
|
||||
serviceStatusText.setText(R.string.stopped);
|
||||
serviceStatusText.setTextColor(getResources().getColor(R.color.red));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateTestRunnerOutput(String output) {
|
||||
testRunnerOutput.setText(formatTestRunnerOutput(output));
|
||||
}
|
||||
|
||||
private boolean isServiceRunning(Class<?> serviceClass) {
|
||||
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
|
||||
for (ActivityManager.RunningServiceInfo serviceInfo : manager.getRunningServices(Integer.MAX_VALUE)) {
|
||||
if (serviceClass.getName().equals(serviceInfo.service.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Spanned formatTestRunnerOutput(String output) {
|
||||
output = output.replace("[OK]", "<font color=\"#008000\">[OK]</font>");
|
||||
output = output.replace("[ERROR]", "<font color=\"#ff0000\">[ERROR]</font>");
|
||||
output = output.replace("[FAILURE]", "<font color=\"#cc0000\">[FAILURE]</font>");
|
||||
|
||||
return Html.fromHtml(output);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
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.R;
|
||||
|
||||
public class BackgroundMediaModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final int NOTIFICATION_ID = 30;
|
||||
|
||||
public static final String ACTION_PLAY = "io.lbry.browser.ACTION_MEDIA_PLAY";
|
||||
|
||||
public static final String ACTION_PAUSE = "io.lbry.browser.ACTION_MEDIA_PAUSE";
|
||||
|
||||
public static final String ACTION_STOP = "io.lbry.browser.ACTION_MEDIA_STOP";
|
||||
|
||||
private Context context;
|
||||
|
||||
public BackgroundMediaModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BackgroundMedia";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void showPlaybackNotification(String title, String publisher, String uri, boolean paused) {
|
||||
Intent contextIntent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Intent playIntent = new Intent();
|
||||
playIntent.setAction(ACTION_PLAY);
|
||||
PendingIntent playPendingIntent = PendingIntent.getBroadcast(context, 0, playIntent, 0);
|
||||
|
||||
Intent pauseIntent = new Intent();
|
||||
pauseIntent.setAction(ACTION_PAUSE);
|
||||
PendingIntent pausePendingIntent = PendingIntent.getBroadcast(context, 0, pauseIntent, 0);
|
||||
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, LbrynetService.NOTIFICATION_CHANNEL_ID);
|
||||
builder.setColor(ContextCompat.getColor(context, R.color.lbryGreen))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentTitle(title)
|
||||
.setContentText(publisher)
|
||||
.setGroup(LbrynetService.GROUP_SERVICE)
|
||||
.setOngoing(!paused)
|
||||
.setSmallIcon(paused ? android.R.drawable.ic_media_pause : android.R.drawable.ic_media_play)
|
||||
.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
|
||||
.setShowActionsInCompactView(0))
|
||||
.addAction(paused ? android.R.drawable.ic_media_play : android.R.drawable.ic_media_pause,
|
||||
paused ? "Play" : "Pause",
|
||||
paused ? playPendingIntent : pausePendingIntent)
|
||||
.build();
|
||||
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hidePlaybackNotification() {
|
||||
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationChannel;
|
||||
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 {
|
||||
|
||||
private Context context;
|
||||
|
||||
public DaemonServiceControlModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "DaemonServiceControl";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void startService() {
|
||||
ServiceHelper.start(context, "", LbrynetService.class, "lbrynetservice");
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.firebase.iid.FirebaseInstanceId;
|
||||
import com.google.firebase.iid.InstanceIdResult;
|
||||
|
||||
import io.lbry.browser.BuildConfig;
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.Utils;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class FirebaseModule extends ReactContextBaseJavaModule {
|
||||
|
||||
private Context context;
|
||||
|
||||
private FirebaseAnalytics firebaseAnalytics;
|
||||
|
||||
public FirebaseModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
this.firebaseAnalytics = FirebaseAnalytics.getInstance(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Firebase";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setCurrentScreen(String name, final Promise promise) {
|
||||
final Activity activity = getCurrentActivity();
|
||||
if (activity != null && firebaseAnalytics != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
firebaseAnalytics.setCurrentScreen(activity, name, Utils.capitalizeAndStrip(name));
|
||||
}
|
||||
});
|
||||
}
|
||||
promise.resolve(true);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void track(String name, ReadableMap payload) {
|
||||
Bundle bundle = new Bundle();
|
||||
if (payload != null) {
|
||||
HashMap<String, Object> payloadMap = payload.toHashMap();
|
||||
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value != null) {
|
||||
bundle.putString(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firebaseAnalytics != null) {
|
||||
firebaseAnalytics.logEvent(name, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void logException(boolean fatal, String message, String error) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("message", message);
|
||||
bundle.putString("error", error);
|
||||
if (firebaseAnalytics != null) {
|
||||
firebaseAnalytics.logEvent(fatal ? "reactjs_exception" : "reactjs_warning", bundle);
|
||||
}
|
||||
|
||||
if (fatal) {
|
||||
Toast.makeText(context,
|
||||
"An application error occurred which has been automatically logged. " +
|
||||
"If you keep seeing this message, please provide feedback to the LBRY " +
|
||||
"team by emailing hello@lbry.com.",
|
||||
Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getMessagingToken(final Promise promise) {
|
||||
FirebaseInstanceId.getInstance().getInstanceId()
|
||||
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
|
||||
@Override
|
||||
public void onComplete(Task<InstanceIdResult> task) {
|
||||
if (!task.isSuccessful()) {
|
||||
promise.reject("Firebase getInstanceId call failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get new Instance ID token
|
||||
String token = task.getResult().getToken();
|
||||
promise.resolve(token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void logLaunchTiming() {
|
||||
Date end = new Date();
|
||||
MainActivity.LaunchTiming currentTiming = MainActivity.CurrentLaunchTiming;
|
||||
if (currentTiming == null) {
|
||||
// no start timing data, so skip this
|
||||
return;
|
||||
}
|
||||
|
||||
long totalTimeMs = end.getTime() - currentTiming.getStart().getTime();
|
||||
String eventName = currentTiming.isColdStart() ? "app_cold_start" : "app_warm_start";
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong("total_ms", totalTimeMs);
|
||||
bundle.putLong("total_seconds", new Double(Math.ceil(totalTimeMs / 1000.0)).longValue());
|
||||
if (firebaseAnalytics != null) {
|
||||
firebaseAnalytics.logEvent(eventName, bundle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
|
||||
public class FirstRunModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
private SharedPreferences sp;
|
||||
|
||||
public FirstRunModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
this.sp = reactContext.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "FirstRun";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void isFirstRun(final Promise promise) {
|
||||
// If firstRun flag does not exist, default to true
|
||||
boolean firstRun = sp.getBoolean("firstRun", true);
|
||||
promise.resolve(firstRun);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void firstRunCompleted() {
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstRun", false);
|
||||
editor.commit();
|
||||
|
||||
FirebaseAnalytics firebase = FirebaseAnalytics.getInstance(context);
|
||||
if (firebase != null) {
|
||||
Bundle bundle = new Bundle();
|
||||
firebase.logEvent("first_run_completed", bundle);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.Manifest;
|
||||
import android.media.ThumbnailUtils;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GalleryModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
public GalleryModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Gallery";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getVideos(Promise promise) {
|
||||
WritableArray items = Arguments.createArray();
|
||||
List<GalleryItem> videos = loadVideos();
|
||||
for (int i = 0; i < videos.size(); i++) {
|
||||
items.pushMap(videos.get(i).toMap());
|
||||
}
|
||||
|
||||
promise.resolve(items);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getThumbnailPath(Promise promise) {
|
||||
if (context != null) {
|
||||
File cacheDir = context.getExternalCacheDir();
|
||||
String thumbnailPath = String.format("%s/thumbnails", cacheDir.getAbsolutePath());
|
||||
promise.resolve(thumbnailPath);
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getUploadsPath(Promise promise) {
|
||||
if (context != null) {
|
||||
String baseFolder = Utils.getExternalStorageDir(context);
|
||||
String uploadsPath = String.format("%s/LBRY/Uploads", baseFolder);
|
||||
File uploadsDir = new File(uploadsPath);
|
||||
if (!uploadsDir.isDirectory()) {
|
||||
uploadsDir.mkdirs();
|
||||
}
|
||||
promise.resolve(uploadsPath);
|
||||
}
|
||||
|
||||
promise.reject("The content could not be saved to the device. Please check your storage permissions.");
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createVideoThumbnail(String targetId, String filePath, Promise promise) {
|
||||
(new AsyncTask<Void, Void, String>() {
|
||||
protected String doInBackground(Void... param) {
|
||||
String thumbnailPath = null;
|
||||
|
||||
if (context != null) {
|
||||
Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(filePath, MediaStore.Video.Thumbnails.MINI_KIND);
|
||||
File cacheDir = context.getExternalCacheDir();
|
||||
thumbnailPath = String.format("%s/thumbnails/%s.png", cacheDir.getAbsolutePath(), targetId);
|
||||
|
||||
File file = new File(thumbnailPath);
|
||||
try (FileOutputStream os = new FileOutputStream(thumbnailPath)) {
|
||||
thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os);
|
||||
os.close();
|
||||
} catch (IOException ex) {
|
||||
promise.reject("Could not create a thumbnail for the video");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnailPath;
|
||||
}
|
||||
|
||||
public void onPostExecute(String thumbnailPath) {
|
||||
if (thumbnailPath != null && thumbnailPath.trim().length() > 0) {
|
||||
promise.resolve(thumbnailPath);
|
||||
}
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void createImageThumbnail(String targetId, String filePath, Promise promise) {
|
||||
(new AsyncTask<Void, Void, String>() {
|
||||
protected String doInBackground(Void... param) {
|
||||
String thumbnailPath = null;
|
||||
FileOutputStream os = null;
|
||||
try {
|
||||
Bitmap source = BitmapFactory.decodeFile(filePath);
|
||||
// MINI_KIND dimensions
|
||||
Bitmap thumbnail = Bitmap.createScaledBitmap(source, 512, 384, false);
|
||||
|
||||
if (context != null) {
|
||||
File cacheDir = context.getExternalCacheDir();
|
||||
thumbnailPath = String.format("%s/thumbnails/%s.png", cacheDir.getAbsolutePath(), targetId);
|
||||
os = new FileOutputStream(thumbnailPath);
|
||||
if (thumbnail != null) {
|
||||
thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os);
|
||||
}
|
||||
os.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
promise.reject("Could not create a thumbnail for the image");
|
||||
return null;
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException ex) {
|
||||
// ignoe
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnailPath;
|
||||
}
|
||||
|
||||
public void onPostExecute(String thumbnailPath) {
|
||||
if (thumbnailPath != null && thumbnailPath.trim().length() > 0) {
|
||||
promise.resolve(thumbnailPath);
|
||||
}
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private List<GalleryItem> loadVideos() {
|
||||
String[] projection = {
|
||||
MediaStore.MediaColumns._ID,
|
||||
MediaStore.MediaColumns.DATA,
|
||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||
MediaStore.MediaColumns.MIME_TYPE,
|
||||
MediaStore.Video.Media.DURATION
|
||||
};
|
||||
|
||||
List<String> ids = new ArrayList<String>();
|
||||
List<GalleryItem> items = new ArrayList<GalleryItem>();
|
||||
Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null,
|
||||
String.format("%s DESC", MediaStore.MediaColumns.DATE_MODIFIED));
|
||||
while (cursor.moveToNext()) {
|
||||
int idColumn = cursor.getColumnIndex(MediaStore.MediaColumns._ID);
|
||||
int nameColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
int typeColumn = cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE);
|
||||
int pathColumn = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
|
||||
int durationColumn = cursor.getColumnIndex(MediaStore.Video.Media.DURATION);
|
||||
|
||||
String id = cursor.getString(idColumn);
|
||||
GalleryItem item = new GalleryItem();
|
||||
item.setId(id);
|
||||
item.setName(cursor.getString(nameColumn));
|
||||
item.setType(cursor.getString(typeColumn));
|
||||
item.setFilePath(cursor.getString(pathColumn));
|
||||
items.add(item);
|
||||
ids.add(id);
|
||||
}
|
||||
|
||||
checkThumbnails(ids);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private void checkThumbnails(final List<String> ids) {
|
||||
(new AsyncTask<Void, Void, Void>() {
|
||||
protected Void doInBackground(Void... param) {
|
||||
if (context != null) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
String id = ids.get(i);
|
||||
File cacheDir = context.getExternalCacheDir();
|
||||
File thumbnailsDir = new File(String.format("%s/thumbnails", cacheDir.getAbsolutePath()));
|
||||
if (!thumbnailsDir.isDirectory()) {
|
||||
thumbnailsDir.mkdirs();
|
||||
}
|
||||
|
||||
String thumbnailPath = String.format("%s/%s.png", thumbnailsDir.getAbsolutePath(), id);
|
||||
File file = new File(thumbnailPath);
|
||||
if (!file.exists()) {
|
||||
// save the thumbnail to the path
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = 1;
|
||||
Bitmap thumbnail = MediaStore.Video.Thumbnails.getThumbnail(
|
||||
resolver, Long.parseLong(id), MediaStore.Video.Thumbnails.MINI_KIND, options);
|
||||
if (thumbnail != null) {
|
||||
try (FileOutputStream os = new FileOutputStream(thumbnailPath)) {
|
||||
thumbnail.compress(Bitmap.CompressFormat.PNG, 80, os);
|
||||
} catch (IOException ex) {
|
||||
// skip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file.exists() && file.length() > 0 && GalleryModule.this.context != null) {
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("id", id);
|
||||
((ReactApplicationContext) GalleryModule.this.context).getJSModule(
|
||||
DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onGalleryThumbnailChecked", params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void onPostExecute(Void result) {
|
||||
if (GalleryModule.this.context != null) {
|
||||
((ReactApplicationContext) GalleryModule.this.context).getJSModule(
|
||||
DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("onAllGalleryThumbnailsChecked", null);
|
||||
}
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
private static class GalleryItem {
|
||||
private String id;
|
||||
|
||||
private int duration;
|
||||
|
||||
private String filePath;
|
||||
|
||||
private String name;
|
||||
|
||||
private String type;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public WritableMap toMap() {
|
||||
WritableMap map = Arguments.createMap();
|
||||
map.putString("id", id);
|
||||
map.putString("name", name);
|
||||
map.putString("filePath", filePath);
|
||||
map.putString("type", type);
|
||||
map.putInt("duration", duration);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void canUseCamera(final Promise promise) {
|
||||
promise.resolve(MainActivity.hasPermission(Manifest.permission.CAMERA, MainActivity.getActivity()));
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.Utils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class RequestsModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
public RequestsModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Requests";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void get(final String url, final Promise promise) {
|
||||
(new AsyncTask<Void, Void, String>() {
|
||||
@Override
|
||||
protected String doInBackground(Void... params) {
|
||||
try {
|
||||
return Utils.performRequest(url);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(String response) {
|
||||
if (response == null) {
|
||||
promise.reject(String.format("Request to %s returned null.", url));
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(response);
|
||||
}
|
||||
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void lbryioCall(String authToken, final Promise promise) {
|
||||
// get the auth token here, or let the app pass it in?
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void lbryCall(final Promise promise) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
public class ScreenOrientationModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
public ScreenOrientationModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ScreenOrientation";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void unlockOrientation() {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void lockOrientationLandscape() {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void lockOrientationPortrait() {
|
||||
Activity activity = getCurrentActivity();
|
||||
if (activity != null) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import io.lbry.browser.MainActivity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class StatePersistorModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
private List<ReadableMap> queue;
|
||||
|
||||
private ReadableMap filter;
|
||||
|
||||
private ReadableMap lastState;
|
||||
|
||||
private AsyncTask persistTask;
|
||||
|
||||
public StatePersistorModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
queue = new ArrayList<ReadableMap>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "StatePersistor";
|
||||
}
|
||||
|
||||
/*private WritableMap filterState(ReadableMap state) {
|
||||
WritableMap filteredState = Arguments.createMap();
|
||||
|
||||
return state;
|
||||
}*/
|
||||
|
||||
public boolean hasStateChanged(ReadableMap newState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void update(ReadableMap state, ReadableMap filter) {
|
||||
if (this.filter == null) {
|
||||
this.filter = filter;
|
||||
}
|
||||
// process state updates from the queue using a background task
|
||||
synchronized (this) {
|
||||
queue.add(state);
|
||||
}
|
||||
persistState();
|
||||
}
|
||||
|
||||
private void persistState() {
|
||||
persistState(false);
|
||||
}
|
||||
|
||||
private void persistState(final boolean flush) {
|
||||
if (flush && persistTask != null) {
|
||||
persistTask.cancel(true);
|
||||
persistTask = null;
|
||||
}
|
||||
|
||||
if (persistTask == null) {
|
||||
persistTask = (new AsyncTask<Object, Void, Boolean>() {
|
||||
protected Boolean doInBackground(Object... param) {
|
||||
// get the first item in the queue
|
||||
ReadableMap queuedState = null;
|
||||
if (queue.size() > 0) {
|
||||
synchronized (StatePersistorModule.this) {
|
||||
queuedState = queue.remove(flush ? queue.size() - 1 : 0);
|
||||
if (flush) {
|
||||
// we only want the final state in this scenario
|
||||
queue.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (queuedState != null) {
|
||||
ReadableMap state = queuedState; //(ReadableMap) filterState(queuedState);
|
||||
// convert to JSON object
|
||||
|
||||
try {
|
||||
JSONObject json = readableMapToJSON(state);
|
||||
|
||||
// save the state file
|
||||
// TODO: explore this option at a later date
|
||||
throw new UnsupportedOperationException();
|
||||
} catch (JSONException ex) {
|
||||
// normally shouldn't happen, but if it does, reinsert into the queue
|
||||
if (queuedState != null) {
|
||||
synchronized (StatePersistorModule.this) {
|
||||
queue.add(0, queuedState);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onPostExecute(Boolean result) {
|
||||
if (queue.size() > 0) {
|
||||
persistState();
|
||||
}
|
||||
|
||||
persistTask = null;
|
||||
}
|
||||
});
|
||||
persistTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void flush() {
|
||||
persistState(true);
|
||||
}
|
||||
|
||||
private static JSONObject readableMapToJSON(ReadableMap readableMap) throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
||||
while (iterator.hasNextKey()) {
|
||||
String key = iterator.nextKey();
|
||||
switch (readableMap.getType(key)) {
|
||||
case Map:
|
||||
json.put(key, readableMapToJSON(readableMap.getMap(key)));
|
||||
break;
|
||||
case Array:
|
||||
json.put(key, readableArrayToJSON(readableMap.getArray(key)));
|
||||
break;
|
||||
case Boolean:
|
||||
json.put(key, readableMap.getBoolean(key));
|
||||
break;
|
||||
case Null:
|
||||
json.put(key, JSONObject.NULL);
|
||||
break;
|
||||
case Number:
|
||||
json.put(key, readableMap.getDouble(key));
|
||||
break;
|
||||
case String:
|
||||
json.put(key, readableMap.getString(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private static JSONArray readableArrayToJSON(ReadableArray readableArray) throws JSONException {
|
||||
JSONArray array = new JSONArray();
|
||||
for (int i = 0; i < readableArray.size(); i++) {
|
||||
switch (readableArray.getType(i)) {
|
||||
case Null:
|
||||
break;
|
||||
case Boolean:
|
||||
array.put(readableArray.getBoolean(i));
|
||||
break;
|
||||
case Number:
|
||||
array.put(readableArray.getDouble(i));
|
||||
break;
|
||||
case String:
|
||||
array.put(readableArray.getString(i));
|
||||
break;
|
||||
case Map:
|
||||
array.put(readableMapToJSON(readableArray.getMap(i)));
|
||||
break;
|
||||
case Array:
|
||||
array.put(readableArrayToJSON(readableArray.getArray(i)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
|
@ -1,473 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.Manifest;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Closeable;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import io.lbry.browser.DownloadManager;
|
||||
import io.lbry.browser.MainActivity;
|
||||
import io.lbry.browser.LbrynetService;
|
||||
import io.lbry.browser.R;
|
||||
import io.lbry.browser.Utils;
|
||||
|
||||
public class UtilityModule extends ReactContextBaseJavaModule {
|
||||
private static final Map<String, Integer> activeNotifications = new HashMap<String, Integer>();
|
||||
|
||||
private static final String FILE_PROVIDER = "io.lbry.browser.fileprovider";
|
||||
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "io.lbry.browser.SUBSCRIPTIONS_NOTIFICATION_CHANNEL";
|
||||
|
||||
public static final String ACTION_NOTIFICATION_PLAY = "io.lbry.browser.ACTION_NOTIFICATION_PLAY";
|
||||
|
||||
public static final String ACTION_NOTIFICATION_LATER = "io.lbry.browser.ACTION_NOTIFICATION_LATER";
|
||||
|
||||
public static final String RECEIVE_SUBSCRIPTION_NOTIFICATIONS = "receiveSubscriptionNotifications";
|
||||
|
||||
public static final String RECEIVE_REWARD_NOTIFICATIONS = "receiveRewardNotifications";
|
||||
|
||||
public static final String RECEIVE_INTERESTS_NOTIFICATIONS = "receiveInterestsNotifications";
|
||||
|
||||
public static final String RECEIVE_CREATOR_NOTIFICATIONS = "receiveCreatorNotifications";
|
||||
|
||||
// the last language set to be loaded
|
||||
private static final String LANGUAGE_SETTING_KEY = "language";
|
||||
|
||||
private String language;
|
||||
|
||||
private Context context;
|
||||
|
||||
private KeyStore keyStore;
|
||||
|
||||
public UtilityModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
try {
|
||||
this.keyStore = Utils.initKeyStore(context);
|
||||
} catch (Exception ex) {
|
||||
// continue without keystore
|
||||
}
|
||||
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
language = sp.getString(LANGUAGE_SETTING_KEY, "en");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getConstants() {
|
||||
final Map<String, Object> constants = MapBuilder.newHashMap();
|
||||
constants.put("language", language);
|
||||
return constants;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "UtilityModule";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void keepAwakeOn() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void keepAwakeOff() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hideNavigationBar() {
|
||||
final Activity activity = MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void showNavigationBar() {
|
||||
final Activity activity = MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getDeviceId(boolean requestPermission, final Promise promise) {
|
||||
if (isEmulator()) {
|
||||
promise.reject("Rewards cannot be claimed from an emulator nor virtual device.");
|
||||
return;
|
||||
}
|
||||
|
||||
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
String id = null;
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
id = telephonyManager.getImei(); // GSM
|
||||
if (id == null) {
|
||||
id = telephonyManager.getMeid(); // CDMA
|
||||
}
|
||||
} else {
|
||||
id = telephonyManager.getDeviceId();
|
||||
}
|
||||
} catch (SecurityException ex) {
|
||||
// Maybe the permission was not granted? Try to acquire permission
|
||||
/*if (requestPermission) {
|
||||
requestPhoneStatePermission();
|
||||
}*/
|
||||
} catch (Exception ex) {
|
||||
// id could not be obtained. Display a warning that rewards cannot be claimed.
|
||||
promise.reject(ex.getMessage());
|
||||
}
|
||||
|
||||
if (id == null || id.trim().length() == 0) {
|
||||
promise.reject("Rewards cannot be claimed because your device could not be identified.");
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(id);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void canReceiveSms(final Promise promise) {
|
||||
promise.resolve(MainActivity.hasPermission(Manifest.permission.RECEIVE_SMS, MainActivity.getActivity()));
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestReceiveSmsPermission() {
|
||||
MainActivity activity = (MainActivity) MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
// Request for the RECEIVE_SMS permission
|
||||
MainActivity.checkReceiveSmsPermission(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void canReadWriteStorage(final Promise promise) {
|
||||
promise.resolve(MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, MainActivity.getActivity()));
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void requestStoragePermission() {
|
||||
MainActivity activity = (MainActivity) MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
MainActivity.checkStoragePermission(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void shareLogFile(Callback errorCallback) {
|
||||
String logFileName = "lbrynet.log";
|
||||
File logFile = new File(String.format("%s/%s", Utils.getAppInternalStorageDir(context), "lbrynet"), logFileName);
|
||||
if (!logFile.exists()) {
|
||||
errorCallback.invoke("The lbrynet.log file could not be found.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Uri fileUri = FileProvider.getUriForFile(context, FILE_PROVIDER, logFile);
|
||||
if (fileUri != null) {
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
|
||||
Intent sendLogIntent = Intent.createChooser(shareIntent, "Send LBRY log");
|
||||
sendLogIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(sendLogIntent);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
errorCallback.invoke("The lbrynet.log file cannot be shared due to permission restrictions.");
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void shareUrl(String url) {
|
||||
Intent shareIntent = new Intent();
|
||||
shareIntent.setAction(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, url);
|
||||
|
||||
Intent shareUrlIntent = Intent.createChooser(shareIntent, "Share LBRY content");
|
||||
shareUrlIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(shareUrlIntent);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void showNotificationForContent(final String uri, String title, String publisher, final String thumbnail, boolean isPlayable) {
|
||||
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, "LBRY Subscriptions", NotificationManager.IMPORTANCE_DEFAULT);
|
||||
channel.setDescription("LBRY subscription notifications");
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
if (activeNotifications.containsKey(uri)) {
|
||||
// the notification for the specified uri is already present, don't try to create another one
|
||||
return;
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
Random random = new Random();
|
||||
do {
|
||||
id = random.nextInt();
|
||||
} while (id < 100);
|
||||
final int notificationId = id;
|
||||
|
||||
String uriWithParam = String.format("%s?download=true", uri);
|
||||
Intent playIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uriWithParam));
|
||||
playIntent.putExtra(MainActivity.SOURCE_NOTIFICATION_ID_KEY, notificationId);
|
||||
playIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
PendingIntent playPendingIntent = PendingIntent.getActivity(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
boolean hasThumbnail = false;
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
builder.setAutoCancel(true)
|
||||
.setColor(ContextCompat.getColor(context, R.color.lbryGreen))
|
||||
.setContentIntent(DownloadManager.getLaunchPendingIntent(uri, context))
|
||||
.setContentTitle(publisher)
|
||||
.setContentText(title)
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.addAction(android.R.drawable.ic_media_play, (isPlayable ? "Play" : "Open"), playPendingIntent);
|
||||
|
||||
activeNotifications.put(uri, notificationId);
|
||||
if (thumbnail != null) {
|
||||
// attempt to load the thumbnail Bitmap before displaying the notification
|
||||
final Uri thumbnailUri = Uri.parse(thumbnail);
|
||||
if (thumbnailUri != null) {
|
||||
hasThumbnail = true;
|
||||
(new AsyncTask<Void, Void, Bitmap>() {
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
try {
|
||||
return Picasso.get().load(thumbnailUri).get();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bitmap result) {
|
||||
if (result != null) {
|
||||
builder.setLargeIcon(result)
|
||||
.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(result).bigLargeIcon(null));
|
||||
}
|
||||
notificationManager.notify(notificationId, builder.build());
|
||||
}
|
||||
}).execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasThumbnail) {
|
||||
notificationManager.notify(notificationId, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isEmulator() {
|
||||
String buildModel = Build.MODEL.toLowerCase();
|
||||
return (// Check FINGERPRINT
|
||||
Build.FINGERPRINT.startsWith("generic") ||
|
||||
Build.FINGERPRINT.startsWith("unknown") ||
|
||||
Build.FINGERPRINT.contains("test-keys") ||
|
||||
|
||||
// Check MODEL
|
||||
buildModel.contains("google_sdk") ||
|
||||
buildModel.contains("emulator") ||
|
||||
buildModel.contains("android sdk built for x86") ||
|
||||
|
||||
// Check MANUFACTURER
|
||||
Build.MANUFACTURER.contains("Genymotion") ||
|
||||
"unknown".equals(Build.MANUFACTURER) ||
|
||||
|
||||
// Check HARDWARE
|
||||
Build.HARDWARE.contains("goldfish") ||
|
||||
Build.HARDWARE.contains("vbox86") ||
|
||||
|
||||
// Check PRODUCT
|
||||
"google_sdk".equals(Build.PRODUCT) ||
|
||||
"sdk_google_phone_x86".equals(Build.PRODUCT) ||
|
||||
"sdk".equals(Build.PRODUCT) ||
|
||||
"sdk_x86".equals(Build.PRODUCT) ||
|
||||
"vbox86p".equals(Build.PRODUCT) ||
|
||||
|
||||
// Check BRAND and DEVICE
|
||||
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|
||||
);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setSecureValue(String key, String value) {
|
||||
if (keyStore != null) {
|
||||
Utils.setSecureValue(key, value, context, keyStore);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getSecureValue(String key, Promise promise) {
|
||||
if (keyStore == null) {
|
||||
promise.reject("no keyStore found");
|
||||
return;
|
||||
}
|
||||
|
||||
promise.resolve(Utils.getSecureValue(key, context, keyStore));
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void checkDownloads() {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(LbrynetService.ACTION_CHECK_DOWNLOADS);
|
||||
if (context != null) {
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void queueDownload(String outpoint) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(LbrynetService.ACTION_QUEUE_DOWNLOAD);
|
||||
intent.putExtra("outpoint", outpoint);
|
||||
|
||||
if (context != null) {
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deleteDownload(String uri) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(LbrynetService.ACTION_DELETE_DOWNLOAD);
|
||||
intent.putExtra("uri", uri);
|
||||
if (context != null) {
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void openDocumentPicker(String type) {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||
intent.setType(type);
|
||||
Activity activity = MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
activity.startActivityForResult(
|
||||
Intent.createChooser(intent, "Select a file"), MainActivity.DOCUMENT_PICKER_RESULT_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setNativeBooleanSetting(String key, boolean value) {
|
||||
if (context != null) {
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean(key, value);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void setNativeStringSetting(String key, String value) {
|
||||
if (context != null) {
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putString(key, value);
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getNativeStringSetting(String key, String defaultValue, Promise promise) {
|
||||
if (context != null) {
|
||||
SharedPreferences sp = context.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
promise.resolve(sp.getString(key, defaultValue));
|
||||
} else {
|
||||
promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getNotificationLaunchTarget(Promise promise) {
|
||||
Activity activity = MainActivity.getActivity();
|
||||
if (activity != null) {
|
||||
Intent intent = activity.getIntent();
|
||||
if (intent != null) {
|
||||
String target = intent.getStringExtra("target");
|
||||
if (target != null && target.trim().length() > 0) {
|
||||
promise.resolve(target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promise.resolve(null);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getDownloadDirectory(Promise promise) {
|
||||
// This obtains a public default download directory after the storage permission has been granted
|
||||
promise.resolve(Utils.getConfiguredDownloadDirectory(context));
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package io.lbry.browser.reactmodules;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
public class VersionInfoModule extends ReactContextBaseJavaModule {
|
||||
private Context context;
|
||||
|
||||
public VersionInfoModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.context = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "VersionInfo";
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getAppVersion(final Promise promise) {
|
||||
PackageManager packageManager = this.context.getPackageManager();
|
||||
String packageName = this.context.getPackageName();
|
||||
try {
|
||||
PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
|
||||
promise.resolve(packageInfo.versionName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// normally shouldn't happen
|
||||
promise.resolve("Unknown");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package io.lbry.browser.reactpackages;
|
||||
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import io.lbry.browser.reactmodules.BackgroundMediaModule;
|
||||
import io.lbry.browser.reactmodules.DaemonServiceControlModule;
|
||||
import io.lbry.browser.reactmodules.FirstRunModule;
|
||||
import io.lbry.browser.reactmodules.FirebaseModule;
|
||||
import io.lbry.browser.reactmodules.GalleryModule;
|
||||
import io.lbry.browser.reactmodules.RequestsModule;
|
||||
import io.lbry.browser.reactmodules.ScreenOrientationModule;
|
||||
import io.lbry.browser.reactmodules.StatePersistorModule;
|
||||
import io.lbry.browser.reactmodules.VersionInfoModule;
|
||||
import io.lbry.browser.reactmodules.UtilityModule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LbryReactPackage implements ReactPackage {
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> modules = new ArrayList<>();
|
||||
|
||||
modules.add(new BackgroundMediaModule(reactContext));
|
||||
modules.add(new DaemonServiceControlModule(reactContext));
|
||||
modules.add(new FirstRunModule(reactContext));
|
||||
modules.add(new FirebaseModule(reactContext));
|
||||
modules.add(new GalleryModule(reactContext));
|
||||
modules.add(new RequestsModule(reactContext));
|
||||
modules.add(new ScreenOrientationModule(reactContext));
|
||||
modules.add(new StatePersistorModule(reactContext));
|
||||
modules.add(new UtilityModule(reactContext));
|
||||
modules.add(new VersionInfoModule(reactContext));
|
||||
|
||||
return modules;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package io.lbry.browser.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import io.lbry.browser.DownloadManager;
|
||||
|
||||
public class NotificationDeletedReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int notificationId = intent.getExtras().getInt(DownloadManager.NOTIFICATION_ID_KEY);
|
||||
if (DownloadManager.DOWNLOAD_NOTIFICATION_GROUP_ID == notificationId) {
|
||||
DownloadManager.groupCreated = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.lbry.browser;
|
||||
package io.lbry.lbrysdk;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
|
@ -12,12 +12,6 @@ import androidx.core.app.NotificationCompat;
|
|||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
|
||||
import io.lbry.browser.receivers.NotificationDeletedReceiver;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -25,7 +19,7 @@ import java.util.Map;
|
|||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class DownloadManager {
|
||||
public final class DownloadManager {
|
||||
private Context context;
|
||||
|
||||
private List<String> activeDownloads = new ArrayList<String>();
|
||||
|
@ -56,9 +50,11 @@ public class DownloadManager {
|
|||
private static boolean channelCreated = false;
|
||||
|
||||
private static NotificationCompat.Builder groupBuilder = null;
|
||||
|
||||
|
||||
public static final String NOTIFICATION_ID_KEY = "io.lbry.browser.notificationId";
|
||||
|
||||
public static final String ACTION_NOTIFICATION_DELETED = "io.lbry.browser.ACTION_NOTIFICATION_DELETED";
|
||||
|
||||
public static final String ACTION_DOWNLOAD_EVENT = "io.lbry.browser.ACTION_DOWNLOAD_EVENT";
|
||||
|
||||
public static final String ACTION_START = "start";
|
||||
|
@ -100,7 +96,7 @@ public class DownloadManager {
|
|||
|
||||
private void createNotificationGroup() {
|
||||
if (!groupCreated) {
|
||||
Intent intent = new Intent(context, NotificationDeletedReceiver.class);
|
||||
Intent intent = new Intent(ACTION_NOTIFICATION_DELETED);
|
||||
intent.putExtra(NOTIFICATION_ID_KEY, DOWNLOAD_NOTIFICATION_GROUP_ID);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DOWNLOAD_NOTIFICATION_GROUP_ID, intent, 0);
|
||||
|
@ -368,10 +364,6 @@ public class DownloadManager {
|
|||
}
|
||||
|
||||
int notificationId = generateNotificationId();
|
||||
if (MainActivity.downloadNotificationIds != null &&
|
||||
!MainActivity.downloadNotificationIds.contains(notificationId)) {
|
||||
MainActivity.downloadNotificationIds.add(notificationId);
|
||||
}
|
||||
downloadIdNotificationIdMap.put(id, notificationId);
|
||||
return notificationId;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.lbry.browser;
|
||||
package io.lbry.lbrysdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
|
@ -54,7 +54,7 @@ import org.renpy.android.ResourceManager;
|
|||
* @author akinwale
|
||||
* @version 0.1
|
||||
*/
|
||||
public class LbrynetService extends PythonService {
|
||||
public final class LbrynetService extends PythonService {
|
||||
|
||||
public static final int SERVICE_NOTIFICATION_GROUP_ID = 5;
|
||||
|
||||
|
@ -76,6 +76,8 @@ public class LbrynetService extends PythonService {
|
|||
|
||||
private static final int SDK_POLL_INTERVAL = 1000; // 1 second
|
||||
|
||||
private PendingIntent pendingContextIntent;
|
||||
|
||||
private BroadcastReceiver stopServiceReceiver;
|
||||
|
||||
private BroadcastReceiver downloadReceiver;
|
||||
|
@ -132,16 +134,44 @@ public class LbrynetService extends PythonService {
|
|||
};
|
||||
registerReceiver(downloadReceiver, downloadFilter);
|
||||
}
|
||||
|
||||
public void setPendingContextIntent(PendingIntent pendingIntent) {
|
||||
this.pendingContextIntent = pendingIntent;
|
||||
|
||||
// update the notification with the context intent
|
||||
Notification notification = buildNotification();
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(1, notification);
|
||||
}
|
||||
|
||||
private Notification buildNotification() {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
|
||||
if (pendingContextIntent != null) {
|
||||
builder.setContentIntent(pendingContextIntent);
|
||||
}
|
||||
|
||||
Intent stopIntent = new Intent(ACTION_STOP_SERVICE);
|
||||
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(this, 0, stopIntent, 0);
|
||||
|
||||
String serviceDescription = "The LBRY service is running in the background.";
|
||||
Notification notification = builder.setColor(ContextCompat.getColor(this, R.color.lbryGreen))
|
||||
.setContentText(serviceDescription)
|
||||
.setGroup(GROUP_SERVICE)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.setOngoing(true)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
|
||||
.build();
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStartForeground(Bundle extras) {
|
||||
String serviceTitle = extras.getString("serviceTitle");
|
||||
String serviceDescription = "The LBRY service is running in the background.";
|
||||
|
||||
Context context = getApplicationContext();
|
||||
downloadManager = new DownloadManager(context);
|
||||
downloadManager = new DownloadManager(this);
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID, "LBRY Browser", NotificationManager.IMPORTANCE_LOW);
|
||||
|
@ -150,33 +180,17 @@ public class LbrynetService extends PythonService {
|
|||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
Intent contextIntent = new Intent(context, MainActivity.class);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
Intent stopIntent = new Intent(ACTION_STOP_SERVICE);
|
||||
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent, 0);
|
||||
|
||||
// Create the notification group
|
||||
NotificationCompat.Builder groupBuilder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
NotificationCompat.Builder groupBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
|
||||
groupBuilder.setContentTitle("LBRY Browser")
|
||||
.setColor(ContextCompat.getColor(context, R.color.lbryGreen))
|
||||
.setColor(ContextCompat.getColor(this, R.color.lbryGreen))
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setGroup(GROUP_SERVICE)
|
||||
.setGroupSummary(true);
|
||||
notificationManager.notify(SERVICE_NOTIFICATION_GROUP_ID, groupBuilder.build());
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
Notification notification = builder.setColor(ContextCompat.getColor(context, R.color.lbryGreen))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentTitle(serviceTitle)
|
||||
.setContentText(serviceDescription)
|
||||
.setGroup(GROUP_SERVICE)
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_lbry)
|
||||
.setOngoing(true)
|
||||
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
|
||||
.build();
|
||||
Notification notification = buildNotification();
|
||||
startForeground(1, notification);
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.lbry.browser;
|
||||
package io.lbry.lbrysdk;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
|
@ -12,12 +12,14 @@ import java.io.InputStream;
|
|||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ServiceHelper {
|
||||
public final class ServiceHelper {
|
||||
|
||||
private static final String TAG = "io.lbry.browser.ServiceHelper";
|
||||
|
||||
private static final String HEADERS_ASSET_KEY = "headersAssetInitialized";
|
||||
|
||||
public static final String SHARED_PREFERENCES_NAME = "LBRY";
|
||||
|
||||
public static Intent buildIntent(Context ctx, String pythonServiceArgument, Class serviceClass, String pythonName) {
|
||||
Intent intent = new Intent(ctx, serviceClass);
|
||||
String argument = ctx.getFilesDir().getAbsolutePath() + "/app";
|
||||
|
@ -34,7 +36,7 @@ public class ServiceHelper {
|
|||
|
||||
public static void start(final Context ctx, String pythonServiceArgument, Class serviceClass, String pythonName) {
|
||||
// always check initial headers status before starting the service
|
||||
final SharedPreferences sp = ctx.getSharedPreferences(MainActivity.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
final SharedPreferences sp = ctx.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||
boolean initHeaders = sp.getBoolean(HEADERS_ASSET_KEY, false);
|
||||
if (initHeaders) {
|
||||
// initial headers asset copy already done. simply start the service
|
|
@ -1,4 +1,4 @@
|
|||
package io.lbry.browser;
|
||||
package io.lbry.lbrysdk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
@ -7,7 +7,7 @@ import android.security.KeyPairGeneratorSpec;
|
|||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import io.lbry.browser.BuildConfig;
|
||||
import io.lbry.lbrysdk.BuildConfig;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
|
@ -18,8 +18,8 @@ from lbry.extras.daemon.loggly_handler import get_loggly_handler
|
|||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.DEBUG)
|
||||
|
||||
lbrynet_android_utils = autoclass('io.lbry.browser.Utils')
|
||||
service = autoclass('io.lbry.browser.LbrynetService').serviceInstance
|
||||
lbrynet_android_utils = autoclass('io.lbry.lbrysdk.Utils')
|
||||
service = autoclass('io.lbry.lbrysdk.LbrynetService').serviceInstance
|
||||
platform.platform = lambda: 'Android %s (API %s)' % (lbrynet_android_utils.getAndroidRelease(), lbrynet_android_utils.getAndroidSdk())
|
||||
build_info.BUILD = 'dev' if lbrynet_android_utils.isDebug() else 'release'
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
__version__ = "0.13.0"
|
||||
# going forward, this should match sdk version
|
||||
__version__ = "0.58.0"
|
||||
|
||||
class ServiceApp(App):
|
||||
def build(self):
|
||||
from jnius import autoclass
|
||||
|
||||
Intent = autoclass('android.content.Intent')
|
||||
LbrynetService = autoclass('io.lbry.browser.LbrynetService')
|
||||
LbrynetService = autoclass('io.lbry.lbrysdk.LbrynetService')
|
||||
|
||||
if __name__ == '__main__':
|
||||
ServiceApp().run()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
import sys
|
||||
import StringIO
|
||||
|
||||
from twisted.trial.runner import (
|
||||
TestLoader,
|
||||
TrialRunner
|
||||
)
|
||||
from twisted.trial.reporter import TreeReporter
|
||||
from twisted.plugin import getPlugins, IPlugin
|
||||
from jnius import autoclass
|
||||
import lbrynet.tests
|
||||
from os import listdir
|
||||
|
||||
str_stream = StringIO.StringIO()
|
||||
|
||||
serviceClass = autoclass('io.lbry.browser.LbrynetTestRunnerService')
|
||||
|
||||
def update_output_in_activity(str):
|
||||
service = serviceClass.serviceInstance
|
||||
if service is not None:
|
||||
service.broadcastTestRunnerOutput(str)
|
||||
|
||||
class AndroidTestReporter(TreeReporter):
|
||||
def addSuccess(self, test):
|
||||
super(TreeReporter, self).addSuccess(test)
|
||||
self.endLine('[OK]', self.SUCCESS)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def addError(self, *args):
|
||||
super(TreeReporter, self).addError(*args)
|
||||
self.endLine('[ERROR]', self.ERROR)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def addFailure(self, *args):
|
||||
super(TreeReporter, self).addFailure(*args)
|
||||
self.endLine('[FAIL]', self.FAILURE)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def addSkip(self, *args):
|
||||
super(TreeReporter, self).addSkip(*args)
|
||||
self.endLine('[SKIPPED]', self.SKIP)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def addExpectedFailure(self, *args):
|
||||
super(TreeReporter, self).addExpectedFailure(*args)
|
||||
self.endLine('[TODO]', self.TODO)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def addUnexpectedSuccess(self, *args):
|
||||
super(TreeReporter, self).addUnexpectedSuccess(*args)
|
||||
self.endLine('[SUCCESS!?!]', self.TODONE)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def startTest(self, test):
|
||||
super(AndroidTestReporter, self).startTest(test)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def endLine(self, message, color):
|
||||
super(AndroidTestReporter, self).endLine(message, color)
|
||||
update_output_in_activity(str_to_basic_html(self._stream.getvalue()))
|
||||
|
||||
def str_to_basic_html(value):
|
||||
return value.replace("\n", "<br>").replace(" ", ' ')
|
||||
|
||||
def run():
|
||||
loader = TestLoader();
|
||||
suite = loader.loadPackage(lbrynet.tests, True)
|
||||
runner = TrialRunner(AndroidTestReporter)
|
||||
runner.stream = str_stream
|
||||
passFail = not runner.run(suite).wasSuccessful()
|
||||
|
||||
print str_stream.getvalue()
|
||||
sys.exit(passFail)
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 640 B |
Before Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 494 B |
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 247 B |
Before Width: | Height: | Size: 879 B |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 332 B |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 437 B |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 523 B |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 3.8 KiB |