Skip to content

Commit 2121083

Browse files
authored
Merge pull request #7 from GeoTecINIT/integrate-permissions-request
Integrate two-step permissions request workflow
2 parents 97b0a7c + d2578df commit 2121083

File tree

5 files changed

+179
-156
lines changed

5 files changed

+179
-156
lines changed

README.md

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ The library has the following requirements:
4949
### Tested WearOS versions and devices
5050
The library has been tested in the following WearOS versions:
5151

52-
- **Wear OS 2.33** (Android 9 - API 28):
52+
- **WearOS 2.33** (Android 9 - API 28):
5353
- TicWatch Pro 3 GPS
54-
- **Wear OS 3.5** (Android 11 - API 30):
54+
- **WearOS 3.5** (Android 11 - API 30):
5555
- TicWatch Pro 3 GPS
5656
- TicWatch Pro 5
5757
- **WearOS 4** (Android 13 - API 33):
@@ -118,50 +118,45 @@ Before going deeper with the data collection, we have to talk about permissions.
118118
#### Permissions
119119
In order to access to the data of the heart rate and the GPS sensors, the user
120120
has to grant some permissions. The library handles this situation (i.e., when permissions are required), and
121-
launches a notification to warn the user that some permissions need to be granted. However, we **do not**
122-
provide a mechanism to ask the permissions, we delegate that task on the developer. Why? To customise the
123-
way the permissions are required. In other words, the developer has to provide a class reference of an
124-
activity for requesting permissions using the [`PermissionsManager.setPermissionsActivity()`](#permissionsmanager). There are two
125-
ways of requesting permissions:
126-
127-
- If the data collection is started using the paired smartphone, the permissions will be automatically requested. You have to do nothing!
128-
- If the data collection is started from the smartphone, you should check that the necessary permissions are granted using the [`PermissionsManager.launchPermissionsRequestIfNeeded()`](#permissionsmanager)
129-
130-
and the library
131-
will start that activity when any permission is required and the user taps into the notification warning.
132-
133-
We have tried to make it easy for you to implement that activity:
134-
- You can obtain the list of the permissions that are required and also to request them using the [`PermissionsManager`](#permissionsmanager) API.
135-
- After the user grants or denies a permission, you have to tell the smartphone that which permissions
136-
have been granted and which ones not using the [`PermissionsResultClient`](#permissionsresultclient).
137-
Remember: the smartphone is the one that starts the data collection, including the request for permissions.
121+
launches a notification to warn the user that some permissions need to be granted. We can differentiate
122+
two main steps in the process:
123+
124+
1. Check if permissions are required:
125+
- If the data collection is started using the paired smartphone, the check is automatic. You have to do nothing!
126+
- If the data collection is started using the smartphone, you need to do the check using the [`PermissionsManager.launchPermissionsRequestIfNeeded()`](#permissionsmanager) method.
127+
2. Request the required permissions: the library handles mostly of the process by you, but you still have to do execute some steps:
128+
- Create an Activity: it will be used by the library to request the permissions. This will allow you to define a UI where some information can be given prior to the permissions request.
129+
- In your activity:
130+
- Create an instance of the [`PermissionsRequestHandler`](#permissionsrequesthandler) class.
131+
- Provide a callback using the [`PermissionsRequestHandler.onPermissionsResult()`]() method. The callback will be called with the result of the request, which can be used to update the UI. After 1 second, the activity will close.
132+
- Override the `Activity.onRequestPermissionsResult` and inside it, call [`PermissionsRequestHandler.handleResult()`](#permissionsrequesthandler).
133+
- Call the method [`PermissionsRequestHandler.handleRequest()`](#permissionsrequesthandler) to start the permission request.
134+
- Inject the Activity to the library using the [`PermissionsManager.setPermissionsActivity()`](#permissionsmanager) method.
138135

136+
139137
Here you can see an example of an activity for requesting permissions:
140138
```java
141139
public class YourRequestPermissionsActivity extends FragmentActivity {
142-
protected void onCreate(Bundle savedInstanceState) {
143-
// ...
144140

145-
// Get permissions to request
146-
ArrayList<String> permissionsToRequest = PermissionsManager.permissionsToRequestFromIntent(getIntent());
147-
141+
private PermissionsRequestHandler handler;
142+
143+
protected void onCreate(Bundle savedInstanceState) {
148144
// You can show a message explaining why you are going to request permissions
149145
// and then...
150-
PermissionsManager.requestPermissions(this, permissions);
146+
147+
handler = new PermissionsRequestHandler(this);
148+
handler.onPermissionsResult(this::updateUI);
149+
handler.handleRequest();
151150
}
152151

153152
@Override
154153
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
155-
// Check which permissions are granted and which ones rejected
156-
157-
// Tell the smartphone if ...
158-
PermissionsResultClient permissionsResultClient = new PermissionsResultClient(this);
159-
160-
// all permissions are granted ...
161-
permissionsResultClient.sendPermissionsSuccessfulResponse(getIntent());
162-
163-
// or if any permission was rejected (include in your failure message which permission were rejected)
164-
permissionsResultClient.sendPermissionsFailureResponse(getIntent(), failureMessage);
154+
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
155+
handler.handleResult(requestCode, permissions, grantResults);
156+
}
157+
158+
private void updateUI(boolean success) {
159+
// Update your UI...
165160
}
166161
}
167162
```
@@ -175,7 +170,10 @@ public class MainActivity extends Activity {
175170
protected void onCreate(Bundle savedInstanceState) {
176171
// ...
177172

173+
// Inject the permissions Activity
178174
PermissionsManager.setPermissionsActivity(this, YourRequestPermissionsActivity.class);
175+
176+
// Request Android 13+ required permissions
179177
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
180178
PermissionsManager.launchRequiredPermissionsRequest(this);
181179
}
@@ -301,21 +299,15 @@ Refer to the [_Background Sensors_](https://github.com/GeoTecINIT/BackgroundSens
301299
### [`PermissionsManager`](wearossensors/src/main/java/es/uji/geotec/wearossensors/permissions/PermissionsManager.java)
302300
| **Static Method** | **Return type** | **Description** |
303301
|--------------------------------------------------------------------------------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
304-
| `permissionsToRequestFromIntent(Intent intent)` | `ArrayList<String>` | Permissions to request in the custom request permissions activity. |
305-
| `specialPermissionsToRequestFromIntent(Intent intent)` | `ArrayList<String>` | Special permissions to request in the custom request permissions activity. |
306-
| `launchRequiredPermissionsRequest(Activity activity)` | `void` | Launch the permissions activity to request the required permission to post notifications (only WearOS 4+). |
307-
| `launchPermissionsRequestIfNeeded(Activity activity, ArrayList<String> permissions)` | `boolean` | Launch the permissions activity to request the specified permissions. Call this method to request the permissions for a specified sensor. Returns `true` if the permissions activity has been launched. |
308-
| `permissionsToBeRequested(Context context, ArrayList<String> required)` | `ArrayList<String>` | Returns the permissions that need to be requested (internal use only). |
309-
| `requestPermissions(Activity activity, ArrayList<String> permissions)` | `void` | Request the specified permissions previously obtained from `permissionsToRequestFromIntent`. You should call this method in your custom request permissions activity. |
310302
| `setPermissionsActivity(Context context, Class<?> permissionsActivity)` | `void` | Sets up the class that will be used for requesting permissions. You should call this method in your MainActivity class once your app has started. |
311-
| `getPermissionsActivity(Context context)` | `Class<?>` | Gets the class that will be used for requesting permissions. |
312303

313304

314-
### [`PermissionsResultClient`](wearossensors/src/main/java/es/uji/geotec/wearossensors/permissions/PermissionsResultClient.java)
315-
| **Method** | **Return type** | **Description** |
316-
|-----------------------------------------------------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
317-
| `sendPermissionsSuccessfulResponse(Intent intent)` | `void` | Indicates to the smartphone that all requested permissions have been granted |
318-
| `sendPermissionsFailureResponse(Intent intent, String failureReason)` | `void` | Indicates to the smartphone that some requested permissions have not been granted. It includes a message to indicate which permission/s was/were not granted (`failureReason`). |
305+
### [`PermissionsRequestHandler`](wearossensors/src/main/java/es/uji/geotec/wearossensors/permissions/handler/PermissionsRequestHandler.java)
306+
| **Method** | **Return type** | **Description** |
307+
|---------------------------------------------------------------------------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------|
308+
| `handleRequest()` | `void` | Starts the workflow to request permissions. Call inside your permissions activity. |
309+
| `handleResult(int requestCode, String[] permissions, int[] grantResults)` | `void` | Call inside the overrided `onRequestPermissionsResult` of your permissions activity. |
310+
| `onPermissionsResult(PermissionsResult permissionsResult)` | `void` | Inject a `PermissionsResult` callback. The callback will be called with `True` if all required permissions were granted, `False` otherwise. |
319311

320312
### [`CommandClient`](wearossensors/src/main/java/es/uji/geotec/wearossensors/command/CommandClient.java)
321313
| **Method** | **Return type** | **Description** |
Lines changed: 6 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package es.uji.geotec.wearossensorsdemo;
22

3-
import android.content.pm.PackageManager;
43
import android.os.Bundle;
54
import android.view.View;
65
import android.widget.ImageView;
@@ -10,23 +9,15 @@
109
import androidx.annotation.NonNull;
1110
import androidx.fragment.app.FragmentActivity;
1211

13-
import java.util.ArrayList;
14-
import java.util.Timer;
15-
import java.util.TimerTask;
16-
17-
import es.uji.geotec.wearossensors.intent.IntentManager;
18-
import es.uji.geotec.wearossensors.permissions.PermissionsManager;
19-
import es.uji.geotec.wearossensors.permissions.PermissionsResultClient;
12+
import es.uji.geotec.wearossensors.permissions.handler.PermissionsRequestHandler;
2013

2114
public class RequestPermissionsActivity extends FragmentActivity {
2215

2316
TextView descriptionText;
2417
ProgressBar progressBar;
2518
ImageView checkIcon, failIcon;
2619

27-
ArrayList<String> permissions;
28-
ArrayList<String> specialPermissions;
29-
boolean specialPermissionsRequested = false;
20+
private PermissionsRequestHandler handler;
3021

3122
@Override
3223
protected void onCreate(Bundle savedInstanceState) {
@@ -38,97 +29,15 @@ protected void onCreate(Bundle savedInstanceState) {
3829
checkIcon = findViewById(R.id.checkIcon);
3930
failIcon = findViewById(R.id.failIcon);
4031

41-
Timer delayer = new Timer();
42-
delayer.schedule(new TimerTask() {
43-
@Override
44-
public void run() {
45-
permissions = PermissionsManager.permissionsToRequestFromIntent(getIntent());
46-
specialPermissions = PermissionsManager.specialPermissionsToRequestFromIntent(getIntent());
47-
48-
if (permissions.size() != 0) {
49-
requestPermissions(permissions);
50-
} else if (specialPermissions.size() != 0) {
51-
specialPermissionsRequested = true;
52-
requestPermissions(specialPermissions);
53-
} else {
54-
finishDeferred();
55-
}
56-
}
57-
}, 500);
32+
handler = new PermissionsRequestHandler(this);
33+
handler.onPermissionsResult(this::updateUI);
34+
handler.handleRequest();
5835
}
5936

60-
6137
@Override
6238
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
6339
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
64-
65-
if (requestCode != PermissionsManager.PERMISSIONS_RC) {
66-
return;
67-
}
68-
69-
ArrayList<String> permissionsRejected = new ArrayList<>();
70-
for (int i = 0; i < grantResults.length; i++) {
71-
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
72-
permissionsRejected.add(permissions[i]);
73-
}
74-
}
75-
76-
if (permissionsRejected.size() != 0) {
77-
requestFailed(permissionsRejected);
78-
return;
79-
}
80-
81-
if (!specialPermissionsRequested && specialPermissions.size() != 0) {
82-
Timer delayer = new Timer();
83-
delayer.schedule(new TimerTask() {
84-
@Override
85-
public void run() {
86-
requestPermissions(specialPermissions);
87-
specialPermissionsRequested = true;
88-
}
89-
}, 1000);
90-
return;
91-
}
92-
93-
requestSucceeded();
94-
}
95-
96-
private void requestPermissions(ArrayList<String> permissions) {
97-
PermissionsManager.requestPermissions(this, permissions);
98-
}
99-
100-
private void requestSucceeded() {
101-
PermissionsResultClient permissionsResultClient = new PermissionsResultClient(this);
102-
boolean remoteRequest = IntentManager.isRemoteRequest(getIntent());
103-
if (remoteRequest) {
104-
permissionsResultClient.sendPermissionsSuccessfulResponse(getIntent());
105-
}
106-
107-
updateUI(true);
108-
}
109-
110-
private void requestFailed(ArrayList<String> permissionsRejected) {
111-
PermissionsResultClient permissionsResultClient = new PermissionsResultClient(this);
112-
boolean remoteRequest = IntentManager.isRemoteRequest(getIntent());
113-
if (remoteRequest) {
114-
String failureMessage = buildFailureMessage(permissionsRejected);
115-
permissionsResultClient.sendPermissionsFailureResponse(getIntent(), failureMessage);
116-
}
117-
118-
updateUI(false);
119-
}
120-
121-
private String buildFailureMessage(ArrayList<String> failures) {
122-
StringBuilder sb = new StringBuilder();
123-
sb.append("Permissions rejected: ");
124-
String separator = " ";
125-
for (String failure : failures) {
126-
sb.append(separator);
127-
separator = ", ";
128-
sb.append(failure);
129-
}
130-
131-
return sb.toString();
40+
handler.handleResult(requestCode, permissions, grantResults);
13241
}
13342

13443
private void updateUI(boolean success) {
@@ -140,16 +49,5 @@ private void updateUI(boolean success) {
14049
descriptionText.setText("Permissions denied :(");
14150
failIcon.setVisibility(View.VISIBLE);
14251
}
143-
finishDeferred();
144-
}
145-
146-
private void finishDeferred() {
147-
Timer delayer = new Timer();
148-
delayer.schedule(new TimerTask() {
149-
@Override
150-
public void run() {
151-
finish();
152-
}
153-
}, 1000);
15452
}
15553
}

app/src/main/res/layout/activity_main.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
android:background="@color/dark_grey"
77
tools:context=".MainActivity"
88
tools:deviceIds="wear">
9-
9+
<requestFocus/>
1010
<LinearLayout
1111
android:id="@+id/linear_layout"
1212
android:layout_width="match_parent"

0 commit comments

Comments
 (0)