1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-28 18:49:56 +02:00

Add a ContentProvider so other apps can access (with permission) the openScale data.

This could be used for e.g. syncing weight logs to a third-party service such as Google Fit,
without the openScale app having to do so; with this, a 'connector' app could be written that
reads this data and does the push to a third-party service (or, indeed, an HTTP RPC connector,
an MQTT connector, SMS notifications, or anything else) without openScale needing this
functionality (or additional permissions).
This commit is contained in:
Paul Cowan
2018-07-04 23:28:36 +10:00
parent a2543afe02
commit cfa86f738c
6 changed files with 177 additions and 0 deletions

View File

@@ -9,6 +9,15 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- Permission to allow read of data from the database through a ContentProvider.
Marked "dangerous" so that explicit user approval is required to read this data, not
just the permission implied from installing the app from the Play Store. -->
<permission
android:name="com.health.openscale.READ_DATA"
android:description="@string/permission_read_data_description"
android:label="@string/permission_read_data_label"
android:protectionLevel="dangerous" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
<application
@@ -59,6 +68,13 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
<provider
android:name=".core.export.OpenScaleContentProvider"
android:authorities="com.health.openscale.provider"
android:enabled="true"
android:exported="true"
android:readPermission="com.health.openscale.READ_DATA">
</provider>
</application>
</manifest>

View File

@@ -606,4 +606,19 @@ public class OpenScale {
}
}
}
// As getScaleUserList(), but as a Cursor for export via a Content Provider.
public Cursor getScaleUserListCursor() {
return userDAO.selectAll();
}
// As getScaleUser(), but as a Cursor for export via a Content Provider.
public Cursor getScaleUserCursor(int userId) {
return userDAO.select(userId);
}
// As getScaleMeasurementList(), but as a Cursor for export via a Content Provider.
public Cursor getScaleMeasurementListCursor(int userId) {
return measurementDAO.selectAll(userId);
}
}

View File

@@ -21,6 +21,7 @@ import android.arch.persistence.room.Insert;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Query;
import android.arch.persistence.room.Update;
import android.database.Cursor;
import com.health.openscale.core.datatypes.ScaleMeasurement;
@@ -64,4 +65,8 @@ public interface ScaleMeasurementDAO {
@Query("DELETE FROM scaleMeasurements WHERE userId = :userId")
void deleteAll(int userId);
// selectAll() is equivalent to getAll(), but returns a Cursor, for exposing via a ContentProvider.
@Query("SELECT * FROM scaleMeasurements WHERE userId = :userId AND enabled = 1 ORDER BY datetime DESC")
Cursor selectAll(int userId);
}

View File

@@ -21,6 +21,7 @@ import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.Query;
import android.arch.persistence.room.Update;
import android.database.Cursor;
import com.health.openscale.core.datatypes.ScaleUser;
@@ -45,4 +46,13 @@ public interface ScaleUserDAO {
@Delete
void delete(ScaleUser user);
// selectAll() and select() are equivalent to getall() and get(), but return a Cursor,
// for exposing via a ContentProvider.
@Query("SELECT * FROM scaleUsers")
Cursor selectAll();
@Query("SELECT * FROM scaleUsers WHERE id = :id")
Cursor select(int id);
}

View File

@@ -0,0 +1,129 @@
/* Copyright (C) 2018 Paul Cowan <paul@custardsource.com>
*
* 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 <http://www.gnu.org/licenses/>
*/package com.health.openscale.core.export;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.health.openscale.core.OpenScale;
/**
* Exposes the user and measurement data from openScale via
* <a href="https://developer.android.com/guide/topics/providers/content-providers">Android
* Content Providers</a>. This allows other apps to access the openScale data for their own purposes
* (e.g. syncing to third-party services like Google Fit, Fitbit API, etc) without openScale itself
* needing to do so or request additional permissions. <br />
*
* This access is gated by the com.health.openscale.READ_DATA permission, which is defined in the
* manifest; it is not accessible to any other app without user confirmation.<br />
*
* The following URIs are supported:
* <ul>
* <li><code>content://com.health.openscale.provider/user</code>: list all users.</li>
* <li><code>content://com.health.openscale.provider/user/$ID</code>: retrieve single user
* by ID.</li>
* <li><code>content://com.health.openscale.provider/user/$ID/measurements</code>:
* retrieve all measurements for the supplied user ID.</li>
* </ul>
*/
public class OpenScaleContentProvider extends ContentProvider {
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final String AUTHORITY = "com.health.openscale.provider";
private static final int MATCH_TYPE_USER_LIST = 1;
private static final int MATCH_TYPE_USER_ENTRY = 2;
private static final int MATCH_TYPE_USER_MEASUREMENTS = 3;
static {
uriMatcher.addURI(AUTHORITY, "user", MATCH_TYPE_USER_LIST);
uriMatcher.addURI(AUTHORITY, "user/#", MATCH_TYPE_USER_ENTRY);
uriMatcher.addURI(AUTHORITY, "user/#/measurements", MATCH_TYPE_USER_MEASUREMENTS);
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case MATCH_TYPE_USER_LIST:
return "vnd.android.cursor.dir/vnd.com.health.openscale.provider.user";
case MATCH_TYPE_USER_ENTRY:
return "vnd.android.cursor.item/vnd.com.health.openscale.provider.user";
case MATCH_TYPE_USER_MEASUREMENTS:
return "vnd.android.cursor.item/vnd.com.health.openscale.provider.measurement";
default:
return null;
}
}
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
final Context context = getContext();
Cursor cursor;
switch (uriMatcher.match(uri)) {
case MATCH_TYPE_USER_LIST:
cursor = OpenScale.getInstance().getScaleUserListCursor();
break;
case MATCH_TYPE_USER_ENTRY:
cursor = OpenScale.getInstance().getScaleUserCursor(Integer.valueOf(uri.getPathSegments().get(1)));
break;
case MATCH_TYPE_USER_MEASUREMENTS:
cursor = OpenScale.getInstance().getScaleMeasurementListCursor(Integer.valueOf(uri.getPathSegments().get(1)));
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
cursor.setNotificationUri(context.getContentResolver(), uri);
return cursor;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@@ -190,6 +190,8 @@
<string name="permission_not_granted">Permission not granted</string>
<string name="permission_bluetooth_info">Coarse location permission needed to search for Bluetooth devices. It can be revoked after the device is found.</string>
<string name="permission_bluetooth_info_title">Info</string>
<string name="permission_read_data_description">read openScale data, including user information and all saved measurements</string>
<string name="permission_read_data_label">Read openScale Data</string>
<string name="label_next">Next</string>
<string name="label_long_press_drag_reorder">Long press and drag measurement to reorder</string>
<string name="label_click_measurement_configure">Click measurement to configure</string>