diff --git a/android_app/app/build.gradle b/android_app/app/build.gradle index ed852f07..d0ea3d7f 100644 --- a/android_app/app/build.gradle +++ b/android_app/app/build.gradle @@ -60,7 +60,7 @@ android { } dependencies { - implementation 'com.google.android.material:material:1.1.0-beta01' + implementation 'com.google.android.material:material:1.2.0-alpha01' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.0.0' @@ -72,10 +72,12 @@ dependencies { implementation 'com.github.weliem:blessed-android:0.10' // CustomActivityOnCrash implementation 'cat.ereza:customactivityoncrash:2.2.0' + // AppIntro + implementation 'com.github.AppIntro:AppIntro:5.1.0' // Room - implementation 'androidx.room:room-runtime:2.2.0' - annotationProcessor 'androidx.room:room-compiler:2.2.0' - androidTestImplementation 'androidx.room:room-testing:2.2.0' + implementation 'androidx.room:room-runtime:2.2.1' + annotationProcessor 'androidx.room:room-compiler:2.2.1' + androidTestImplementation 'androidx.room:room-testing:2.2.1' // Timber implementation 'com.jakewharton.timber:timber:4.7.1' // Local unit tests diff --git a/android_app/app/src/main/AndroidManifest.xml b/android_app/app/src/main/AndroidManifest.xml index f1a01bce..a775fedc 100644 --- a/android_app/app/src/main/AndroidManifest.xml +++ b/android_app/app/src/main/AndroidManifest.xml @@ -38,6 +38,8 @@ + + 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 a8a0d60f..3fa336d0 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 @@ -55,15 +55,15 @@ import com.health.openscale.core.OpenScale; import com.health.openscale.core.bluetooth.BluetoothCommunication; import com.health.openscale.core.datatypes.ScaleMeasurement; import com.health.openscale.core.datatypes.ScaleUser; +import com.health.openscale.gui.activities.AppIntroActivity; import com.health.openscale.gui.activities.BaseAppCompatActivity; +import com.health.openscale.gui.activities.BluetoothSettingsActivity; import com.health.openscale.gui.activities.DataEntryActivity; import com.health.openscale.gui.activities.SettingsActivity; -import com.health.openscale.gui.activities.UserSettingsActivity; 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.preferences.BluetoothPreferences; import java.io.File; import java.util.List; @@ -150,9 +150,8 @@ public class MainActivity extends BaseAppCompatActivity } if (prefs.getBoolean("firstStart", true)) { - Intent intent = new Intent(this, UserSettingsActivity.class); - intent.putExtra(UserSettingsActivity.EXTRA_MODE, UserSettingsActivity.ADD_USER_REQUEST); - startActivity(intent); + Intent appIntroIntent = new Intent(this, AppIntroActivity.class); + startActivity(appIntroIntent); prefs.edit().putBoolean("firstStart", false).apply(); } @@ -479,9 +478,9 @@ public class MainActivity extends BaseAppCompatActivity } String deviceName = prefs.getString( - BluetoothPreferences.PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""); + BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""); String hwAddress = prefs.getString( - BluetoothPreferences.PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, ""); + BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, ""); if (!BluetoothAdapter.checkBluetoothAddress(hwAddress)) { setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); diff --git a/android_app/app/src/main/java/com/health/openscale/gui/activities/AppIntroActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/activities/AppIntroActivity.java new file mode 100644 index 00000000..b7efb31b --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/activities/AppIntroActivity.java @@ -0,0 +1,87 @@ +/* Copyright (C) 2019 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.activities; + +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.github.paolorotolo.appintro.AppIntro; +import com.health.openscale.R; +import com.health.openscale.core.OpenScale; +import com.health.openscale.gui.slides.BluetoothIntroSlide; +import com.health.openscale.gui.slides.MetricsIntroSlide; +import com.health.openscale.gui.slides.OpenSourceIntroSlide; +import com.health.openscale.gui.slides.PrivacyIntroSlide; +import com.health.openscale.gui.slides.SupportIntroSlide; +import com.health.openscale.gui.slides.UserIntroSlide; +import com.health.openscale.gui.slides.WelcomeIntroSlide; + +public class AppIntroActivity extends AppIntro { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setBarColor(getResources().getColor(R.color.blue_normal)); + setWizardMode(true); + setBackButtonVisibilityWithDone(true); + + showSkipButton(true); + + addSlide(WelcomeIntroSlide.newInstance(R.layout.slide_welcome)); + addSlide(PrivacyIntroSlide.newInstance(R.layout.slide_privacy)); + addSlide(UserIntroSlide.newInstance(R.layout.slide_user)); + addSlide(OpenSourceIntroSlide.newInstance(R.layout.slide_opensource)); + addSlide(BluetoothIntroSlide.newInstance(R.layout.slide_bluetooth)); + addSlide(MetricsIntroSlide.newInstance(R.layout.slide_metrics)); + addSlide(SupportIntroSlide.newInstance(R.layout.slide_support)); + } + + @Override + public void onSkipPressed(Fragment currentFragment) { + super.onSkipPressed(currentFragment); + finish(); + checkUserCreation(); + } + + @Override + public void onDonePressed(Fragment currentFragment) { + super.onDonePressed(currentFragment); + finish(); + checkUserCreation(); + } + + @Override + public void onSlideChanged(@Nullable Fragment oldFragment, @Nullable Fragment newFragment) { + super.onSlideChanged(oldFragment, newFragment); + + if (newFragment instanceof WelcomeIntroSlide) { + showSkipButton(true); + } else { + showSkipButton(false); + } + } + + private void checkUserCreation() { + if (OpenScale.getInstance().getSelectedScaleUserId() == -1) { + Intent intent = new Intent(this, UserSettingsActivity.class); + intent.putExtra(UserSettingsActivity.EXTRA_MODE, UserSettingsActivity.ADD_USER_REQUEST); + startActivity(intent); + } + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/activities/BluetoothSettingsActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/activities/BluetoothSettingsActivity.java new file mode 100644 index 00000000..d69cbd9c --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/activities/BluetoothSettingsActivity.java @@ -0,0 +1,396 @@ +/* Copyright (C) 2019 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.activities; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.DatePickerDialog; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.le.ScanResult; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.preference.Preference; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.DatePicker; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RadioGroup; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.drawable.DrawableCompat; + +import com.health.openscale.R; +import com.health.openscale.core.OpenScale; +import com.health.openscale.core.bluetooth.BluetoothCommunication; +import com.health.openscale.core.bluetooth.BluetoothFactory; +import com.health.openscale.core.datatypes.ScaleUser; +import com.health.openscale.core.utils.Converters; +import com.health.openscale.gui.preferences.BluetoothPreferences; +import com.health.openscale.gui.utils.ColorUtil; +import com.health.openscale.gui.utils.PermissionHelper; +import com.welie.blessed.BluetoothCentral; +import com.welie.blessed.BluetoothCentralCallback; +import com.welie.blessed.BluetoothPeripheral; + +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import timber.log.Timber; + +public class BluetoothSettingsActivity extends BaseAppCompatActivity { + private Context context; + + public static final int GET_SCALE_REQUEST = 150; + + public static final String PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME = "btDeviceName"; + public static final String PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS = "btHwAddress"; + + private Map foundDevices = new HashMap<>(); + + private LinearLayout deviceListView; + private TextView txtSearching; + private ProgressBar progressBar; + private Handler progressHandler; + private BluetoothCentral central; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_bluetoothsettings); + context = this; + + deviceListView = findViewById(R.id.deviceListView); + txtSearching = findViewById(R.id.txtSearching); + progressBar = findViewById(R.id.progressBar); + Toolbar toolbar = findViewById(R.id.bluetoothSettingToolbar); + setSupportActionBar(toolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle(R.string.label_bluetooth_title); + + if (PermissionHelper.requestBluetoothPermission(this)) { + if (PermissionHelper.requestLocationServicePermission(this)) { + startBluetoothDiscovery(); + } + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + // Override the default behaviour in order to return to the correct fragment + // (e.g. the table view) and not always go to the overview. + case android.R.id.home: + stopBluetoothDiscovery(); + onBackPressed(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private static final String formatDeviceName(String name, String address) { + if (name.isEmpty() || address.isEmpty()) { + return "-"; + } + return String.format("%s [%s]", name, address); + } + + private static final String formatDeviceName(BluetoothDevice device) { + return formatDeviceName(device.getName(), device.getAddress()); + } + + private final BluetoothCentralCallback bluetoothCentralCallback = new BluetoothCentralCallback() { + @Override + public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) { + onDeviceFound(scanResult); + } + }; + + private void startBluetoothDiscovery() { + deviceListView.removeAllViews(); + foundDevices.clear(); + + central = new BluetoothCentral(getApplicationContext(), bluetoothCentralCallback, new Handler(Looper.getMainLooper())); + central.scanForPeripherals(); + + txtSearching.setVisibility(View.VISIBLE); + txtSearching.setText(R.string.label_bluetooth_searching); + progressBar.setVisibility(View.VISIBLE); + + progressHandler = new Handler(); + + // Don't let the BLE discovery run forever + progressHandler.postDelayed(new Runnable() { + @Override + public void run() { + stopBluetoothDiscovery(); + + txtSearching.setText(R.string.label_bluetooth_searching_finished); + progressBar.setVisibility(View.GONE); + + BluetoothDeviceView notSupported = new BluetoothDeviceView(context); + notSupported.setDeviceName(getString(R.string.label_scale_not_supported)); + notSupported.setSummaryText(getString(R.string.label_click_to_help_add_support)); + notSupported.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent notSupportedIntent = new Intent(Intent.ACTION_VIEW); + notSupportedIntent.setData( + Uri.parse("https://github.com/oliexdev/openScale/wiki/Supported-scales-in-openScale")); + + startActivity(notSupportedIntent); + } + }); + deviceListView.addView(notSupported); + } + }, 20 * 1000); + } + + private void stopBluetoothDiscovery() { + if (progressHandler != null) { + progressHandler.removeCallbacksAndMessages(null); + progressHandler = null; + } + + if (central != null) { + central.stopScan(); + } + } + + private void onDeviceFound(final ScanResult bleScanResult) { + BluetoothDevice device = bleScanResult.getDevice(); + + if (device.getName() == null || foundDevices.containsKey(device.getAddress())) { + return; + } + + BluetoothDeviceView deviceView = new BluetoothDeviceView(this); + deviceView.setDeviceName(formatDeviceName(bleScanResult.getDevice())); + + BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(this, device.getName()); + if (btDevice != null) { + Timber.d("Found supported device %s (driver: %s)", + formatDeviceName(device), btDevice.driverName()); + deviceView.setOnClickListener(new onClickListenerDeviceSelect()); + deviceView.setDeviceAddress(device.getAddress()); + deviceView.setIcon(R.drawable.ic_bluetooth_device_supported); + deviceView.setSummaryText(btDevice.driverName()); + } + else { + Timber.d("Found unsupported device %s", + formatDeviceName(device)); + deviceView.setIcon(R.drawable.ic_bluetooth_device_not_supported); + deviceView.setSummaryText(getString(R.string.label_bt_device_no_support)); + deviceView.setEnabled(false); + + if (OpenScale.DEBUG_MODE) { + deviceView.setEnabled(true); + deviceView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + getDebugInfo(device); + } + }); + } + } + + foundDevices.put(device.getAddress(), btDevice != null ? device : null); + deviceListView.addView(deviceView); + } + + private class onClickListenerDeviceSelect implements View.OnClickListener { + @Override + public void onClick(View view) { + BluetoothDeviceView deviceView = (BluetoothDeviceView)view; + + BluetoothDevice device = foundDevices.get(deviceView.getDeviceAddress()); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + prefs.edit() + .putString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, device.getAddress()) + .putString(PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, device.getName()) + .apply(); + + finishActivity(GET_SCALE_REQUEST); + } + } + + private void getDebugInfo(final BluetoothDevice device) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Fetching info") + .setMessage("Please wait while we fetch extended info from your scale...") + .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + OpenScale.getInstance().disconnectFromBluetoothDevice(); + dialog.dismiss(); + } + }); + + final AlertDialog dialog = builder.create(); + + Handler btHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (BluetoothCommunication.BT_STATUS.values()[msg.what]) { + case CONNECTION_LOST: + OpenScale.getInstance().disconnectFromBluetoothDevice(); + dialog.dismiss(); + break; + } + } + }; + + dialog.show(); + + String macAddress = device.getAddress(); + stopBluetoothDiscovery(); + OpenScale.getInstance().connectToBluetoothDeviceDebugMode(macAddress, btHandler); + } + + private class BluetoothDeviceView extends LinearLayout { + + private TextView deviceName; + private ImageView deviceIcon; + private String deviceAddress; + + public BluetoothDeviceView(Context context) { + super(context); + + deviceName = new TextView(context); + deviceName.setLines(2); + deviceIcon = new ImageView(context); + + addView(deviceIcon); + addView(deviceName); + } + + public void setDeviceAddress(String address) { + deviceAddress = address; + } + + public String getDeviceAddress() { + return deviceAddress; + } + + public void setDeviceName(String name) { + deviceName.setText(name); + } + + public void setSummaryText(String text) { + SpannableStringBuilder stringBuilder = new SpannableStringBuilder(new String()); + + stringBuilder.append(deviceName.getText()); + stringBuilder.append("\n"); + + int deviceNameLength = deviceName.getText().length(); + + stringBuilder.append(text); + stringBuilder.setSpan(new ForegroundColorSpan(Color.GRAY), deviceNameLength, deviceNameLength + text.length()+1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + stringBuilder.setSpan(new RelativeSizeSpan(0.8f), deviceNameLength, deviceNameLength + text.length()+1, + Spanned.SPAN_INCLUSIVE_INCLUSIVE); + + deviceName.setText(stringBuilder); + } + + public void setIcon(int resId) { + deviceIcon.setImageResource(resId); + + int tintColor = ColorUtil.getTextColor(getApplicationContext()); + deviceIcon.setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); + } + + @Override + public void setOnClickListener(OnClickListener listener) { + super.setOnClickListener(listener); + deviceName.setOnClickListener(listener); + deviceIcon.setOnClickListener(listener); + } + + @Override + public void setEnabled(boolean status) { + super.setEnabled(status); + deviceName.setEnabled(status); + deviceIcon.setEnabled(status); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == PermissionHelper.ENABLE_BLUETOOTH_REQUEST) { + if (resultCode == Activity.RESULT_OK) { + if (PermissionHelper.requestBluetoothPermission(this)) { + startBluetoothDiscovery(); + } + } + else { + onBackPressed(); + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (PermissionHelper.requestLocationServicePermission(this)) { + startBluetoothDiscovery(); + } + } else { + Toast.makeText(this, R.string.permission_not_granted, Toast.LENGTH_SHORT).show(); + onBackPressed(); + } + break; + } + } + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/activities/SettingsActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/activities/SettingsActivity.java index 39340003..78681de0 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/activities/SettingsActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/activities/SettingsActivity.java @@ -105,10 +105,6 @@ public class SettingsActivity extends PreferenceActivity public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { // TODO HACK to call RequestPermissionResult(...) in PreferenceFragment otherwise API level > 23 is required switch(requestCode) { - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: - BluetoothPreferences bluetoothPreferences = (BluetoothPreferences)currentFragment; - bluetoothPreferences.onMyOwnRequestPermissionsResult(requestCode, permissions, grantResults); - break; case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_READ_STORAGE: case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE: BackupPreferences backupPreferences = (BackupPreferences)currentFragment; 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 e3382cc1..bfc8bb1e 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 @@ -40,6 +40,8 @@ import com.health.openscale.R; import com.health.openscale.core.OpenScale; import com.health.openscale.core.bluetooth.BluetoothCommunication; import com.health.openscale.core.bluetooth.BluetoothFactory; +import com.health.openscale.gui.activities.BluetoothSettingsActivity; +import com.health.openscale.gui.activities.UserSettingsActivity; import com.health.openscale.gui.utils.ColorUtil; import com.health.openscale.gui.utils.PermissionHelper; import com.welie.blessed.BluetoothCentral; @@ -51,18 +53,13 @@ import java.util.Map; import timber.log.Timber; +import static android.app.Activity.RESULT_OK; + public class BluetoothPreferences extends PreferenceFragment { private static final String PREFERENCE_KEY_BLUETOOTH_SCANNER = "btScanner"; - public static final String PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME = "btDeviceName"; - public static final String PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS = "btHwAddress"; - private PreferenceScreen btScanner; - private Map foundDevices = new HashMap<>(); - - private Handler progressHandler; - private BluetoothCentral central; private static final String formatDeviceName(String name, String address) { if (name.isEmpty() || address.isEmpty()) { @@ -71,141 +68,11 @@ public class BluetoothPreferences extends PreferenceFragment { return String.format("%s [%s]", name, address); } - private static final String formatDeviceName(BluetoothDevice device) { - return formatDeviceName(device.getName(), device.getAddress()); - } - private String getCurrentDeviceName() { SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); return formatDeviceName( - prefs.getString(PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""), - prefs.getString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, "")); - } - - private final BluetoothCentralCallback bluetoothCentralCallback = new BluetoothCentralCallback() { - @Override - public void onDiscoveredPeripheral(BluetoothPeripheral peripheral, ScanResult scanResult) { - onDeviceFound(scanResult); - } - }; - - private void startBluetoothDiscovery() { - foundDevices.clear(); - btScanner.removeAll(); - - central = new BluetoothCentral(getActivity().getApplicationContext(), bluetoothCentralCallback, new Handler(Looper.getMainLooper())); - central.scanForPeripherals(); - - final Preference scanning = new Preference(getActivity()); - scanning.setEnabled(false); - btScanner.addPreference(scanning); - btScanner.getPreference(0).setTitle(R.string.label_bluetooth_searching); - - progressHandler = new Handler(); - - // Draw a simple progressbar during the discovery/scanning - final int progressUpdatePeriod = 150; - final String[] blocks = {"▏","▎","▍","▌","▋","▊","▉","█"}; - scanning.setSummary(blocks[0]); - progressHandler.postDelayed(new Runnable() { - int index = 1; - @Override - public void run() { - String summary = scanning.getSummary() - .subSequence(0, scanning.getSummary().length() - 1).toString(); - if (index == blocks.length) { - summary += blocks[blocks.length - 1]; - index = 0; - } - summary += blocks[index++]; - scanning.setSummary(summary); - progressHandler.postDelayed(this, progressUpdatePeriod); - } - }, progressUpdatePeriod); - - // Don't let the BLE discovery run forever - progressHandler.postDelayed(new Runnable() { - @Override - public void run() { - stopBluetoothDiscovery(); - - Preference scanning = btScanner.getPreference(0); - scanning.setTitle(R.string.label_bluetooth_searching_finished); - scanning.setSummary(""); - - Intent notSupportedIntent = new Intent(Intent.ACTION_VIEW); - notSupportedIntent.setData( - Uri.parse("https://github.com/oliexdev/openScale/wiki/Supported-scales-in-openScale")); - - Preference notSupported = new Preference(getActivity()); - notSupported.setTitle(R.string.label_scale_not_supported); - notSupported.setSummary(R.string.label_click_to_help_add_support); - notSupported.setIntent(notSupportedIntent); - btScanner.addPreference(notSupported); - } - }, 20 * 1000); - - // Abort discovery and scan if back is pressed or a scale is selected - btScanner.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - stopBluetoothDiscovery(); - } - }); - } - - private void stopBluetoothDiscovery() { - if (progressHandler != null) { - progressHandler.removeCallbacksAndMessages(null); - progressHandler = null; - } - - central.stopScan(); - } - - private void onDeviceFound(final ScanResult bleScanResult) { - BluetoothDevice device = bleScanResult.getDevice(); - - if (device.getName() == null || foundDevices.containsKey(device.getAddress())) { - return; - } - - Preference prefBtDevice = new Preference(getActivity()); - prefBtDevice.setTitle(formatDeviceName(bleScanResult.getDevice())); - - BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(getActivity(), device.getName()); - if (btDevice != null) { - Timber.d("Found supported device %s (driver: %s)", - formatDeviceName(device), btDevice.driverName()); - prefBtDevice.setOnPreferenceClickListener(new onClickListenerDeviceSelect()); - prefBtDevice.setKey(device.getAddress()); - prefBtDevice.setIcon(R.drawable.ic_bluetooth_device_supported); - prefBtDevice.setSummary(btDevice.driverName()); - - int tintColor = ColorUtil.getTextColor(getActivity().getApplicationContext()); - prefBtDevice.getIcon().setColorFilter(tintColor, PorterDuff.Mode.SRC_IN); - } - else { - Timber.d("Found unsupported device %s", - formatDeviceName(device)); - prefBtDevice.setIcon(R.drawable.ic_bluetooth_device_not_supported); - prefBtDevice.setSummary(R.string.label_bt_device_no_support); - prefBtDevice.setEnabled(false); - - if (OpenScale.DEBUG_MODE) { - prefBtDevice.setEnabled(true); - prefBtDevice.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - getDebugInfo(device); - return false; - } - }); - } - } - - foundDevices.put(device.getAddress(), btDevice != null ? device : null); - btScanner.addPreference(prefBtDevice); + prefs.getString(BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""), + prefs.getString(BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, "")); } private void updateBtScannerSummary() { @@ -222,15 +89,11 @@ public class BluetoothPreferences extends PreferenceFragment { btScanner = (PreferenceScreen) findPreference(PREFERENCE_KEY_BLUETOOTH_SCANNER); - final Fragment fragment = this; btScanner.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { - if (PermissionHelper.requestBluetoothPermission(getActivity(), fragment)) { - if (PermissionHelper.requestLocationServicePermission(getActivity())) { - startBluetoothDiscovery(); - } - } + Intent intent = new Intent(preference.getContext(), BluetoothSettingsActivity.class); + startActivityForResult(intent, BluetoothSettingsActivity.GET_SCALE_REQUEST); return true; } }); @@ -239,98 +102,15 @@ public class BluetoothPreferences extends PreferenceFragment { // Dummy preference to make screen open btScanner.addPreference(new Preference(getActivity())); - - } - - @Override - public void onStart() { - super.onStart(); - - // Restart discovery after e.g. orientation change - if (btScanner.getDialog() != null && btScanner.getDialog().isShowing()) { - startBluetoothDiscovery(); - } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PermissionHelper.ENABLE_BLUETOOTH_REQUEST) { - if (resultCode == Activity.RESULT_OK) { - if (PermissionHelper.requestBluetoothPermission(getActivity(), this)) { - startBluetoothDiscovery(); - } - } - else { - btScanner.getDialog().dismiss(); - } + if (resultCode != RESULT_OK) { + return; } - } - - private void getDebugInfo(final BluetoothDevice device) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - builder.setTitle("Fetching info") - .setMessage("Please wait while we fetch extended info from your scale...") - .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - OpenScale.getInstance().disconnectFromBluetoothDevice(); - dialog.dismiss(); - } - }); - - final AlertDialog dialog = builder.create(); - - Handler btHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (BluetoothCommunication.BT_STATUS.values()[msg.what]) { - case CONNECTION_LOST: - OpenScale.getInstance().disconnectFromBluetoothDevice(); - dialog.dismiss(); - break; - } - } - }; - - dialog.show(); - - String macAddress = device.getAddress(); - stopBluetoothDiscovery(); - OpenScale.getInstance().connectToBluetoothDeviceDebugMode( - macAddress, btHandler); - } - - private class onClickListenerDeviceSelect implements Preference.OnPreferenceClickListener { - @Override - public boolean onPreferenceClick(final Preference preference) { - BluetoothDevice device = foundDevices.get(preference.getKey()); - - preference.getSharedPreferences().edit() - .putString(PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, device.getAddress()) - .putString(PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, device.getName()) - .apply(); - + if (requestCode == BluetoothSettingsActivity.GET_SCALE_REQUEST) { updateBtScannerSummary(); - - btScanner.getDialog().dismiss(); - - return true; - } - } - - public void onMyOwnRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - switch (requestCode) { - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - if (PermissionHelper.requestLocationServicePermission(getActivity())) { - startBluetoothDiscovery(); - } - } else { - Toast.makeText(getActivity(), R.string.permission_not_granted, Toast.LENGTH_SHORT).show(); - btScanner.getDialog().dismiss(); - } - break; - } } } } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/BluetoothIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/BluetoothIntroSlide.java new file mode 100644 index 00000000..fd730826 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/BluetoothIntroSlide.java @@ -0,0 +1,106 @@ +/* Copyright (C) 2019 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.slides; + +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; +import com.health.openscale.gui.activities.BluetoothSettingsActivity; + +import static android.app.Activity.RESULT_OK; + +public class BluetoothIntroSlide extends Fragment { + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + private Button btnSearchScale; + private TextView txtFoundDevice; + + public static BluetoothIntroSlide newInstance(int layoutResId) { + BluetoothIntroSlide sampleSlide = new BluetoothIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + txtFoundDevice = view.findViewById(R.id.txtFoundDevice); + txtFoundDevice.setText(getCurrentDeviceName()); + + btnSearchScale = view.findViewById(R.id.btnSearchScale); + btnSearchScale.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(getContext(), BluetoothSettingsActivity.class); + startActivityForResult(intent, BluetoothSettingsActivity.GET_SCALE_REQUEST); + } + }); + + return view; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != RESULT_OK) { + return; + } + if (requestCode == BluetoothSettingsActivity.GET_SCALE_REQUEST) { + txtFoundDevice.setText(getCurrentDeviceName()); + } + } + + private final String formatDeviceName(String name, String address) { + if (name.isEmpty() || address.isEmpty()) { + return "[" + getContext().getString(R.string.label_empty) + "]"; + } + return String.format("%s [%s]", name, address); + } + + private String getCurrentDeviceName() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + return formatDeviceName( + prefs.getString(BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""), + prefs.getString(BluetoothSettingsActivity.PREFERENCE_KEY_BLUETOOTH_HW_ADDRESS, "")); + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/MetricsIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/MetricsIntroSlide.java new file mode 100644 index 00000000..439f732f --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/MetricsIntroSlide.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2019 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.slides; + +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; + +public class MetricsIntroSlide extends Fragment { + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + private TextView slideMainText; + + public static MetricsIntroSlide newInstance(int layoutResId) { + MetricsIntroSlide sampleSlide = new MetricsIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + slideMainText = view.findViewById(R.id.slideMainText); + slideMainText.setLinksClickable(true); + slideMainText.setMovementMethod(LinkMovementMethod.getInstance()); + + return view; + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/OpenSourceIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/OpenSourceIntroSlide.java new file mode 100644 index 00000000..dff77219 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/OpenSourceIntroSlide.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2019 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.slides; + +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; + +public class OpenSourceIntroSlide extends Fragment { + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + private TextView slideMainText; + + public static OpenSourceIntroSlide newInstance(int layoutResId) { + OpenSourceIntroSlide sampleSlide = new OpenSourceIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + slideMainText = view.findViewById(R.id.slideMainText); + slideMainText.setLinksClickable(true); + slideMainText.setMovementMethod(LinkMovementMethod.getInstance()); + + return view; + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/PrivacyIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/PrivacyIntroSlide.java new file mode 100644 index 00000000..c3d32ad7 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/PrivacyIntroSlide.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2019 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.slides; + +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; + +public class PrivacyIntroSlide extends Fragment { + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + private TextView slideMainText; + + public static PrivacyIntroSlide newInstance(int layoutResId) { + PrivacyIntroSlide sampleSlide = new PrivacyIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + slideMainText = view.findViewById(R.id.slideMainText); + slideMainText.setLinksClickable(true); + slideMainText.setMovementMethod(LinkMovementMethod.getInstance()); + + return view; + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/SupportIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/SupportIntroSlide.java new file mode 100644 index 00000000..531b7b8c --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/SupportIntroSlide.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2019 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.slides; + +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; + +public class SupportIntroSlide extends Fragment { + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + private TextView slideMainText; + + public static SupportIntroSlide newInstance(int layoutResId) { + SupportIntroSlide sampleSlide = new SupportIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + slideMainText = view.findViewById(R.id.slideMainText); + slideMainText.setLinksClickable(true); + slideMainText.setMovementMethod(LinkMovementMethod.getInstance()); + + return view; + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/UserIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/UserIntroSlide.java new file mode 100644 index 00000000..4cef57ec --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/UserIntroSlide.java @@ -0,0 +1,165 @@ +/* Copyright (C) 2019 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.slides; + +import android.content.Intent; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import com.health.openscale.R; +import com.health.openscale.core.OpenScale; +import com.health.openscale.core.datatypes.ScaleUser; +import com.health.openscale.gui.activities.UserSettingsActivity; + +import java.util.List; + +public class UserIntroSlide extends Fragment{ + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + private Button btnAddUser; + private TableLayout tblUsers; + + public static UserIntroSlide newInstance(int layoutResId) { + UserIntroSlide sampleSlide = new UserIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(layoutResId, container, false); + + btnAddUser = view.findViewById(R.id.btnAddUser); + tblUsers = view.findViewById(R.id.tblUsers); + + btnAddUser.setOnClickListener(new onBtnAddUserClickListener()); + + updateTableUsers(); + + return view; + } + + private class onBtnAddUserClickListener implements View.OnClickListener { + + @Override + public void onClick(View view) { + Intent intent = new Intent(getContext(), UserSettingsActivity.class); + intent.putExtra(UserSettingsActivity.EXTRA_MODE, UserSettingsActivity.ADD_USER_REQUEST); + startActivityForResult(intent, 100); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + updateTableUsers(); + } + + private void updateTableUsers() { + tblUsers.removeAllViews(); + tblUsers.setStretchAllColumns(true); + + List scaleUserList = OpenScale.getInstance().getScaleUserList(); + + TableRow header = new TableRow(getContext()); + + TextView headerUsername = new TextView(getContext()); + headerUsername.setText(R.string.label_user_name); + headerUsername.setGravity(Gravity.CENTER_HORIZONTAL); + headerUsername.setTypeface(null, Typeface.BOLD); + header.addView(headerUsername); + + TextView headAge = new TextView(getContext()); + headAge.setText(R.string.label_age); + headAge.setGravity(Gravity.CENTER_HORIZONTAL); + headAge.setTypeface(null, Typeface.BOLD); + header.addView(headAge); + + TextView headerGender = new TextView(getContext()); + headerGender.setText(R.string.label_gender); + headerGender.setGravity(Gravity.CENTER_HORIZONTAL); + headerGender.setTypeface(null, Typeface.BOLD); + header.addView(headerGender); + + tblUsers.addView(header); + + if (!scaleUserList.isEmpty()) { + TableRow row = new TableRow(getContext()); + + for (ScaleUser scaleUser : scaleUserList) { + row = new TableRow(getContext()); + + TextView txtUsername = new TextView(getContext()); + txtUsername.setText(scaleUser.getUserName()); + txtUsername.setGravity(Gravity.CENTER_HORIZONTAL); + row.addView(txtUsername); + + TextView txtAge = new TextView(getContext()); + txtAge.setText(Integer.toString(scaleUser.getAge())); + txtAge.setGravity(Gravity.CENTER_HORIZONTAL); + row.addView(txtAge); + + TextView txtGender = new TextView(getContext()); + txtGender.setText((scaleUser.getGender().isMale()) ? getString(R.string.label_male) : getString(R.string.label_female)); + txtGender.setGravity(Gravity.CENTER_HORIZONTAL); + row.addView(txtGender); + + row.setGravity(Gravity.CENTER_HORIZONTAL); + + tblUsers.addView(row); + } + } else { + TableRow row = new TableRow(getContext()); + + TextView txtEmpty = new TextView(getContext()); + txtEmpty.setText("[" + getContext().getString(R.string.label_empty) + "]"); + txtEmpty.setGravity(Gravity.CENTER_HORIZONTAL); + row.addView(txtEmpty); + + row.setGravity(Gravity.CENTER_HORIZONTAL); + + tblUsers.addView(row); + } + } +} diff --git a/android_app/app/src/main/java/com/health/openscale/gui/slides/WelcomeIntroSlide.java b/android_app/app/src/main/java/com/health/openscale/gui/slides/WelcomeIntroSlide.java new file mode 100644 index 00000000..f73e43c2 --- /dev/null +++ b/android_app/app/src/main/java/com/health/openscale/gui/slides/WelcomeIntroSlide.java @@ -0,0 +1,56 @@ +/* Copyright (C) 2019 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.slides; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +public class WelcomeIntroSlide extends Fragment { + + private static final String ARG_LAYOUT_RES_ID = "layoutResId"; + private int layoutResId; + + public static WelcomeIntroSlide newInstance(int layoutResId) { + WelcomeIntroSlide sampleSlide = new WelcomeIntroSlide(); + + Bundle args = new Bundle(); + args.putInt(ARG_LAYOUT_RES_ID, layoutResId); + sampleSlide.setArguments(args); + + return sampleSlide; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID)) { + layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + return inflater.inflate(layoutResId, container, false); + } +} 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 index c40ae959..8d01ba21 100644 --- 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 @@ -43,7 +43,7 @@ public class PermissionHelper { public final static int ENABLE_BLUETOOTH_REQUEST = 5; - public static boolean requestBluetoothPermission(final Activity activity, Fragment fragment) { + public static boolean requestBluetoothPermission(final Activity activity) { final BluetoothManager bluetoothManager = (BluetoothManager) activity.getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); @@ -52,7 +52,7 @@ public class PermissionHelper { if (btAdapter != null) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - fragment.startActivityForResult(enableBtIntent, ENABLE_BLUETOOTH_REQUEST); + activity.startActivityForResult(enableBtIntent, ENABLE_BLUETOOTH_REQUEST); } return false; } diff --git a/android_app/app/src/main/res/drawable-hdpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-hdpi/ic_slide_group.png new file mode 100644 index 00000000..52dd6daf Binary files /dev/null and b/android_app/app/src/main/res/drawable-hdpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-hdpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-hdpi/ic_slide_opensource.png new file mode 100644 index 00000000..53d93905 Binary files /dev/null and b/android_app/app/src/main/res/drawable-hdpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-hdpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-hdpi/ic_slide_privacy.png new file mode 100644 index 00000000..1792caef Binary files /dev/null and b/android_app/app/src/main/res/drawable-hdpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-hdpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-hdpi/ic_slide_support.png new file mode 100644 index 00000000..1d9953ae Binary files /dev/null and b/android_app/app/src/main/res/drawable-hdpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/drawable-ldpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-ldpi/ic_slide_group.png new file mode 100644 index 00000000..db69c82b Binary files /dev/null and b/android_app/app/src/main/res/drawable-ldpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-ldpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-ldpi/ic_slide_opensource.png new file mode 100644 index 00000000..1c649301 Binary files /dev/null and b/android_app/app/src/main/res/drawable-ldpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-ldpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-ldpi/ic_slide_privacy.png new file mode 100644 index 00000000..ff9de101 Binary files /dev/null and b/android_app/app/src/main/res/drawable-ldpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-ldpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-ldpi/ic_slide_support.png new file mode 100644 index 00000000..d64dd4bf Binary files /dev/null and b/android_app/app/src/main/res/drawable-ldpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/drawable-mdpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-mdpi/ic_slide_group.png new file mode 100644 index 00000000..89fad028 Binary files /dev/null and b/android_app/app/src/main/res/drawable-mdpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-mdpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-mdpi/ic_slide_opensource.png new file mode 100644 index 00000000..aee10d5f Binary files /dev/null and b/android_app/app/src/main/res/drawable-mdpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-mdpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-mdpi/ic_slide_privacy.png new file mode 100644 index 00000000..7027baa2 Binary files /dev/null and b/android_app/app/src/main/res/drawable-mdpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-mdpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-mdpi/ic_slide_support.png new file mode 100644 index 00000000..e13a6efb Binary files /dev/null and b/android_app/app/src/main/res/drawable-mdpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/drawable-xhdpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_group.png new file mode 100644 index 00000000..7748325d Binary files /dev/null and b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-xhdpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_opensource.png new file mode 100644 index 00000000..c86cc074 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-xhdpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_privacy.png new file mode 100644 index 00000000..4ed6fc31 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-xhdpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_support.png new file mode 100644 index 00000000..23167953 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xhdpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_group.png new file mode 100644 index 00000000..30e18e57 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_opensource.png new file mode 100644 index 00000000..cf731c86 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_privacy.png new file mode 100644 index 00000000..47f4edca Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_support.png new file mode 100644 index 00000000..63d0bd79 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxhdpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_group.png b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_group.png new file mode 100644 index 00000000..1eedcc21 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_group.png differ diff --git a/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_opensource.png b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_opensource.png new file mode 100644 index 00000000..9f7feaa3 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_opensource.png differ diff --git a/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_privacy.png b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_privacy.png new file mode 100644 index 00000000..8d0a5a92 Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_privacy.png differ diff --git a/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_support.png b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_support.png new file mode 100644 index 00000000..0721a99b Binary files /dev/null and b/android_app/app/src/main/res/drawable-xxxhdpi/ic_slide_support.png differ diff --git a/android_app/app/src/main/res/layout/activity_bluetoothsettings.xml b/android_app/app/src/main/res/layout/activity_bluetoothsettings.xml new file mode 100644 index 00000000..71ed84bb --- /dev/null +++ b/android_app/app/src/main/res/layout/activity_bluetoothsettings.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + diff --git a/android_app/app/src/main/res/layout/slide_bluetooth.xml b/android_app/app/src/main/res/layout/slide_bluetooth.xml new file mode 100644 index 00000000..3250aaae --- /dev/null +++ b/android_app/app/src/main/res/layout/slide_bluetooth.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + +