diff --git a/android_app/app/build.gradle b/android_app/app/build.gradle index be0b279d..99fbccd7 100644 --- a/android_app/app/build.gradle +++ b/android_app/app/build.gradle @@ -7,7 +7,7 @@ android { applicationId "com.health.openscale" testApplicationId "com.health.openscale.test" minSdkVersion 18 - targetSdkVersion 22 // don't set target sdk > 22 otherwise bluetooth le discovery need permission to ACCESS_COARSE_LOCATION + targetSdkVersion 27 versionCode 23 versionName "1.7" diff --git a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java index 2679cd5c..c929a3c0 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java @@ -18,10 +18,7 @@ package com.health.openscale.gui; import android.annotation.SuppressLint; import android.app.AlertDialog; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; import android.content.ActivityNotFoundException; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -61,6 +58,7 @@ import com.health.openscale.gui.fragments.GraphFragment; import com.health.openscale.gui.fragments.OverviewFragment; import com.health.openscale.gui.fragments.StatisticsFragment; import com.health.openscale.gui.fragments.TableFragment; +import com.health.openscale.gui.utils.PermissionHelper; import java.lang.reflect.Field; @@ -72,6 +70,8 @@ public class MainActivity extends AppCompatActivity { private static int bluetoothStatusIcon = R.drawable.ic_bluetooth_disabled; private static MenuItem bluetoothStatus; + private boolean permGrantedCoarseLocation; + private Fragment currentFragment; private DrawerLayout drawerLayout; private Toolbar toolbar; @@ -96,6 +96,7 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); currentFragment = null; + permGrantedCoarseLocation = false; // Set a Toolbar to replace the ActionBar. toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -382,42 +383,33 @@ public class MainActivity extends AppCompatActivity { } private void invokeSearchBluetoothDevice() { - final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); - BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); - - if (btAdapter == null || !btAdapter.isEnabled()) { - setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); - Toast.makeText(getApplicationContext(), "Bluetooth " + getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); - - Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - startActivityForResult(enableBtIntent, 1); - return; - } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String deviceName = prefs.getString("btDeviceName", "-"); - if (deviceName == "-") { - Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device_set), Toast.LENGTH_SHORT).show(); - return; - } - // Check if Bluetooth 4.x is available if (deviceName != "openScale_MCU") { - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); - Toast.makeText(getApplicationContext(), "Bluetooth 4.x " + getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show(); - return; - } + permGrantedCoarseLocation = PermissionHelper.requestBluetoothPermission(this, false); + } else { + permGrantedCoarseLocation = PermissionHelper.requestBluetoothPermission(this, true); } - Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_try_connection) + " " + deviceName, Toast.LENGTH_SHORT).show(); - setBluetoothStatusIcon(R.drawable.ic_bluetooth_searching); + if (permGrantedCoarseLocation) { + if (deviceName == "-") { + Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_no_device_set), Toast.LENGTH_SHORT).show(); + return; + } - OpenScale.getInstance(getApplicationContext()).stopSearchingForBluetooth(); - if (!OpenScale.getInstance(getApplicationContext()).startSearchingForBluetooth(deviceName, callbackBtHandler)) { - Toast.makeText(getApplicationContext(), deviceName + " " + getResources().getString(R.string.label_bt_device_no_support), Toast.LENGTH_SHORT).show(); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.info_bluetooth_try_connection) + " " + deviceName, Toast.LENGTH_SHORT).show(); + setBluetoothStatusIcon(R.drawable.ic_bluetooth_searching); + + OpenScale.getInstance(getApplicationContext()).stopSearchingForBluetooth(); + if (!OpenScale.getInstance(getApplicationContext()).startSearchingForBluetooth(deviceName, callbackBtHandler)) { + Toast.makeText(getApplicationContext(), deviceName + " " + getResources().getString(R.string.label_bt_device_no_support), Toast.LENGTH_SHORT).show(); + } + } else { + setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); } } @@ -472,6 +464,21 @@ public class MainActivity extends AppCompatActivity { bluetoothStatus.setIcon(getResources().getDrawable(bluetoothStatusIcon)); } + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedCoarseLocation = true; + } else { + permGrantedCoarseLocation = false; + } + return; + } + } + } + + @SuppressLint("RestrictedApi") public static void disableShiftMode(BottomNavigationView view) { BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); diff --git a/android_app/app/src/main/java/com/health/openscale/gui/activities/UserSettingsActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/activities/UserSettingsActivity.java index cf35fd8a..c32e4303 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/activities/UserSettingsActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/activities/UserSettingsActivity.java @@ -153,11 +153,9 @@ public class UserSettingsActivity extends AppCompatActivity { final Drawable wrapped = DrawableCompat.wrap(drawable.mutate()); - String menuTitle = item.getTitle().toString(); - - if (menuTitle == getResources().getString(R.string.save)) { + if (item.getItemId() == R.id.saveButton) { DrawableCompat.setTint(wrapped, Color.parseColor("#FFFFFF")); - } else if (menuTitle == getResources().getString(R.string.label_delete)) { + } else if (item.getItemId() == R.id.deleteButton) { DrawableCompat.setTint(wrapped, Color.parseColor("#FF4444")); } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/fragments/TableFragment.java b/android_app/app/src/main/java/com/health/openscale/gui/fragments/TableFragment.java index fa451450..434d9d5f 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/fragments/TableFragment.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/fragments/TableFragment.java @@ -19,6 +19,7 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Color; import android.graphics.Typeface; @@ -52,6 +53,7 @@ import com.health.openscale.core.OpenScale; import com.health.openscale.core.datatypes.ScaleMeasurement; import com.health.openscale.core.datatypes.ScaleUser; import com.health.openscale.gui.activities.DataEntryActivity; +import com.health.openscale.gui.utils.PermissionHelper; import com.health.openscale.gui.views.BMIMeasurementView; import com.health.openscale.gui.views.BMRMeasurementView; import com.health.openscale.gui.views.BoneMeasurementView; @@ -85,6 +87,9 @@ public class TableFragment extends Fragment implements FragmentUpdateListener { private SharedPreferences prefs; private LinearLayout subpageView; + private boolean permGrantedReadAccess; + private boolean permGrantedWriteAccess; + private ArrayList measurementsList; private int selectedSubpageNr; @@ -138,6 +143,9 @@ public class TableFragment extends Fragment implements FragmentUpdateListener { selectedSubpageNr = savedInstanceState.getInt(SELECTED_SUBPAGE_NR_KEY); } + permGrantedReadAccess = false; + permGrantedWriteAccess = false; + OpenScale.getInstance(getContext()).registerFragment(this); return tableView; @@ -290,6 +298,13 @@ public class TableFragment extends Fragment implements FragmentUpdateListener { @Override public void onClick(View v) { + permGrantedReadAccess = PermissionHelper.requestReadPermission(getActivity()); + + if (!permGrantedReadAccess) { + Toast.makeText(getContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + return; + } + int selectedUserId = OpenScale.getInstance(getContext()).getSelectedScaleUserId(); if (selectedUserId == -1) @@ -339,6 +354,14 @@ public class TableFragment extends Fragment implements FragmentUpdateListener { private class onClickListenerExport implements View.OnClickListener { @Override public void onClick(View v) { + + permGrantedWriteAccess = PermissionHelper.requestWritePermission(getActivity()); + + if (!permGrantedWriteAccess) { + Toast.makeText(getContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + return; + } + AlertDialog.Builder filenameDialog = new AlertDialog.Builder(getActivity()); filenameDialog.setTitle(getResources().getString(R.string.info_set_filename) + " " + Environment.getExternalStorageDirectory().getPath()); @@ -391,6 +414,28 @@ public class TableFragment extends Fragment implements FragmentUpdateListener { } } + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_READ_STORAGE: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedReadAccess = true; + } else { + permGrantedReadAccess = false; + } + return; + } + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedWriteAccess = true; + } else { + permGrantedWriteAccess = false; + } + return; + } + } + } + private class onClickListenerMoveSubpageLeft implements View.OnClickListener { @Override public void onClick(View v) { diff --git a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java index 82b4fda6..94e54b87 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java @@ -15,7 +15,7 @@ */ package com.health.openscale.gui.preferences; -import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; import android.preference.EditTextPreference; @@ -30,6 +30,7 @@ import android.widget.Toast; import com.health.openscale.R; import com.health.openscale.core.OpenScale; import com.health.openscale.core.datatypes.ScaleUser; +import com.health.openscale.gui.utils.PermissionHelper; import java.io.File; import java.io.FileInputStream; @@ -44,6 +45,9 @@ public class BackupPreferences extends PreferenceFragment { private static final String PREFERENCE_KEY_IMPORT_BACKUP = "importBackup"; private static final String PREFERENCE_KEY_EXPORT_BACKUP = "exportBackup"; + private boolean permGrantedReadAccess; + private boolean permGrantedWriteAccess; + private Preference importBackup; private Preference exportBackup; @@ -59,6 +63,9 @@ public class BackupPreferences extends PreferenceFragment { exportBackup = (Preference) findPreference(PREFERENCE_KEY_EXPORT_BACKUP); exportBackup.setOnPreferenceClickListener(new onClickListenerExportBackup()); + permGrantedReadAccess = false; + permGrantedWriteAccess = false; + initSummary(getPreferenceScreen()); } @@ -145,6 +152,13 @@ public class BackupPreferences extends PreferenceFragment { } private boolean importBackup(String databaseName, File exportDir) { + permGrantedReadAccess = PermissionHelper.requestReadPermission(getActivity()); + + if (!permGrantedReadAccess) { + Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + return false; + } + if (!isExternalStoragePresent()) return false; @@ -169,6 +183,13 @@ public class BackupPreferences extends PreferenceFragment { } private boolean exportBackup(String databaseName, File exportDir) { + permGrantedWriteAccess = PermissionHelper.requestWritePermission(getActivity()); + + if (!permGrantedWriteAccess) { + Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + return false; + } + if (!isExternalStoragePresent()) return false; @@ -208,4 +229,26 @@ public class BackupPreferences extends PreferenceFragment { private boolean isExternalStoragePresent() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_READ_STORAGE: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedReadAccess = true; + } else { + permGrantedReadAccess = false; + } + return; + } + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedWriteAccess = true; + } else { + permGrantedWriteAccess = false; + } + return; + } + } + } } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothPreferences.java b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothPreferences.java index ef2e1696..3528e8cb 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothPreferences.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothPreferences.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; @@ -37,6 +38,7 @@ import android.widget.Toast; import com.health.openscale.R; import com.health.openscale.core.bluetooth.BluetoothCommunication; +import com.health.openscale.gui.utils.PermissionHelper; import java.util.ArrayList; import java.util.HashMap; @@ -49,6 +51,8 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr private static final String PREFERENCE_KEY_BLUETOOTH_IGNOREOUTOFRANGE = "ignoreOutOfRange"; private static final String PREFERENCE_KEY_BLUETOOTH_SCANNER = "btScanner"; + private boolean permGrantedCoarseLocation; + private CheckBoxPreference smartAssignEnable; private CheckBoxPreference ignoreOutOfRangeEnable; private PreferenceScreen btScanner; @@ -132,6 +136,8 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + permGrantedCoarseLocation = false; + btAdapter = BluetoothAdapter.getDefaultAdapter(); addPreferencesFromResource(R.xml.bluetooth_preferences); @@ -220,7 +226,14 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr private class onClickListenerScannerSelect implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(Preference preference) { - startSearching(); + permGrantedCoarseLocation = PermissionHelper.requestBluetoothPermission(getActivity(), true); + + if (permGrantedCoarseLocation) { + startSearching(); + } else { + Toast.makeText(getActivity().getApplicationContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + } + return true; } } @@ -240,4 +253,18 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr return true; } } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + permGrantedCoarseLocation = true; + } else { + permGrantedCoarseLocation = false; + } + return; + } + } + } } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java b/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java new file mode 100644 index 00000000..8a6dad08 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java @@ -0,0 +1,98 @@ +/* Copyright (C) 2018 olie.xdev +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see +*/ +package com.health.openscale.gui.utils; + +import android.Manifest; +import android.app.Activity; +import android.app.AlertDialog; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.widget.Toast; + +import com.health.openscale.R; + +public class PermissionHelper { + public final static int PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1; + public final static int PERMISSIONS_REQUEST_ACCESS_READ_STORAGE = 2; + public final static int PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE = 3; + + public static boolean requestBluetoothPermission(final Activity activity, boolean BLE) { + final BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE); + BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); + + if (btAdapter == null || !btAdapter.isEnabled()) { + + Toast.makeText(activity.getApplicationContext(), "Bluetooth " + activity.getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); + + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + activity.startActivityForResult(enableBtIntent, 1); + return false; + } + + // Check if Bluetooth 4.x is available + if (BLE) { + if (!activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Toast.makeText(activity.getApplicationContext(), "Bluetooth 4.x " + activity.getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show(); + return false; + } + } + + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + + builder.setMessage(R.string.permission_bluetooth_info) + .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } else { + return true; + } + + return false; + } + + public static boolean requestReadPermission(final Activity activity) { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_ACCESS_READ_STORAGE); + } else { + return true; + } + + return false; + } + + public static boolean requestWritePermission(final Activity activity) { + if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE); + } else { + return true; + } + + return false; + } +} diff --git a/android_app/app/src/main/res/menu/action_menu.xml b/android_app/app/src/main/res/menu/action_menu.xml index 2ed09eaf..b7ade228 100644 --- a/android_app/app/src/main/res/menu/action_menu.xml +++ b/android_app/app/src/main/res/menu/action_menu.xml @@ -3,15 +3,16 @@ xmlns:tools="http://schemas.android.com/tools" tools:context="com.example.openscale.MainActivity" > + + - diff --git a/android_app/app/src/main/res/values/strings.xml b/android_app/app/src/main/res/values/strings.xml index ab138186..22cc48a0 100644 --- a/android_app/app/src/main/res/values/strings.xml +++ b/android_app/app/src/main/res/values/strings.xml @@ -216,4 +216,7 @@ Edit Save + Permission not granted + openScale needs permission to access to coarse location to search for Bluetooth devices +