diff --git a/app/src/main/java/science/apolline/utils/CheckUtility.kt b/app/src/main/java/science/apolline/utils/CheckUtility.kt index 51fbbf9..25e2c4a 100644 --- a/app/src/main/java/science/apolline/utils/CheckUtility.kt +++ b/app/src/main/java/science/apolline/utils/CheckUtility.kt @@ -101,6 +101,10 @@ object CheckUtility : AnkoLogger { return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED } + fun checkReandPhoneStatePermission(context: Context): Boolean { + return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED + } + fun canGetLocation(context: Context): Boolean { var gpsEnabled = false var networkEnabled = false diff --git a/app/src/main/java/science/apolline/utils/DataExport.kt b/app/src/main/java/science/apolline/utils/DataExport.kt index 648457c..0cf62fd 100644 --- a/app/src/main/java/science/apolline/utils/DataExport.kt +++ b/app/src/main/java/science/apolline/utils/DataExport.kt @@ -59,58 +59,87 @@ object DataExport : AnkoLogger { } fun exportToJson(context: Context, sensorDao: SensorDao) { + var isDone: Boolean doAsync { val dataList = sensorDao.dumpSensor() info("List size: " + dataList.size.toString()) - if (dataList.isNotEmpty()) { + isDone = if (dataList.isNotEmpty()) { val fw = FileWriter(createFileName("json")) val gson = GsonBuilder().setPrettyPrinting().create() val jsonFile = gson.toJson(dataList) fw.write(jsonFile) + true + } else { + false } + uiThread { - Toasty.success(context, "Data exported to JSON with success!", Toast.LENGTH_SHORT, true).show() + if (isDone) { + Toasty.success(context, "Data exported to JSON with success!", Toast.LENGTH_SHORT, true).show() + } else { + Toasty.error(context, "No data found, please collect some data before export", Toast.LENGTH_LONG, true).show() + } } } } fun exportToCsv(context: Context, sensorDao: SensorDao, multiple: Boolean) { + var isDone: Boolean doAsync { val dataList = sensorDao.dumpSensor() info("List size: " + dataList.size.toString()) - if (dataList.isNotEmpty()) { + isDone = if (dataList.isNotEmpty()) { createCsv(dataList, multiple) + true + } else { + false } + uiThread { - Toasty.success(context, "Data exported to CSV with success!", Toast.LENGTH_SHORT, true).show() + if (isDone) { + Toasty.success(context, "Data exported to CSV with success!", Toast.LENGTH_SHORT, true).show() + } else { + Toasty.error(context, "No data found, please collect some data before export", Toast.LENGTH_LONG, true).show() + } } } } fun exportShareCsv(context: Context, sensorDao: SensorDao, multiple: Boolean) { + var isDone: Boolean doAsync { val dataList = sensorDao.dumpSensor() info("List size: " + dataList.size.toString()) - if (dataList.isNotEmpty()) { + isDone = if (dataList.isNotEmpty()) { createCsv(dataList, multiple) + true + } else { + false } + uiThread { - val file = File(localFolder(), "data_${CheckUtility.newDate()}.csv") - val uri: Uri - uri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - Uri.fromFile(file) + if (isDone) { + val file = File(localFolder(), "data_${CheckUtility.newDate()}.csv") + val uri: Uri + uri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + Uri.fromFile(file) + } else { + FileProvider.getUriForFile(context, "science.apolline.fileprovider", file) + } + + val shareIntent = Intent() + shareIntent.action = Intent.ACTION_SEND + shareIntent.putExtra(Intent.EXTRA_STREAM, uri) + shareIntent.type = "text/plain" + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + Toasty.success(context, "Data exported with success!", Toast.LENGTH_SHORT, true).show() + context.startActivity(Intent.createChooser(shareIntent, context.resources.getText(R.string.send_to))) } else { - FileProvider.getUriForFile(context, "science.apolline.fileprovider", file) + Toasty.error(context, "No data found, please collect some data before export", Toast.LENGTH_LONG, true).show() } - val shareIntent = Intent() - shareIntent.action = Intent.ACTION_SEND - shareIntent.putExtra(Intent.EXTRA_STREAM, uri) - shareIntent.type = "text/plain" - shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - Toasty.success(context, "Data exported with success!", Toast.LENGTH_SHORT, true).show() - context.startActivity(Intent.createChooser(shareIntent, context.resources.getText(R.string.send_to))) + } } } diff --git a/app/src/main/java/science/apolline/view/activity/MainActivity.kt b/app/src/main/java/science/apolline/view/activity/MainActivity.kt index e981d3b..9a24baa 100644 --- a/app/src/main/java/science/apolline/view/activity/MainActivity.kt +++ b/app/src/main/java/science/apolline/view/activity/MainActivity.kt @@ -9,6 +9,7 @@ import android.content.Intent import android.content.SharedPreferences import android.net.wifi.WifiManager.WifiLock import android.os.Bundle +import android.os.CountDownTimer import android.os.PowerManager.WakeLock import android.preference.PreferenceManager import android.support.design.widget.NavigationView @@ -31,8 +32,7 @@ import com.github.salomonbrys.kodein.instance import com.github.salomonbrys.kodein.with import es.dmoral.toasty.Toasty import kotlinx.android.synthetic.main.activity_main.* -import org.jetbrains.anko.AnkoLogger -import org.jetbrains.anko.info +import org.jetbrains.anko.* import science.apolline.BuildConfig import science.apolline.R import science.apolline.root.RootActivity @@ -232,9 +232,9 @@ class MainActivity : RootActivity(), NavigationView.OnNavigationItemSelectedList request.listeners { onAccepted { permissions -> - info("granted" + permissions[0]) - info("granted" + permissions.size) Toasty.success(applicationContext, "READ_PHONE_STATE and ACCESS_FINE_LOCATION permissions granted.", Toast.LENGTH_SHORT, true).show() + stopService(Intent(applicationContext, IOIOService::class.java)) + startService(Intent(applicationContext, IOIOService::class.java)) } onDenied { permissions -> @@ -242,11 +242,29 @@ class MainActivity : RootActivity(), NavigationView.OnNavigationItemSelectedList } onPermanentlyDenied { permissions -> - finish() + Toasty.error(applicationContext, "READ_PHONE_STATE and ACCESS_FINE_LOCATION permissions permanently denied, please grant it manually, Apolline will close in 10 seconds", Toast.LENGTH_LONG, true).show() + object : CountDownTimer(10000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + finish() + } + }.start() } onShouldShowRationale { permissions, nonce -> - Toasty.info(applicationContext, "Apolline will not work, please grant READ_PHONE_STATE and ACCESS_FINE_LOCATION permissions.", Toast.LENGTH_LONG, true).show() + + alert("Apolline will not work, please grant READ_PHONE_STATE and ACCESS_FINE_LOCATION permissions.", "Request read phone state and location permissions") { + yesButton { + checkPermissions(mRequestPermissions) + } + noButton { + checkPermissions(mRequestPermissions) + } + }.show() + } } } diff --git a/app/src/main/java/science/apolline/view/activity/SplashScreen.kt b/app/src/main/java/science/apolline/view/activity/SplashScreen.kt index 3c2510c..e92a63f 100644 --- a/app/src/main/java/science/apolline/view/activity/SplashScreen.kt +++ b/app/src/main/java/science/apolline/view/activity/SplashScreen.kt @@ -10,11 +10,11 @@ import android.content.SharedPreferences import android.graphics.Color import android.graphics.PorterDuff import android.os.Bundle +import android.os.CountDownTimer import android.preference.PreferenceManager import android.support.v4.content.ContextCompat import science.apolline.R import kotlinx.android.synthetic.main.content_splash_screen.* -import org.jetbrains.anko.AnkoLogger import com.szugyi.circlemenu.view.CircleImageView import android.widget.Toast import android.view.View @@ -26,11 +26,11 @@ import es.dmoral.toasty.Toasty import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers -import org.jetbrains.anko.info import science.apolline.root.RootActivity import com.github.ivbaranov.rxbluetooth.predicates.BtPredicate import com.fondesa.kpermissions.extension.permissionsBuilder import com.fondesa.kpermissions.request.PermissionRequest +import org.jetbrains.anko.* class SplashScreen : RootActivity(), AnkoLogger { @@ -42,8 +42,9 @@ class SplashScreen : RootActivity(), AnkoLogger { private var mDetectedDevices = hashMapOf() private lateinit var mPrefs: SharedPreferences private var EXTRA_DEVICE_ADDRESS: String = "fffffff-ffff-ffff-ffff-ffffffffffff" + private var mIsLocationPermissionGranted = false - private val mRequestPermissions by lazy { + private val mRequestLocationPermission by lazy { permissionsBuilder(Manifest.permission.ACCESS_FINE_LOCATION) .build() } @@ -64,7 +65,11 @@ class SplashScreen : RootActivity(), AnkoLogger { pairCurrentDevice() } circle_layout.setOnCenterClickListener { - checkBlueToothState() + if(!mIsLocationPermissionGranted){ + checkFineLocationPermission(mRequestLocationPermission) + } else { + checkBlueToothState() + } } circle_layout.setOnRotationFinishedListener { val animation = RotateAnimation(0f, 360f, it.width.toFloat() / 2, it.height.toFloat() / 2) @@ -74,7 +79,7 @@ class SplashScreen : RootActivity(), AnkoLogger { } setCurrentItemText() - checkFineLocationPermission(mRequestPermissions) + checkFineLocationPermission(mRequestLocationPermission) initBoundedDevices() } @@ -318,23 +323,45 @@ class SplashScreen : RootActivity(), AnkoLogger { request.listeners { onAccepted { permissions -> + mIsLocationPermissionGranted = true Toasty.success(applicationContext, "ACCESS_FINE_LOCATION granted.", Toast.LENGTH_SHORT, true).show() } onDenied { permissions -> + mIsLocationPermissionGranted = false Toasty.warning(applicationContext, "ACCESS_FINE_LOCATION denied.", Toast.LENGTH_SHORT, true).show() } onPermanentlyDenied { permissions -> - Toasty.error(applicationContext, "Fatal error, ACCESS_FINE_LOCATION permission permanently denied.", Toast.LENGTH_SHORT, true).show() + mIsLocationPermissionGranted = false + Toasty.error(applicationContext, "ACCESS_FINE_LOCATION permission permanently denied, please grant it manually, Apolline will close in 10 seconds", Toast.LENGTH_LONG, true).show() + object : CountDownTimer(10000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + finish() + } + }.start() } onShouldShowRationale { permissions, nonce -> - Toasty.warning(applicationContext, "Apolline will not work properly, please grant ACCESS_FINE_LOCATION permission.", Toast.LENGTH_LONG, true).show() + mIsLocationPermissionGranted = false + alert("Apolline will not work, please grant ACCESS_FINE_LOCATION permission.", "Request location permission") { + yesButton { + checkFineLocationPermission(mRequestLocationPermission) + } + noButton { + request.detachAllListeners() + } + }.show() + } } } + private fun checkBlueToothState() { if (mBluetoothAdapter == null) { diff --git a/app/src/main/java/science/apolline/view/fragment/ChartFragment.kt b/app/src/main/java/science/apolline/view/fragment/ChartFragment.kt index 26448a6..ae9d3bb 100644 --- a/app/src/main/java/science/apolline/view/fragment/ChartFragment.kt +++ b/app/src/main/java/science/apolline/view/fragment/ChartFragment.kt @@ -35,8 +35,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.fragment_chart_content.* import kotlinx.android.synthetic.main.fragment_ioio.* -import org.jetbrains.anko.AnkoLogger -import org.jetbrains.anko.info +import org.jetbrains.anko.* import science.apolline.R import science.apolline.models.Device import science.apolline.models.IOIOData @@ -60,7 +59,7 @@ class ChartFragment : RootFragment(), OnChartValueSelectedListener, FragmentLife private lateinit var mDisposable: CompositeDisposable private lateinit var mViewModel: SensorViewModel private lateinit var mPrefs: SharedPreferences - private var mIsWriteToExternalStorage = false + private var mIsWriteToExternalStoragePermissionGranted = false private var MAX_VISIBLE_ENTRIES: Int = 100 @@ -92,7 +91,7 @@ class ChartFragment : RootFragment(), OnChartValueSelectedListener, FragmentLife floating_action_menu_json.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToJson(activity!!.application, mSensorDao) @@ -100,21 +99,21 @@ class ChartFragment : RootFragment(), OnChartValueSelectedListener, FragmentLife } floating_action_menu_csv_multi.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToCsv(activity!!.application, mSensorDao, true) } } floating_action_menu_csv.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToCsv(activity!!.application, mSensorDao, false) } } floating_action_menu_share.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportShareCsv(activity!!.application, mSensorDao, false) @@ -265,6 +264,7 @@ class ChartFragment : RootFragment(), OnChartValueSelectedListener, FragmentLife } .onErrorReturn { error("Error device list not found $it") + emptyList() } .subscribe { if (it.isNotEmpty()) { @@ -338,23 +338,29 @@ class ChartFragment : RootFragment(), OnChartValueSelectedListener, FragmentLife request.listeners { onAccepted { permissions -> - mIsWriteToExternalStorage = true + mIsWriteToExternalStoragePermissionGranted = true Toasty.success(activity!!.applicationContext, "WRITE_EXTERNAL_STORAGE permission granted.", Toast.LENGTH_SHORT, true).show() } onDenied { permissions -> - mIsWriteToExternalStorage = false + mIsWriteToExternalStoragePermissionGranted = false Toasty.error(activity!!.applicationContext, "WRITE_EXTERNAL_STORAGE permission denied.", Toast.LENGTH_SHORT, true).show() } onPermanentlyDenied { permissions -> - mIsWriteToExternalStorage = false - Toasty.error(activity!!.applicationContext, "Fatal error, WRITE_EXTERNAL_STORAGE permission permanently denied.", Toast.LENGTH_SHORT, true).show() + mIsWriteToExternalStoragePermissionGranted = false + Toasty.error(activity!!.applicationContext, "Fatal error, WRITE_EXTERNAL_STORAGE permission permanently denied, please grant it manually", Toast.LENGTH_LONG, true).show() } onShouldShowRationale { permissions, nonce -> - mIsWriteToExternalStorage = false - Toasty.warning(activity!!.applicationContext, "Apolline couldn't export any file, please grant WRITE_EXTERNAL_STORAGE permission.", Toast.LENGTH_LONG, true).show() + mIsWriteToExternalStoragePermissionGranted = false + + activity!!.alert("Apolline couldn't export any file, please grant WRITE_EXTERNAL_STORAGE permission.", "Request write permission") { + yesButton { + checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) + } + noButton {} + }.show() } } diff --git a/app/src/main/java/science/apolline/view/fragment/IOIOFragment.kt b/app/src/main/java/science/apolline/view/fragment/IOIOFragment.kt index 712f443..7fb8c76 100644 --- a/app/src/main/java/science/apolline/view/fragment/IOIOFragment.kt +++ b/app/src/main/java/science/apolline/view/fragment/IOIOFragment.kt @@ -22,8 +22,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.fragment_ioio.* import kotlinx.android.synthetic.main.fragment_ioio_content.* -import org.jetbrains.anko.AnkoLogger -import org.jetbrains.anko.info +import org.jetbrains.anko.* import science.apolline.R import science.apolline.models.Device import science.apolline.models.IOIOData @@ -43,7 +42,7 @@ class IOIOFragment : RootFragment(), FragmentLifecycle, AnkoLogger { private val mSensorDao by instance() private lateinit var mDisposable: CompositeDisposable private lateinit var mViewModel: SensorViewModel - private var mIsWriteToExternalStorage = false + private var mIsWriteToExternalStoragePermissionGranted = false private val mRequestWriteToExternalStoragePermission by lazy { permissionsBuilder(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -68,7 +67,7 @@ class IOIOFragment : RootFragment(), FragmentLifecycle, AnkoLogger { floating_action_menu_json.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToJson(activity!!.application, mSensorDao) @@ -76,21 +75,21 @@ class IOIOFragment : RootFragment(), FragmentLifecycle, AnkoLogger { } floating_action_menu_csv_multi.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToCsv(activity!!.application, mSensorDao, true) } } floating_action_menu_csv.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportToCsv(activity!!.application, mSensorDao, false) } } floating_action_menu_share.setOnClickListener { - if (!mIsWriteToExternalStorage) { + if (!mIsWriteToExternalStoragePermissionGranted) { checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) } else { exportShareCsv(activity!!.application, mSensorDao, false) @@ -138,6 +137,7 @@ class IOIOFragment : RootFragment(), FragmentLifecycle, AnkoLogger { } .onErrorReturn { error("Error device list not found $it") + emptyList() } .subscribe { if (it.isNotEmpty()) { @@ -218,23 +218,30 @@ class IOIOFragment : RootFragment(), FragmentLifecycle, AnkoLogger { request.listeners { onAccepted { permissions -> - mIsWriteToExternalStorage = true + mIsWriteToExternalStoragePermissionGranted = true Toasty.success(activity!!.applicationContext, "WRITE_EXTERNAL_STORAGE permission granted.", Toast.LENGTH_SHORT, true).show() } onDenied { permissions -> - mIsWriteToExternalStorage = false + mIsWriteToExternalStoragePermissionGranted = false Toasty.error(activity!!.applicationContext, "WRITE_EXTERNAL_STORAGE permission denied.", Toast.LENGTH_SHORT, true).show() } onPermanentlyDenied { permissions -> - mIsWriteToExternalStorage = false - Toasty.error(activity!!.applicationContext, "Fatal error, WRITE_EXTERNAL_STORAGE permission permanently denied.", Toast.LENGTH_SHORT, true).show() + mIsWriteToExternalStoragePermissionGranted = false + Toasty.error(activity!!.applicationContext, "Fatal error, WRITE_EXTERNAL_STORAGE permission permanently denied, please grant it manually", Toast.LENGTH_LONG, true).show() } onShouldShowRationale { permissions, nonce -> - mIsWriteToExternalStorage = false - Toasty.warning(activity!!.applicationContext, "Apolline couldn't export any file, please grant WRITE_EXTERNAL_STORAGE permission.", Toast.LENGTH_LONG, true).show() + mIsWriteToExternalStoragePermissionGranted = false + + activity!!.alert("Apolline couldn't export any file, please grant WRITE_EXTERNAL_STORAGE permission.", "Request write permission") { + yesButton { + checkWriteToExternalStoragePermission(mRequestWriteToExternalStoragePermission) + } + noButton {} + }.show() + } } diff --git a/app/src/main/java/science/apolline/view/fragment/ViewPagerFragment.kt b/app/src/main/java/science/apolline/view/fragment/ViewPagerFragment.kt index c4a1a25..d9bf49b 100644 --- a/app/src/main/java/science/apolline/view/fragment/ViewPagerFragment.kt +++ b/app/src/main/java/science/apolline/view/fragment/ViewPagerFragment.kt @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothAdapter import android.content.BroadcastReceiver import android.content.IntentFilter import com.github.mikephil.charting.jobs.MoveViewJob +import science.apolline.utils.CheckUtility /** @@ -138,7 +139,9 @@ class ViewPagerFragment : RootFragment(), AnkoLogger { override fun onAttach(context: Context?) { super.onAttach(context) -// activity!!.startService(Intent(activity, IOIOService::class.java)) + if(CheckUtility.checkReandPhoneStatePermission(activity!!.applicationContext)){ + activity!!.startService(Intent(activity, IOIOService::class.java)) + } } @@ -157,7 +160,9 @@ class ViewPagerFragment : RootFragment(), AnkoLogger { BluetoothAdapter.STATE_TURNING_OFF -> info("Turning Bluetooth off...") BluetoothAdapter.STATE_ON -> { info("Bluetooth on") -// activity!!.startService(Intent(activity, IOIOService::class.java)) + if(CheckUtility.checkReandPhoneStatePermission(activity!!.applicationContext)){ + activity!!.startService(Intent(activity, IOIOService::class.java)) + } } BluetoothAdapter.STATE_TURNING_ON -> info("Turning Bluetooth on...") }