From deed7886c3434a746023a99e9312e50c63b26572 Mon Sep 17 00:00:00 2001 From: Caio Faustino Date: Tue, 17 Dec 2019 07:21:21 +0100 Subject: [PATCH 1/4] Check and request storage permission in MainActivity. --- .../abcore/BitcoinConfEditActivity.java | 13 -- .../com/greenaddress/abcore/MainActivity.java | 117 ++++++++++++------ app/src/main/res/values/strings.xml | 1 + 3 files changed, 81 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/com/greenaddress/abcore/BitcoinConfEditActivity.java b/app/src/main/java/com/greenaddress/abcore/BitcoinConfEditActivity.java index f5015d84..e7fa6601 100755 --- a/app/src/main/java/com/greenaddress/abcore/BitcoinConfEditActivity.java +++ b/app/src/main/java/com/greenaddress/abcore/BitcoinConfEditActivity.java @@ -1,14 +1,10 @@ package com.greenaddress.abcore; -import android.Manifest; -import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; import org.apache.commons.compress.utils.IOUtils; @@ -32,15 +28,6 @@ protected void onCreate(final Bundle savedInstanceState) { @Override protected void onPause() { super.onPause(); - if (ContextCompat.checkSelfPermission(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED && - !ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE)) - ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, - 0); - // save file OutputStream f = null; try { diff --git a/app/src/main/java/com/greenaddress/abcore/MainActivity.java b/app/src/main/java/com/greenaddress/abcore/MainActivity.java index e4d9377a..4f69a8aa 100755 --- a/app/src/main/java/com/greenaddress/abcore/MainActivity.java +++ b/app/src/main/java/com/greenaddress/abcore/MainActivity.java @@ -1,5 +1,6 @@ package com.greenaddress.abcore; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ClipboardManager; @@ -7,6 +8,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Color; import android.os.Build; @@ -25,8 +27,11 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import com.google.zxing.WriterException; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; @@ -36,13 +41,18 @@ public class MainActivity extends AppCompatActivity { private final static String TAG = MainActivity.class.getName(); + + private final static int SCALE = 4; + private final static int STORAGE_PERMISSION_REQUEST = 0; + private RPCResponseReceiver mRpcResponseReceiver; private TextView mTvStatus; private Switch mSwitchCore; private TextView mQrCodeText; private ImageView mImageViewQr; - private final static int SCALE = 4; + + private boolean mSwitchOn = false; private enum DaemonStatus { UNKNOWN, @@ -99,40 +109,6 @@ else if (mDaemonStatus == DaemonStatus.STARTING || mDaemonStatus == DaemonStatus } } - private void stopDaemonAndSetStatus(){ - mSwitchOn = false; - mDaemonStatus = DaemonStatus.STOPPING; - mTvStatus.setText(getString(R.string.status_header, mDaemonStatus.toString())); - mSwitchCore.setText(R.string.switchcoreon); - final Intent i = new Intent(MainActivity.this, RPCIntentService.class); - i.putExtra("stop", "yep"); - startService(i); - } - - private void setSwitch() { - mSwitchCore.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { - if (isChecked) { - mSwitchOn = true; - mDaemonStatus = DaemonStatus.STARTING; - mTvStatus.setText(getString(R.string.status_header, mDaemonStatus.toString())); - mSwitchCore.setText(R.string.switchcoreoff); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); - final SharedPreferences.Editor e = prefs.edit(); - e.putBoolean("magicallystarted", false); - e.apply(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(new Intent(MainActivity.this, ABCoreService.class)); - } else { - startService(new Intent(MainActivity.this, ABCoreService.class)); - } - } else { - stopDaemonAndSetStatus(); - } - } - }); - } - @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -230,9 +206,76 @@ public boolean onOptionsItemSelected(final MenuItem item) { } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == STORAGE_PERMISSION_REQUEST) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + startDaemonAndSetStatus(); + } else { + mSwitchCore.setChecked(false); + Toast.makeText(MainActivity.this, R.string.permission_required, Toast.LENGTH_LONG).show(); + } + } + } + + private void setSwitch() { + mSwitchCore.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { + if (isChecked) { + if (checkPermissions()) { + startDaemonAndSetStatus(); + } else { + mSwitchCore.setChecked(false); + } + } else { + stopDaemonAndSetStatus(); + } + } + }); + } + + private void startDaemonAndSetStatus() { + mSwitchOn = true; + mDaemonStatus = DaemonStatus.STARTING; + mTvStatus.setText(getString(R.string.status_header, mDaemonStatus.toString())); + mSwitchCore.setText(R.string.switchcoreoff); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this); + final SharedPreferences.Editor e = prefs.edit(); + e.putBoolean("magicallystarted", false); + e.apply(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + startForegroundService(new Intent(MainActivity.this, ABCoreService.class)); + } else { + startService(new Intent(MainActivity.this, ABCoreService.class)); + } + } + + private void stopDaemonAndSetStatus(){ + mSwitchOn = false; + mDaemonStatus = DaemonStatus.STOPPING; + mTvStatus.setText(getString(R.string.status_header, mDaemonStatus.toString())); + mSwitchCore.setText(R.string.switchcoreon); + final Intent i = new Intent(MainActivity.this, RPCIntentService.class); + i.putExtra("stop", "yep"); + startService(i); + } + + private boolean checkPermissions() { + final boolean isInternalStorage = + Utils.getDataDir(this).startsWith(getNoBackupFilesDir().getAbsolutePath()); + final boolean hasStoragePermission = + ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + + if (!isInternalStorage && !hasStoragePermission) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION_REQUEST); + return false; + } + return true; + } + public class RPCResponseReceiver extends BroadcastReceiver { - public static final String ACTION_RESP = - "com.greenaddress.intent.action.RPC_PROCESSED"; + public static final String ACTION_RESP = "com.greenaddress.intent.action.RPC_PROCESSED"; @Override public void onReceive(final Context context, final Intent intent) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f3a33592..25b5995b 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -23,4 +23,5 @@ Switch ON Switch OFF Processed %1d%% (block height %2d) + Datadir in external folder requires storage permission. From 709e3786f1b30fefa98e2e889eef1900f6a3856e Mon Sep 17 00:00:00 2001 From: Caio Faustino Date: Tue, 24 Dec 2019 00:44:31 +0100 Subject: [PATCH 2/4] Remove redundant path parameter from bitcoin process command. --- app/src/main/java/com/greenaddress/abcore/ABCoreService.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/com/greenaddress/abcore/ABCoreService.java b/app/src/main/java/com/greenaddress/abcore/ABCoreService.java index 34fe7598..1cb43428 100755 --- a/app/src/main/java/com/greenaddress/abcore/ABCoreService.java +++ b/app/src/main/java/com/greenaddress/abcore/ABCoreService.java @@ -133,17 +133,12 @@ public void onError(final String[] error) { torErrorGobbler.start(); torOutputGobbler.start(); - // allow to pass in a different datadir directory - - // HACK: if user sets a datadir in the bitcoin.conf file that should then be the one - // used final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final String useDistribution = prefs.getString("usedistribution", "core"); final String daemon = "liquid".equals(useDistribution) ? "liquidd" : "bitcoind"; final ProcessBuilder pb = new ProcessBuilder( String.format("%s/%s", path, daemon), "--server=1", - String.format("--datadir=%s", Utils.getDataDir(this)), String.format("--conf=%s", Utils.getBitcoinConf(this))); pb.directory(new File(path)); From 523c82479c5774ad0731102ce85cd47ac9dcabf6 Mon Sep 17 00:00:00 2001 From: Caio Faustino Date: Tue, 24 Dec 2019 00:40:32 +0100 Subject: [PATCH 3/4] Create datadir folder if doesn't exist. --- .../com/greenaddress/abcore/MainActivity.java | 39 ++++++++++++++++++- app/src/main/res/values/strings.xml | 5 +++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/greenaddress/abcore/MainActivity.java b/app/src/main/java/com/greenaddress/abcore/MainActivity.java index 4f69a8aa..5a7dd0e9 100755 --- a/app/src/main/java/com/greenaddress/abcore/MainActivity.java +++ b/app/src/main/java/com/greenaddress/abcore/MainActivity.java @@ -5,6 +5,7 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -28,6 +29,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; @@ -38,6 +40,8 @@ import com.google.zxing.qrcode.encoder.ByteMatrix; import com.google.zxing.qrcode.encoder.Encoder; +import java.io.File; + public class MainActivity extends AppCompatActivity { private final static String TAG = MainActivity.class.getName(); @@ -211,7 +215,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == STORAGE_PERMISSION_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - startDaemonAndSetStatus(); + checkDataDirExistsAndStart(); } else { mSwitchCore.setChecked(false); Toast.makeText(MainActivity.this, R.string.permission_required, Toast.LENGTH_LONG).show(); @@ -224,7 +228,7 @@ private void setSwitch() { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { if (isChecked) { if (checkPermissions()) { - startDaemonAndSetStatus(); + checkDataDirExistsAndStart(); } else { mSwitchCore.setChecked(false); } @@ -251,6 +255,37 @@ private void startDaemonAndSetStatus() { } } + private void checkDataDirExistsAndStart() { + final File dataDirFile = new File(Utils.getDataDir(this)); + if (dataDirFile.exists() && dataDirFile.isDirectory()) { + startDaemonAndSetStatus(); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.create_dir_dialog_title) + .setMessage(getString(R.string.create_dir_dialog_msg, dataDirFile.getAbsolutePath())) + .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (dataDirFile.mkdirs()) { + startDaemonAndSetStatus(); + } else { + Toast.makeText(MainActivity.this, R.string.create_dir_failed_msg, Toast.LENGTH_LONG).show(); + mSwitchCore.setChecked(false); + } + } + }) + .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + Toast.makeText(MainActivity.this, R.string.create_dir_failed_msg, Toast.LENGTH_LONG).show(); + mSwitchCore.setChecked(false); + } + }) + .create() + .show(); + } + } + private void stopDaemonAndSetStatus(){ mSwitchOn = false; mDaemonStatus = DaemonStatus.STOPPING; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25b5995b..49aa56cb 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,4 +24,9 @@ Switch OFF Processed %1d%% (block height %2d) Datadir in external folder requires storage permission. + Create Datadir + The specified data dir does not exist. Do you want to create it?\n%s + YES + NO + Datadir not created. Cannot start daemon. From f1a98a4531a5ceb0a68f6be4f56eeac18b478642 Mon Sep 17 00:00:00 2001 From: Caio Faustino Date: Tue, 24 Dec 2019 15:47:56 +0100 Subject: [PATCH 4/4] Fix toggle position after granting permission. --- app/src/main/java/com/greenaddress/abcore/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/greenaddress/abcore/MainActivity.java b/app/src/main/java/com/greenaddress/abcore/MainActivity.java index 5a7dd0e9..bfac060f 100755 --- a/app/src/main/java/com/greenaddress/abcore/MainActivity.java +++ b/app/src/main/java/com/greenaddress/abcore/MainActivity.java @@ -215,7 +215,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == STORAGE_PERMISSION_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - checkDataDirExistsAndStart(); + mSwitchCore.setChecked(true); } else { mSwitchCore.setChecked(false); Toast.makeText(MainActivity.this, R.string.permission_required, Toast.LENGTH_LONG).show();