tMerge pull request #4864 from SomberNight/android_build_2018nov - electrum - Electrum Bitcoin wallet (HTM) git clone https://git.parazyd.org/electrum (DIR) Log (DIR) Files (DIR) Refs (DIR) Submodules --- (DIR) commit 12c6a4043b4e5054eedf819657a28e30377a6d47 (DIR) parent a53dded50fad595dce34be609c59fddad61d66be (HTM) Author: ghost43 <somber.night@protonmail.com> Date: Mon, 26 Nov 2018 22:02:52 +0100 Merge pull request #4864 from SomberNight/android_build_2018nov android: build apk using new python3 p4a toolchain Diffstat: M electrum/gui/kivy/Readme.md | 81 +++++++++++++++++++------------ M electrum/gui/kivy/data/java-classe… | 65 +++++++++++++++++++++++++------ M electrum/gui/kivy/tools/buildozer.… | 14 +++++++------- M electrum/util.py | 34 ++----------------------------- 4 files changed, 112 insertions(+), 82 deletions(-) --- (DIR) diff --git a/electrum/gui/kivy/Readme.md b/electrum/gui/kivy/Readme.md t@@ -1,6 +1,9 @@ # Kivy GUI -The Kivy GUI is used with Electrum on Android devices. To generate an APK file, follow these instructions. +The Kivy GUI is used with Electrum on Android devices. +To generate an APK file, follow these instructions. + +Recommended env: Ubuntu 18.04 ## 1. Preliminaries t@@ -21,10 +24,7 @@ sudo apt-get install python3-kivy ## 3. Install python-for-android (p4a) p4a is used to package Electrum, Python, SDL and a bootstrap Java app into an APK file. -We patched p4a to add some functionality we need for Electrum. Until those changes are -merged into p4a, you need to merge them locally (into the master branch): - -3.1 [kivy/python-for-android#1217](https://github.com/kivy/python-for-android/pull/1217) +We need some functionality not in p4a master, so for the time being we have our own fork. Something like this should work: t@@ -32,12 +32,9 @@ Something like this should work: cd /opt git clone https://github.com/kivy/python-for-android cd python-for-android -git remote add agilewalker https://github.com/agilewalker/python-for-android git remote add sombernight https://github.com/SomberNight/python-for-android git fetch --all -git checkout 93759f36ba45c7bbe0456a4b3e6788622924cbac -git cherry-pick a2fb5ecbc09c4847adbcfd03c6b1ca62b3d09b8d # openssl-fix -git cherry-pick a0ef2007bc60ed642fbd8b61937995dbed0ddd24 # disable backups +git checkout f74226666af69f9915afaee9ef9292db85a6c617 ``` ## 4. Install buildozer t@@ -51,31 +48,57 @@ sudo python3 setup.py install ``` 4.2 Install additional dependencies: + ```sh sudo apt-get install python-pip ``` -and the ones listed -[here](https://buildozer.readthedocs.io/en/latest/installation.html#targeting-android). -You will also need +(from [buildozer docs](https://buildozer.readthedocs.io/en/latest/installation.html#targeting-android)) +```sh +sudo pip install --upgrade cython==0.21 +sudo dpkg --add-architecture i386 +sudo apt-get update +sudo apt-get 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 +``` + +4.3 Download Android NDK ```sh -python3 -m pip install colorama appdirs sh jinja2 +cd /opt +wget https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip +unzip android-ndk-r14b-linux-x86_64.zip ``` +## 5. Some more dependencies -4.3 Download the [Crystax NDK](https://www.crystax.net/en/download) manually. -Extract into `/opt/crystax-ndk-10.3.2` +```sh +python3 -m pip install colorama appdirs sh jinja2 cython==0.29 +sudo apt-get install autotools-dev autoconf libtool pkg-config python3.7 +``` -## 5. Create the UI Atlas -In the `gui/kivy` directory of Electrum, run `make theming`. +## 6. Create the UI Atlas +In the `electrum/gui/kivy` directory of Electrum, run `make theming`. -## 6. Download Electrum dependencies +## 7. Download Electrum dependencies ```sh sudo contrib/make_packages ``` -## 7. Try building the APK and fail +## 8. Try building the APK and fail + +### 1. Try and fail: + +```sh +contrib/make_apk +``` + +Symlink android tools: + +```sh +ln -sf ~/.buildozer/android/platform/android-sdk-24/tools ~/.buildozer/android/platform/android-sdk-24/tools.save +``` + +### 2. Try and fail: ```sh contrib/make_apk t@@ -84,47 +107,43 @@ contrib/make_apk During this build attempt, buildozer downloaded some tools, e.g. those needed in the next step. -## 8. Update the Android SDK build tools +## 9. Update the Android SDK build tools ### Method 1: Using the GUI Start the Android SDK manager in GUI mode: - ~/.buildozer/android/platform/android-sdk-20/tools/android + ~/.buildozer/android/platform/android-sdk-24/tools/android Check the latest SDK available and install it ("Android SDK Tools" and "Android SDK Platform-tools"). Close the SDK manager. Repeat until there is no newer version. Reopen the SDK manager, and install the latest build tools - ("Android SDK Build-tools"), 27.0.3 at the time of writing. + ("Android SDK Build-tools"), 28.0.3 at the time of writing. + Install "Android 9">"SDK Platform". Install "Android Support Repository" from the SDK manager (under "Extras"). ### Method 2: Using the command line: Repeat the following command until there is nothing to install: - ~/.buildozer/android/platform/android-sdk-20/tools/android update sdk -u -t tools,platform-tools + ~/.buildozer/android/platform/android-sdk-24/tools/android update sdk -u -t tools,platform-tools Install Build Tools, android API 19 and Android Support Library: - ~/.buildozer/android/platform/android-sdk-20/tools/android update sdk -u -t build-tools-27.0.3,android-19,extra-android-m2repository + ~/.buildozer/android/platform/android-sdk-24/tools/android update sdk -u -t build-tools-28.0.3,android-28,extra-android-m2repository + (FIXME: build-tools is not getting installed?! use GUI for now.) -## 9. Build the APK +## 10. Build the APK ```sh contrib/make_apk ``` # FAQ -## Why do I get errors like `package me.dm7.barcodescanner.zxing does not exist` while compiling? -Update your Android build tools to version 27 like described above. - -## Why do I get errors like `(use -source 7 or higher to enable multi-catch statement)` while compiling? -Make sure that your p4a installation includes commit a3cc78a6d1a107cd3b6bd28db8b80f89e3ecddd2. -Also make sure you have recent SDK tools and platform-tools ## I changed something but I don't see any differences on the phone. What did I do wrong? You probably need to clear the cache: `rm -rf .buildozer/android/platform/build/{build,dists}` (DIR) diff --git a/electrum/gui/kivy/data/java-classes/org/electrum/qr/SimpleScannerActivity.java b/electrum/gui/kivy/data/java-classes/org/electrum/qr/SimpleScannerActivity.java t@@ -4,6 +4,9 @@ import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.content.Intent; +import android.support.v4.app.ActivityCompat; +import android.Manifest; +import android.content.pm.PackageManager; import java.util.Arrays; t@@ -13,28 +16,35 @@ import com.google.zxing.Result; import com.google.zxing.BarcodeFormat; public class SimpleScannerActivity extends Activity implements ZXingScannerView.ResultHandler { - private ZXingScannerView mScannerView; - final String TAG = "org.electrum.SimpleScannerActivity"; + private static final int MY_PERMISSIONS_CAMERA = 1002; - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view - mScannerView.setFormats(Arrays.asList(BarcodeFormat.QR_CODE)); - setContentView(mScannerView); // Set the scanner view as the content view - } + private ZXingScannerView mScannerView = null; + final String TAG = "org.electrum.SimpleScannerActivity"; @Override public void onResume() { super.onResume(); - mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results. - mScannerView.startCamera(); // Start camera on resume + if (this.hasPermission()) { + this.startCamera(); + } else { + this.requestPermission(); + } } @Override public void onPause() { super.onPause(); - mScannerView.stopCamera(); // Stop camera on pause + if (null != mScannerView) { + mScannerView.stopCamera(); // Stop camera on pause + } + } + + private void startCamera() { + mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view + mScannerView.setFormats(Arrays.asList(BarcodeFormat.QR_CODE)); + setContentView(mScannerView); // Set the scanner view as the content view + mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results. + mScannerView.startCamera(); // Start camera on resume } @Override t@@ -45,4 +55,35 @@ public class SimpleScannerActivity extends Activity implements ZXingScannerView. setResult(Activity.RESULT_OK, resultIntent); this.finish(); } + + private boolean hasPermission() { + return (ActivityCompat.checkSelfPermission(this, + Manifest.permission.CAMERA) + == PackageManager.PERMISSION_GRANTED); + } + + private void requestPermission() { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.CAMERA}, + MY_PERMISSIONS_CAMERA); + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case MY_PERMISSIONS_CAMERA: { + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // permission was granted, yay! + this.startCamera(); + } else { + // permission denied + this.finish(); + } + return; + } + } + } + } (DIR) diff --git a/electrum/gui/kivy/tools/buildozer.spec b/electrum/gui/kivy/tools/buildozer.spec t@@ -31,7 +31,7 @@ version.filename = %(source.dir)s/electrum/version.py #version = 1.9.8 # (list) Application requirements -requirements = python3crystax==3.6, android, openssl, plyer, kivy==master, libsecp256k1 +requirements = python3, android, openssl, plyer, kivy==master, libffi, libsecp256k1 # (str) Presplash of the application #presplash.filename = %(source.dir)s/gui/kivy/theming/splash.png t@@ -52,25 +52,25 @@ fullscreen = False # # (list) Permissions -android.permissions = INTERNET, WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, CAMERA +android.permissions = INTERNET, CAMERA # (int) Android API to use -#android.api = 14 +android.api = 28 # (int) Minimum API required (8 = Android 2.2 devices) -#android.minapi = 8 +android.minapi = 21 # (int) Android SDK version to use -#android.sdk = 21 +android.sdk = 24 # (str) Android NDK version to use -#android.ndk = 9 +android.ndk = 14b # (bool) Use --private data storage (True) or --dir public storage (False) android.private_storage = True # (str) Android NDK directory (if empty, it will be automatically downloaded.) -android.ndk_path = /opt/crystax-ndk-10.3.2 +android.ndk_path = /opt/android-ndk-r14b # (str) Android SDK directory (if empty, it will be automatically downloaded.) #android.sdk_path = (DIR) diff --git a/electrum/util.py b/electrum/util.py t@@ -354,41 +354,11 @@ def profiler(func): return lambda *args, **kw_args: do_profile(args, kw_args) -def android_ext_dir(): - import jnius - env = jnius.autoclass('android.os.Environment') - return env.getExternalStorageDirectory().getPath() - def android_data_dir(): import jnius PythonActivity = jnius.autoclass('org.kivy.android.PythonActivity') return PythonActivity.mActivity.getFilesDir().getPath() + '/data' -def android_headers_dir(): - d = android_ext_dir() + '/org.electrum.electrum' - if not os.path.exists(d): - try: - os.mkdir(d) - except FileExistsError: - pass # in case of race - return d - -def android_check_data_dir(): - """ if needed, move old directory to sandbox """ - ext_dir = android_ext_dir() - data_dir = android_data_dir() - old_electrum_dir = ext_dir + '/electrum' - if not os.path.exists(data_dir) and os.path.exists(old_electrum_dir): - import shutil - new_headers_path = android_headers_dir() + '/blockchain_headers' - old_headers_path = old_electrum_dir + '/blockchain_headers' - if not os.path.exists(new_headers_path) and os.path.exists(old_headers_path): - print_error("Moving headers file to", new_headers_path) - shutil.move(old_headers_path, new_headers_path) - print_error("Moving data to", data_dir) - shutil.move(old_electrum_dir, data_dir) - return data_dir - def ensure_sparse_file(filename): # On modern Linux, no need to do anything. t@@ -401,7 +371,7 @@ def ensure_sparse_file(filename): def get_headers_dir(config): - return android_headers_dir() if 'ANDROID_DATA' in os.environ else config.path + return config.path def assert_datadir_available(config_path): t@@ -484,7 +454,7 @@ def bh2u(x: bytes) -> str: def user_dir(): if 'ANDROID_DATA' in os.environ: - return android_check_data_dir() + return android_data_dir() elif os.name == 'posix': return os.path.join(os.environ["HOME"], ".electrum") elif "APPDATA" in os.environ: