diff --git a/README.md b/README.md index 08c27fb..6629765 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,26 @@ - # :cyclone: Android Beacon Scanner # -A simple android beacon scanner that can recognize iBeacons, AltBeacons, Eddystone beacons (UID and URL, with or without TLM) and RuuviTags [available on Google Play](https://play.google.com/store/apps/details?id=com.bridou_n.beaconscanner). +Forked from "A simple [android beacon scanner](https://github.com/Bridouille/android-beacon-scanner) that can recognize iBeacons, AltBeacons, Eddystone beacons (UID and URL, with or without TLM) and RuuviTags [available on Google Play](https://play.google.com/store/apps/details?id=com.bridou_n.beaconscanner)." - + +## :key: Modified features ## +The forked project has the following new features: + +* White list: white can help doing experiment without manualy blocking a lot of beacons; (this is helpful if there are a lot of beacons in the experiment enviroment); +* (TODO): dump the db to local csv files; Available for android 5.0+ and smartphones with Bluetooth LE. +* Original app visualization: + + + +* White list part: (new feature, under developing) + + + + + ## :key: Features ## This app will scan for beacons near you! :v: diff --git a/app/build.gradle b/app/build.gradle index ccc212f..7773c8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,21 +18,21 @@ android { multiDexEnabled true } - signingConfigs { - release { - keyAlias 'BeaconScannerKey' - keyPassword beacon_scanner_key_password - storeFile file(beacon_scanner_store_file) - storePassword beacon_scanner_store_password - } - } +// signingConfigs { +// release { +// keyAlias 'BeaconScannerKey' +//// keyPassword beacon_scanner_key_password +// storeFile file(beacon_scanner_store_file) +// storePassword beacon_scanner_store_password +// } +// } buildTypes { release { minifyEnabled true resValue "string", "app_name", "Beacon Scanner" proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - signingConfig signingConfigs.release +// signingConfig signingConfigs.release } debug { diff --git a/app/src/main/java/com/bridou_n/beaconscanner/Database/BeaconsDao.kt b/app/src/main/java/com/bridou_n/beaconscanner/Database/BeaconsDao.kt index d271e66..6724e10 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/Database/BeaconsDao.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/Database/BeaconsDao.kt @@ -19,6 +19,10 @@ interface BeaconsDao { @Query("SELECT * FROM $TABLE_NAME WHERE is_blocked = :blocked") fun getBeacons(blocked: Boolean = false) : Flowable> + + @Query("SELECT * FROM $TABLE_NAME WHERE is_white = :whited") + fun getWhiteBeacons(whited: Boolean = true) : Flowable> + @Query("SELECT * FROM $TABLE_NAME WHERE hashcode = :hashcode") fun getBeaconById(hashcode: Int) : Single @@ -28,6 +32,9 @@ interface BeaconsDao { @Insert(onConflict = REPLACE) fun insertBeacon(beacon: BeaconSaved) +// @Update("") +// fun update + @Delete fun deleteBeacon(beacon: BeaconSaved) diff --git a/app/src/main/java/com/bridou_n/beaconscanner/dagger/AppComponent.kt b/app/src/main/java/com/bridou_n/beaconscanner/dagger/AppComponent.kt index 8275811..3865e7d 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/dagger/AppComponent.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/dagger/AppComponent.kt @@ -28,6 +28,5 @@ interface AppComponent { fun inject(activity: BeaconListActivity) fun inject(activity: SettingsActivity) fun inject(activity: BlockedActivity) - fun inject(bs: ControlsBottomSheetDialog) } diff --git a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconListActivity.kt b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconListActivity.kt index ff0ebf0..6bac521 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconListActivity.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconListActivity.kt @@ -41,6 +41,7 @@ import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.bottom_sheet_controls.* import org.altbeacon.beacon.Beacon import org.altbeacon.beacon.BeaconConsumer import org.altbeacon.beacon.BeaconManager @@ -48,6 +49,7 @@ import org.altbeacon.beacon.Region import timber.log.Timber import java.util.* import javax.inject.Inject +import kotlin.collections.ArrayList class BeaconListActivity : AppCompatActivity(), BeaconConsumer { @@ -83,9 +85,11 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { private var loggingRequests = CompositeDisposable() private var isScanning = false + + private var isWhiting = false private val rvAdapter = BeaconsRecyclerViewAdapter { beacon -> - ControlsBottomSheetDialog.newInstance(beacon.hashcode).apply { + ControlsBottomSheetDialog.newInstance(beacon.hashcode,false,beacon.isWhite).apply { show(supportFragmentManager) } } @@ -109,6 +113,10 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { scan_fab.setOnClickListener { toggleScan() } + + white_fab.setOnClickListener({ + toogleWhite() + }) } private fun toggleScan() { @@ -148,8 +156,25 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { keepScreenOn(false) isScanning = false } + + + private fun toogleWhite(){ + val ori_name = getString(if (isScanning()) R.string.scanning_for_beacons else R.string.app_name) + if (!isWhiting()){ + tracker.logEvent("start_whiting_clicked",null) + isWhiting = true + toolbar.title = ori_name + " (whitelist)" + } + else{ + isWhiting = false + toolbar.title = ori_name + } + white_fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, if (isWhiting) R.color.colorPauseFab else R.color.colorSecondary)) + } fun isScanning() = isScanning + + fun isWhiting() = isWhiting private fun unbindBeaconManager() { if (beaconManager?.isBound(this) == true) { @@ -192,14 +217,29 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { beaconManager = component().providesBeaconManager() observeBluetoothState() - listQuery = db.beaconsDao().getBeacons(blocked = false) - .subscribeOn(Schedulers.io()) - .map { list -> - if (list.isEmpty()) { - listOf(BeaconRow.EmptyState) - } else { - list.map { BeaconRow.Beacon(it) } + +// val listQuery = db.beaconsDao() + // here not possible to change the white setting: + + listQuery = db.beaconsDao().getBeacons(blocked = false).subscribeOn(Schedulers.io()) + .map { list -> + if(!isWhiting){ + if (list.isEmpty()) { + listOf(BeaconRow.EmptyState) + } else { + list.map { when(it.isWhite){ + true -> BeaconRow.BeaconWhite(it) + else -> BeaconRow.Beacon(it) + } } + } + }else{ + if (list.filter{it.isWhite}.isEmpty()) { + listOf(BeaconRow.EmptyState) + } else { + list.filter{it.isWhite}.map { BeaconRow.BeaconWhite(it) } + } } + } .doOnSubscribe { rvAdapter.submitList(listOf(BeaconRow.Loading)) } .observeOn(AndroidSchedulers.mainThread()) @@ -283,8 +323,8 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { } catch (e: EmptyResultSetException) { null } - - BeaconSaved.createFromBeacon(it, isBlocked = beaconInDb?.isBlocked ?: false) + Timber.d(it.toString()) + BeaconSaved.createFromBeacon(it, isBlocked = beaconInDb?.isBlocked ?: false, isWhite= beaconInDb?.isWhite?:false) } .doOnNext { db.beaconsDao().insertBeacon(it) @@ -419,6 +459,11 @@ class BeaconListActivity : AppCompatActivity(), BeaconConsumer { tracker.log("action_settings") startActivity(Intent(this, SettingsActivity::class.java)) } + + R.id.action_save -> { + tracker.log("white_lists") + } + else -> return super.onOptionsItemSelected(item) } return true diff --git a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconsRVAdapter.kt b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconsRVAdapter.kt index 1dfad5e..b85b9b3 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconsRVAdapter.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/BeaconsRVAdapter.kt @@ -22,6 +22,7 @@ import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.JustifyContent import kotlinx.android.synthetic.main.item_beacon.view.* +import timber.log.Timber import java.util.* /** @@ -41,6 +42,15 @@ class BeaconsRecyclerViewAdapter( is BeaconRow.Beacon -> { when (newItem) { is BeaconRow.Beacon -> oldItem.beacon.hashcode == newItem.beacon.hashcode + is BeaconRow.BeaconWhite -> oldItem.beacon.hashcode == newItem.beacon.hashcode + else -> false + } + } + + is BeaconRow.BeaconWhite -> { + when (newItem) { + is BeaconRow.Beacon -> oldItem.beacon.hashcode == newItem.beacon.hashcode + is BeaconRow.BeaconWhite -> oldItem.beacon.hashcode == newItem.beacon.hashcode else -> false } } @@ -52,6 +62,14 @@ class BeaconsRecyclerViewAdapter( return when (oldItem) { is BeaconRow.Beacon -> { when (newItem) { + is BeaconRow.Beacon -> oldItem.beacon == newItem.beacon + is BeaconRow.BeaconWhite -> oldItem.beacon == newItem.beacon + else -> false + } + } + is BeaconRow.BeaconWhite -> { + when (newItem) { + is BeaconRow.BeaconWhite -> oldItem.beacon == newItem.beacon is BeaconRow.Beacon -> oldItem.beacon == newItem.beacon else -> false } @@ -67,14 +85,110 @@ class BeaconsRecyclerViewAdapter( private val beaconInfosAdapter = BeaconInfosRvAdapter() - @SuppressLint("StringFormatMatches") + @SuppressLint("StringFormatMatches", "ResourceAsColor") fun bindView(beacon: BeaconSaved) { val ctx = itemView.context itemView.beacon_actions.setOnClickListener { true.also { listener?.invoke(beacon) } } - + +// itemView + + itemView.beacon_type.text = ctx.getString(when (beacon.beaconType) { + TYPE_ALTBEACON -> R.string.altbeacon + TYPE_EDDYSTONE_UID -> R.string.eddystone_uid + TYPE_EDDYSTONE_URL -> R.string.eddystone_url + TYPE_RUUVITAG -> R.string.ruuvitag + else -> R.string.ibeacon + }) + itemView.distance.text = String.format(Locale.getDefault(), "%.2f", beacon.distance) + + itemView.address.text = beacon.beaconAddress + itemView.manufacturer.text = String.format(Locale.getDefault(), "0x%04X", beacon.manufacturer) + itemView.last_seen.text = DateUtils.getRelativeTimeSpanString( + beacon.lastSeen, Date().time, + DateUtils.SECOND_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE + ).toString() + + val beaconInfos = mutableListOf().apply { + // Adding iBeacon or AltBeacon data + beacon.ibeaconData?.let { + add(BeaconInfo(ctx.getString(R.string.uuid), it.uuid)) + add(BeaconInfo(ctx.getString(R.string.major), it.major)) + add(BeaconInfo(ctx.getString(R.string.minor), it.minor)) + } + + add(BeaconInfo(ctx.getString(R.string.RSSI_title), String.format(ctx.getString(R.string.x_dbm), beacon.rssi))) + add(BeaconInfo(ctx.getString(R.string.TX_title), String.format(ctx.getString(R.string.x_dbm), beacon.txPower))) + + // Adding Eddystone UID data + beacon.eddystoneUidData?.let { + add(BeaconInfo(ctx.getString(R.string.namespace_id_title), it.namespaceId)) + add(BeaconInfo(ctx.getString(R.string.instance_id_title), it.instanceId)) + } + + // Adding Eddystone URL data + beacon.eddystoneUrlData?.let { + add(BeaconInfo( + title = ctx.getString(R.string.url_title), + content = it.url ?: "UNKNOWN", + onItemClicked = { onUrlClicked(it.url) } + )) + } + + // Adding RuuviTag data + beacon.ruuviData?.let { + add(BeaconInfo(ctx.getString(R.string.air_pressure_title), String.format(ctx.getString(R.string.x_hpa), it.airPressure))) + add(BeaconInfo(ctx.getString(R.string.temperature_title), ctx.getString(R.string.x_degrees, "${it.temperatue}"))) + add(BeaconInfo(ctx.getString(R.string.humidity_title), String.format("%d %%", it.humidity))) + } + + // Adding TLM data + beacon.telemetryData?.let { + add(BeaconInfo(ctx.getString(R.string.battery_title), String.format(ctx.getString(R.string.x_mv), it.batteryMilliVolts))) + add(BeaconInfo(ctx.getString(R.string.temperature_title), ctx.getString(R.string.x_degrees, String.format("%.1f", it.temperature)))) // %.1f + + add(BeaconInfo(ctx.getString(R.string.uptime_title), String.format(ctx.getString(R.string.x_seconds), it.uptime.toCoolFormat()))) + add(BeaconInfo(ctx.getString(R.string.pdu_title), it.pduCount.toCoolFormat())) + } + } + + itemView.infos_rv.apply { + layoutManager = FlexboxLayoutManager(context).apply { + flexDirection = FlexDirection.ROW + justifyContent = JustifyContent.SPACE_BETWEEN + } + adapter = beaconInfosAdapter + isNestedScrollingEnabled = false + } + beaconInfosAdapter.submitList(beaconInfos) + } + + fun onUrlClicked(url: String?) { + url ?: return + + try { + val uri = Uri.parse(url) + itemView.context.startActivity(Intent(Intent.ACTION_VIEW, uri)) + } catch (e: Exception) { } + } + } + class BeaconWhiteViewHolder(itemView: View, val listener: OnControlsOpen?) : RecyclerView.ViewHolder(itemView) { + + private val beaconInfosAdapter = BeaconInfosRvAdapter() + + @SuppressLint("StringFormatMatches", "ResourceAsColor") + fun bindView(beacon: BeaconSaved) { + val ctx = itemView.context + + itemView.beacon_actions.setOnClickListener { + true.also { listener?.invoke(beacon) } + } + +// itemView + itemView.beacon_type.text = ctx.getString(when (beacon.beaconType) { TYPE_ALTBEACON -> R.string.altbeacon TYPE_EDDYSTONE_UID -> R.string.eddystone_uid @@ -155,7 +269,6 @@ class BeaconsRecyclerViewAdapter( } catch (e: Exception) { } } } - class EmptyStateViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) class LoadingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) @@ -164,6 +277,8 @@ class BeaconsRecyclerViewAdapter( return when (viewType) { R.layout.item_beacon -> BeaconViewHolder(view, clickListener) +// R.layout.item_beacon_white + R.layout.item_beacon_white -> BeaconWhiteViewHolder(view,clickListener) R.layout.item_beacon_empty -> EmptyStateViewHolder(view) R.layout.item_loading -> LoadingViewHolder(view) else -> LoadingViewHolder(view) // Should never happen @@ -175,15 +290,21 @@ class BeaconsRecyclerViewAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is BeaconViewHolder -> { + Timber.d("beacon view holder") val beacon = (getItem(position) as BeaconRow.Beacon).beacon holder.bindView(beacon) } + is BeaconWhiteViewHolder -> { + val beacon = (getItem(position) as BeaconRow.BeaconWhite).beacon + holder.bindView(beacon) + } } } } -sealed class BeaconRow(@LayoutRes val layoutRes: Int) { +sealed class BeaconRow(@LayoutRes val layoutRes: Int,val isWhite: Boolean = false) { data class Beacon(val beacon: BeaconSaved) : BeaconRow(R.layout.item_beacon) - object EmptyState : BeaconRow(R.layout.item_beacon_empty) - object Loading : BeaconRow(R.layout.item_loading) + data class BeaconWhite(val beacon: BeaconSaved) : BeaconRow(R.layout.item_beacon_white,true) + object EmptyState : BeaconRow(R.layout.item_beacon_empty,false) + object Loading : BeaconRow(R.layout.item_loading,false) } diff --git a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/ControlsBottomSheetDialog.kt b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/ControlsBottomSheetDialog.kt index 0f9ebd8..c61a4c0 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/ControlsBottomSheetDialog.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/features/beaconList/ControlsBottomSheetDialog.kt @@ -7,18 +7,21 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import com.bridou_n.beaconscanner.AppSingleton import com.bridou_n.beaconscanner.Database.AppDatabase import com.bridou_n.beaconscanner.R +import com.bridou_n.beaconscanner.models.BeaconSaved import com.bridou_n.beaconscanner.utils.dialogs.RoundedBottomSheetDialog import com.bridou_n.beaconscanner.utils.extensionFunctions.showSnackBar import io.reactivex.Completable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers +import timber.log.Timber import javax.inject.Inject @@ -31,12 +34,14 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { companion object { const val KEY_BEACON_ID = "key_beacon_id" const val KEY_BLOCKED = "key_blocked" + const val KEY_WHITE = "key_white" - fun newInstance(beaconId: Int, blocked: Boolean = false) : ControlsBottomSheetDialog { + fun newInstance(beaconId: Int, blocked: Boolean = false, white:Boolean = false) : ControlsBottomSheetDialog { return ControlsBottomSheetDialog().apply { arguments = Bundle().apply { putInt(KEY_BEACON_ID, beaconId) putBoolean(KEY_BLOCKED, blocked) + putBoolean(KEY_WHITE,white) } } } @@ -45,6 +50,8 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { private var beaconId: Int = 0 private var isBlockedLst: Boolean = false + private var isWhiteLst: Boolean = false + private val queries = CompositeDisposable() @Inject lateinit var db: AppDatabase @@ -53,6 +60,7 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { if (bundle != null) { beaconId = bundle.getInt(KEY_BEACON_ID) isBlockedLst = bundle.getBoolean(KEY_BLOCKED) + isWhiteLst = bundle.getBoolean(KEY_WHITE) } } @@ -60,6 +68,7 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { super.onSaveInstanceState(outState) outState.putInt(KEY_BEACON_ID, beaconId) outState.putBoolean(KEY_BLOCKED, isBlockedLst) + outState.putBoolean(KEY_BLOCKED,isWhiteLst) } override fun onCreate(savedInstanceState: Bundle?) { @@ -82,11 +91,20 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { val blockedContainer = contentView.findViewById(R.id.block) val blockLabel = contentView.findViewById(R.id.block_label) + val whiteContainer = contentView.findViewById(R.id.white) + val whiteLabel = contentView.findViewById(R.id.white_label) + val whiteIcon = contentView.findViewById(R.id.fileimage) + if (isBlockedLst) { removeContainer.visibility = View.GONE blockLabel.setText(R.string.unblock) } + if(isWhiteLst){ + whiteLabel.setText(R.string.exclude) + whiteIcon.setImageResource(R.drawable.ic_round_delete_24px) + } + removeContainer.setOnClickListener { queries.add(Completable.fromCallable { db.beaconsDao().deleteBeaconById(beaconId) @@ -96,6 +114,7 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { } clipboardContainer.setOnClickListener { + Timber.d("click clipboard") context?.let { ctx -> queries.add( db.beaconsDao().getBeaconById(beaconId) @@ -105,7 +124,6 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { val clipboard = ctx.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText("Beacon infos", it.toJson()) clipboard.primaryClip = clip - dismissAllowingStateLoss() (activity as? BeaconListActivity)?.showGenericError(ctx.getString(R.string.the_informations_has_been_copied)) ?: (activity as? AppCompatActivity)?.showSnackBar(ctx.getString(R.string.the_informations_has_been_copied)) @@ -129,6 +147,20 @@ class ControlsBottomSheetDialog : RoundedBottomSheetDialog() { dismissAllowingStateLoss() }) } + + whiteContainer.setOnClickListener{ + Timber.d("click white") + queries.add(db.beaconsDao().getBeaconById(beaconId).flatMapCompletable { + Completable.fromCallable{ + db.beaconsDao().insertBeacon(it.copy(isWhite=!isWhiteLst)) + } + }.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + dismissAllowingStateLoss() + }) + + } } override fun onDestroy() { diff --git a/app/src/main/java/com/bridou_n/beaconscanner/features/settings/SettingsActivity.kt b/app/src/main/java/com/bridou_n/beaconscanner/features/settings/SettingsActivity.kt index 9fdf5fb..b22478d 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/features/settings/SettingsActivity.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/features/settings/SettingsActivity.kt @@ -14,7 +14,7 @@ import com.afollestad.materialdialogs.actions.getActionButton import com.afollestad.materialdialogs.input.getInputField import com.afollestad.materialdialogs.input.input import com.afollestad.materialdialogs.list.listItemsSingleChoice -import com.bridou_n.beaconscanner.BuildConfig +//import com.bridou_n.beaconscanner.BuildConfig import com.bridou_n.beaconscanner.R import com.bridou_n.beaconscanner.features.blockedList.BlockedActivity import com.bridou_n.beaconscanner.utils.PreferencesHelper @@ -52,7 +52,7 @@ class SettingsActivity : AppCompatActivity() { handleLoggingState(prefs.isLoggingEnabled) toolbar_title.text = getString(R.string.settings) - app_version.text = String.format("v%s", BuildConfig.VERSION_NAME) +// app_version.text = String.format("v%s", BuildConfig.VERSION_NAME) content.apply { viewTreeObserver.addOnScrollChangedListener { diff --git a/app/src/main/java/com/bridou_n/beaconscanner/models/BeaconSaved.kt b/app/src/main/java/com/bridou_n/beaconscanner/models/BeaconSaved.kt index 2dc2a2a..818720f 100644 --- a/app/src/main/java/com/bridou_n/beaconscanner/models/BeaconSaved.kt +++ b/app/src/main/java/com/bridou_n/beaconscanner/models/BeaconSaved.kt @@ -81,7 +81,11 @@ data class BeaconSaved( val ruuviData: RuuviData? = null, @ColumnInfo(name = "is_blocked") - val isBlocked: Boolean = false + val isBlocked: Boolean = false, + + @ColumnInfo(name="is_white") + val isWhite: Boolean = false + ) { companion object { const val TYPE_EDDYSTONE_UID = "eddystone_uid" @@ -90,7 +94,7 @@ data class BeaconSaved( const val TYPE_IBEACON = "ibeacon" const val TYPE_RUUVITAG = "ruuvitag" - fun createFromBeacon(beacon: Beacon, isBlocked: Boolean = false) : BeaconSaved { + fun createFromBeacon(beacon: Beacon, isBlocked: Boolean = false, isWhite: Boolean = false) : BeaconSaved { // Common fields to every beacons var hashcode = beacon.hashCode() val lastSeen = Date().time @@ -162,8 +166,8 @@ data class BeaconSaved( eddystoneUidData = eddystoneUidData, telemetryData = telemetryData, ruuviData = ruuviData, - - isBlocked = isBlocked + isBlocked = isBlocked, + isWhite = isWhite ) } diff --git a/app/src/main/res/drawable/file_icon.xml b/app/src/main/res/drawable/file_icon.xml new file mode 100644 index 0000000..724e9d2 --- /dev/null +++ b/app/src/main/res/drawable/file_icon.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 3d06cca..a0b5dc3 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -64,6 +64,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/progress_2" + android:layout_marginTop="-5dp" android:clipToPadding="false" android:overScrollMode="never" android:paddingTop="8dp" @@ -71,7 +72,7 @@ android:scrollbars="vertical" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> - + + + diff --git a/app/src/main/res/layout/bottom_sheet_controls.xml b/app/src/main/res/layout/bottom_sheet_controls.xml index 280faaf..d60a7d6 100644 --- a/app/src/main/res/layout/bottom_sheet_controls.xml +++ b/app/src/main/res/layout/bottom_sheet_controls.xml @@ -18,6 +18,7 @@ android:padding="16dp"> - + - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_beacon.xml b/app/src/main/res/layout/item_beacon.xml index b069037..a586df4 100644 --- a/app/src/main/res/layout/item_beacon.xml +++ b/app/src/main/res/layout/item_beacon.xml @@ -154,15 +154,15 @@ + android:overScrollMode="never" + android:paddingBottom="16dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/header_divider" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_beacon_white.xml b/app/src/main/res/layout/item_beacon_white.xml new file mode 100644 index 0000000..2b4fa69 --- /dev/null +++ b/app/src/main/res/layout/item_beacon_white.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml index 09d87aa..d61d377 100644 --- a/app/src/main/res/menu/main_menu.xml +++ b/app/src/main/res/menu/main_menu.xml @@ -4,6 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".features.beaconList.BeaconListActivity"> + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 0f68519..fbdd937 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -21,6 +21,7 @@ #FFF + #FF0 @color/colorBackground diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25c64d1..4ba5cc1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,12 +78,16 @@ Remove from the list Copy infos to clipboard Add to the blocked list + Add to the white list The informations have been copied to the clipboard! Blacklist See the blocked beacons No beacon has been blocked yet! Unblock + Remove from white list Open source This app is open source! Want to see how it works under the hood or customize it to fits your needs? While scanning, the device won\'t be put in sleep mode + Show beacons in white list + Save Scanned BLE to csv diff --git a/build.gradle b/build.gradle index b650cf1..d1d8c2c 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ buildscript { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.google.gms:google-services:4.3.3' + classpath 'com.google.gms:google-services:4.3.5' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/screenshots/white_list_vis_1.png b/screenshots/white_list_vis_1.png new file mode 100644 index 0000000..050e587 Binary files /dev/null and b/screenshots/white_list_vis_1.png differ diff --git a/screenshots/white_list_vis_2.png b/screenshots/white_list_vis_2.png new file mode 100644 index 0000000..d727808 Binary files /dev/null and b/screenshots/white_list_vis_2.png differ