Integrate build process with React Native (#18)
* created react base and updated build to use android sdk 23 * add nodejs install step to travis build script * added simple build script to create the react native bundle before the buildozer APK build
This commit is contained in:
parent
4f720ece58
commit
316634f5b7
15 changed files with 4984 additions and 31 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
.buildozer
|
.buildozer
|
||||||
|
app/node_modules/
|
||||||
bin
|
bin
|
||||||
buildozer.spec
|
buildozer.spec
|
||||||
build.log
|
build.log
|
||||||
|
|
31
.travis.yml
31
.travis.yml
|
@ -8,28 +8,31 @@ install:
|
||||||
- export PATH=/usr/bin:$PATH
|
- export PATH=/usr/bin:$PATH
|
||||||
- sudo dpkg --add-architecture i386
|
- sudo dpkg --add-architecture i386
|
||||||
- sudo apt-get -qq update
|
- sudo apt-get -qq update
|
||||||
- sudo apt-get -qq install 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 python2.7 python2.7-dev openjdk-8-jdk unzip zlib1g-dev zlib1g:i386 m4 libc6-dev-i386
|
- sudo apt-get -qq install 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 python2.7 python2.7-dev openjdk-8-jdk unzip zlib1g-dev zlib1g:i386 m4 libc6-dev-i386 npm nodejs
|
||||||
- sudo pip install --upgrade cython==0.25.2 pip setuptools
|
- sudo pip install --upgrade cython==0.25.2 pip setuptools
|
||||||
- git clone https://github.com/kivy/buildozer.git
|
- git clone https://github.com/akinwale/buildozer.git
|
||||||
|
- cd app
|
||||||
|
- npm install --silent --save react@16.0.0 react-native
|
||||||
|
- cd ..
|
||||||
- cd buildozer
|
- cd buildozer
|
||||||
- sudo python setup.py install
|
- sudo python setup.py install
|
||||||
- cd ..
|
- cd ..
|
||||||
- mv buildozer.spec.travis buildozer.spec
|
- mv buildozer.spec.travis buildozer.spec
|
||||||
- mkdir -p cd ~/.buildozer/android/platform/
|
- mkdir -p cd ~/.buildozer/android/platform/
|
||||||
- wget 'https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip' -P ~/.buildozer/android/platform/
|
- wget 'https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip' -P ~/.buildozer/android/platform/
|
||||||
- wget 'https://dl.google.com/android/android-sdk_r21-linux.tgz' -P ~/.buildozer/android/platform/
|
- wget 'https://dl.google.com/android/android-sdk_r23-linux.tgz' -P ~/.buildozer/android/platform/
|
||||||
- wget 'https://dl.google.com/android/repository/android-21_r02.zip' -P ~/.buildozer/android/platform/
|
- wget 'https://dl.google.com/android/repository/android-23_r02.zip' -P ~/.buildozer/android/platform/
|
||||||
- wget 'https://dl.google.com/android/repository/build-tools_r26.0.1-linux.zip' -P ~/.buildozer/android/platform/
|
- wget 'https://dl.google.com/android/repository/build-tools_r26.0.1-linux.zip' -P ~/.buildozer/android/platform/
|
||||||
- unzip -qq ~/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip -d ~/.buildozer/android/platform/
|
- unzip -qq ~/.buildozer/android/platform/android-ndk-r13b-linux-x86_64.zip -d ~/.buildozer/android/platform/
|
||||||
- tar -xf ~/.buildozer/android/platform/android-sdk_r21-linux.tgz -C ~/.buildozer/android/platform/
|
- tar -xf ~/.buildozer/android/platform/android-sdk_r23-linux.tgz -C ~/.buildozer/android/platform/
|
||||||
- mv ~/.buildozer/android/platform/android-sdk-linux ~/.buildozer/android/platform/android-sdk-21
|
- mv ~/.buildozer/android/platform/android-sdk-linux ~/.buildozer/android/platform/android-sdk-23
|
||||||
- unzip -qq ~/.buildozer/android/platform/android-21_r02.zip -d ~/.buildozer/android/platform/android-sdk-21/platforms
|
- unzip -qq ~/.buildozer/android/platform/android-23_r02.zip -d ~/.buildozer/android/platform/android-sdk-23/platforms
|
||||||
- mv ~/.buildozer/android/platform/android-sdk-21/platforms/android-5.0.1 ~/.buildozer/android/platform/android-sdk-21/platforms/android-21
|
- mv ~/.buildozer/android/platform/android-sdk-23/platforms/android-6.0 ~/.buildozer/android/platform/android-sdk-23/platforms/android-23
|
||||||
- mkdir -p ~/.buildozer/android/platform/android-sdk-21/build-tools
|
- mkdir -p ~/.buildozer/android/platform/android-sdk-23/build-tools
|
||||||
- unzip -qq ~/.buildozer/android/platform/build-tools_r26.0.1-linux.zip -d ~/.buildozer/android/platform/android-sdk-21/build-tools
|
- unzip -qq ~/.buildozer/android/platform/build-tools_r26.0.1-linux.zip -d ~/.buildozer/android/platform/android-sdk-23/build-tools
|
||||||
- mv ~/.buildozer/android/platform/android-sdk-21/build-tools/android-8.0.0 ~/.buildozer/android/platform/android-sdk-21/build-tools/26.0.1
|
- mv ~/.buildozer/android/platform/android-sdk-23/build-tools/android-8.0.0 ~/.buildozer/android/platform/android-sdk-23/build-tools/26.0.1
|
||||||
- mkdir -p ~/.buildozer/android/platform/android-sdk-21/licenses
|
- mkdir -p ~/.buildozer/android/platform/android-sdk-23/licenses
|
||||||
- echo $'\nd56f5187479451eabf01fb78af6dfcb131a6481e' > ~/.buildozer/android/platform/android-sdk-21/licenses/android-sdk-license
|
- echo $'\nd56f5187479451eabf01fb78af6dfcb131a6481e' > ~/.buildozer/android/platform/android-sdk-23/licenses/android-sdk-license
|
||||||
script:
|
script:
|
||||||
- buildozer android debug | grep -v 'working:' --line-buffered
|
- buildozer android debug | grep -Fv -e 'working:' -e 'copy' --line-buffered
|
||||||
- cp bin/*.apk /dev/null
|
- cp bin/*.apk /dev/null
|
2
app/bundle.sh
Executable file
2
app/bundle.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
25
app/index.js
Normal file
25
app/index.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {AppRegistry, StyleSheet, Text, View} from 'react-native';
|
||||||
|
|
||||||
|
class InfoComponent extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>LBRY UI</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
AppRegistry.registerComponent('LBRYApp', () => InfoComponent);
|
4504
app/package-lock.json
generated
Normal file
4504
app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
12
app/package.json
Normal file
12
app/package.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "LBRYApp",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": "true",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node node_modules/react-native/local-cli/cli.js start"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^16.0.0",
|
||||||
|
"react-native": "^0.51.0"
|
||||||
|
}
|
||||||
|
}
|
5
build.sh
Executable file
5
build.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
cd app
|
||||||
|
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ../src/main/assets/index.android.bundle --assets-dest ../src/main/res/
|
||||||
|
cd ..
|
||||||
|
buildozer android debug
|
|
@ -16,7 +16,7 @@ source.dir = ./src/main/python
|
||||||
source.include_exts = py,png,jpg,kv,atlas
|
source.include_exts = py,png,jpg,kv,atlas
|
||||||
|
|
||||||
# (list) List of inclusions using pattern matching
|
# (list) List of inclusions using pattern matching
|
||||||
#source.include_patterns = assets/*,images/*.png
|
#source.include_patterns = assets/*, res/*
|
||||||
|
|
||||||
# (list) Source files to exclude (let empty to not exclude anything)
|
# (list) Source files to exclude (let empty to not exclude anything)
|
||||||
#source.exclude_exts = spec
|
#source.exclude_exts = spec
|
||||||
|
@ -89,13 +89,13 @@ fullscreen = 0
|
||||||
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
|
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
# (int) Android API to use
|
# (int) Android API to use
|
||||||
android.api = 21
|
android.api = 23
|
||||||
|
|
||||||
# (int) Minimum API required
|
# (int) Minimum API required
|
||||||
#android.minapi = 9
|
android.minapi = 16
|
||||||
|
|
||||||
# (int) Android SDK version to use
|
# (int) Android SDK version to use
|
||||||
android.sdk = 21
|
android.sdk = 23
|
||||||
|
|
||||||
# (str) Android NDK version to use
|
# (str) Android NDK version to use
|
||||||
android.ndk = 13b
|
android.ndk = 13b
|
||||||
|
@ -137,7 +137,10 @@ android.ndk = 13b
|
||||||
|
|
||||||
# (list) List of Java files to add to the android project (can be java or a
|
# (list) List of Java files to add to the android project (can be java or a
|
||||||
# directory containing the files)
|
# directory containing the files)
|
||||||
android.add_src = ./src/main/java
|
android.add_src = ./src/main
|
||||||
|
|
||||||
|
# (str) Path to the React files and node_modules for building
|
||||||
|
android.react_src = ./app
|
||||||
|
|
||||||
# (list) Android AAR archives to add (currently works only with sdl2_gradle
|
# (list) Android AAR archives to add (currently works only with sdl2_gradle
|
||||||
# bootstrap)
|
# bootstrap)
|
||||||
|
@ -145,7 +148,7 @@ android.add_src = ./src/main/java
|
||||||
|
|
||||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||||
# bootstrap)
|
# bootstrap)
|
||||||
android.gradle_dependencies = com.android.support:appcompat-v7:21.0.3
|
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+
|
||||||
|
|
||||||
# (str) python-for-android branch to use, defaults to master
|
# (str) python-for-android branch to use, defaults to master
|
||||||
#p4a.branch = stable
|
#p4a.branch = stable
|
||||||
|
|
|
@ -16,7 +16,7 @@ source.dir = ./src/main/python
|
||||||
source.include_exts = py,png,jpg,kv,atlas
|
source.include_exts = py,png,jpg,kv,atlas
|
||||||
|
|
||||||
# (list) List of inclusions using pattern matching
|
# (list) List of inclusions using pattern matching
|
||||||
#source.include_patterns = assets/*,images/*.png
|
#source.include_patterns = assets/*, res/*
|
||||||
|
|
||||||
# (list) Source files to exclude (let empty to not exclude anything)
|
# (list) Source files to exclude (let empty to not exclude anything)
|
||||||
#source.exclude_exts = spec
|
#source.exclude_exts = spec
|
||||||
|
@ -89,13 +89,13 @@ fullscreen = 0
|
||||||
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
|
android.permissions = INTERNET,READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
|
||||||
|
|
||||||
# (int) Android API to use
|
# (int) Android API to use
|
||||||
android.api = 21
|
android.api = 23
|
||||||
|
|
||||||
# (int) Minimum API required
|
# (int) Minimum API required
|
||||||
#android.minapi = 9
|
android.minapi = 16
|
||||||
|
|
||||||
# (int) Android SDK version to use
|
# (int) Android SDK version to use
|
||||||
android.sdk = 21
|
android.sdk = 23
|
||||||
|
|
||||||
# (str) Android NDK version to use
|
# (str) Android NDK version to use
|
||||||
android.ndk = 13b
|
android.ndk = 13b
|
||||||
|
@ -137,7 +137,10 @@ android.ndk = 13b
|
||||||
|
|
||||||
# (list) List of Java files to add to the android project (can be java or a
|
# (list) List of Java files to add to the android project (can be java or a
|
||||||
# directory containing the files)
|
# directory containing the files)
|
||||||
android.add_src = ./src/main/java
|
android.add_src = ./src/main
|
||||||
|
|
||||||
|
# (str) Path to the React files and node_modules for building
|
||||||
|
android.react_src = ./app
|
||||||
|
|
||||||
# (list) Android AAR archives to add (currently works only with sdl2_gradle
|
# (list) Android AAR archives to add (currently works only with sdl2_gradle
|
||||||
# bootstrap)
|
# bootstrap)
|
||||||
|
@ -145,7 +148,7 @@ android.add_src = ./src/main/java
|
||||||
|
|
||||||
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
# (list) Gradle dependencies to add (currently works only with sdl2_gradle
|
||||||
# bootstrap)
|
# bootstrap)
|
||||||
android.gradle_dependencies = com.android.support:appcompat-v7:21.0.3
|
android.gradle_dependencies = com.android.support:appcompat-v7:23.4.0, com.facebook.react:react-native:+
|
||||||
|
|
||||||
# (str) python-for-android branch to use, defaults to master
|
# (str) python-for-android branch to use, defaults to master
|
||||||
#p4a.branch = stable
|
#p4a.branch = stable
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.kivy.android;
|
||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -89,12 +90,17 @@ public class PythonService extends Service implements Runnable {
|
||||||
String serviceDescription = extras.getString("serviceDescription");
|
String serviceDescription = extras.getString("serviceDescription");
|
||||||
|
|
||||||
Context context = getApplicationContext();
|
Context context = getApplicationContext();
|
||||||
Notification notification = new Notification(context.getApplicationInfo().icon,
|
|
||||||
serviceTitle, System.currentTimeMillis());
|
|
||||||
Intent contextIntent = new Intent(context, PythonActivity.class);
|
Intent contextIntent = new Intent(context, PythonActivity.class);
|
||||||
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
|
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
notification.setLatestEventInfo(context, serviceTitle, serviceDescription, pIntent);
|
Notification notification = new Notification.Builder(context)
|
||||||
|
.setContentTitle(serviceTitle)
|
||||||
|
.setContentText(serviceDescription)
|
||||||
|
.setContentIntent(pIntent)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setSmallIcon(android.R.drawable.ic_dialog_info)
|
||||||
|
.setOngoing(true)
|
||||||
|
.build();
|
||||||
startForeground(1, notification);
|
startForeground(1, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,12 +61,20 @@
|
||||||
<meta-data android:name="{{ m.split('=', 1)[0] }}" android:value="{{ m.split('=', 1)[-1] }}"/>{% endfor %}
|
<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 %}"/>
|
<meta-data android:name="wakelock" android:value="{% if args.wakelock %}1{% else %}0{% endif %}"/>
|
||||||
|
|
||||||
<activity android:name="io.lbry.lbrynet.ServiceControlActivity"
|
<!--activity android:name="io.lbry.lbrynet.ServiceControlActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
||||||
|
android:screenOrientation="{{ args.orientation }}"
|
||||||
|
-->
|
||||||
|
|
||||||
|
<activity android:name="io.lbry.lbrynet.MainActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||||
|
android:configChanges="keyboardHidden|orientation{% if args.min_sdk_version >= 13 %}|screenSize{% endif %}"
|
||||||
android:screenOrientation="{{ args.orientation }}"
|
android:screenOrientation="{{ args.orientation }}"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
{% if args.launcher %}
|
{% if args.launcher %}
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.kivy.LAUNCH" />
|
<action android:name="org.kivy.LAUNCH" />
|
||||||
|
|
|
@ -11,6 +11,10 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
|
maven {
|
||||||
|
// All of React Native (JS, Android binaries) is installed from npm
|
||||||
|
url "$rootDir/react/node_modules/react-native/android"
|
||||||
|
}
|
||||||
flatDir {
|
flatDir {
|
||||||
dirs 'libs'
|
dirs 'libs'
|
||||||
}
|
}
|
||||||
|
@ -27,6 +31,10 @@ android {
|
||||||
targetSdkVersion {{ android_api }}
|
targetSdkVersion {{ android_api }}
|
||||||
versionCode {{ args.numeric_version }}
|
versionCode {{ args.numeric_version }}
|
||||||
versionName '{{ args.version }}'
|
versionName '{{ args.version }}'
|
||||||
|
|
||||||
|
ndk {
|
||||||
|
abiFilters "armeabi-v7a", "x86"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{% if args.sign -%}
|
{% if args.sign -%}
|
||||||
|
|
297
src/main/assets/index.android.bundle
Normal file
297
src/main/assets/index.android.bundle
Normal file
File diff suppressed because one or more lines are too long
1
src/main/assets/index.android.bundle.meta
Normal file
1
src/main/assets/index.android.bundle.meta
Normal file
|
@ -0,0 +1 @@
|
||||||
|
fzv‚όκ9©φ¥yFΓ“Ο}‘ά³θ
|
75
src/main/java/io/lbry/lbrynet/MainActivity.java
Normal file
75
src/main/java/io/lbry/lbrynet/MainActivity.java
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package io.lbry.lbrynet;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
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.shell.MainReactPackage;
|
||||||
|
|
||||||
|
public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
|
||||||
|
private ReactRootView mReactRootView;
|
||||||
|
private ReactInstanceManager mReactInstanceManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mReactRootView = new ReactRootView(this);
|
||||||
|
mReactInstanceManager = ReactInstanceManager.builder()
|
||||||
|
.setApplication(getApplication())
|
||||||
|
.setBundleAssetName("index.android.bundle")
|
||||||
|
.setJSMainModulePath("index")
|
||||||
|
.addPackage(new MainReactPackage())
|
||||||
|
/*.setUseDeveloperSupport(BuildConfig.DEBUG)*/
|
||||||
|
.setInitialLifecycleState(LifecycleState.RESUMED)
|
||||||
|
.build();
|
||||||
|
mReactRootView.startReactApplication(mReactInstanceManager, "LBRYApp", null);
|
||||||
|
|
||||||
|
setContentView(mReactRootView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeDefaultOnBackPressed() {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
if (mReactInstanceManager != null) {
|
||||||
|
mReactInstanceManager.onHostPause(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (mReactInstanceManager != null) {
|
||||||
|
mReactInstanceManager.onHostResume(this, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
|
||||||
|
if (mReactInstanceManager != null) {
|
||||||
|
mReactInstanceManager.onHostDestroy(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
if (mReactInstanceManager != null) {
|
||||||
|
mReactInstanceManager.onBackPressed();
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue