Skip to content

Commit

Permalink
feat(android): implement foregroundServiceType parameter (#11197)
Browse files Browse the repository at this point in the history
* docs(android): include foregroundServiceType parameter and constants
* docs(android): include foreground service example
* fix(android): whitelist foregroundServiceType attribute

Fixes TIMOB-26953
  • Loading branch information
garymathews authored and sgtcoolguy committed Oct 2, 2019
1 parent 3a46b79 commit 9ca5864
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 3 deletions.
2 changes: 1 addition & 1 deletion android/cli/lib/AndroidManifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const appc = require('node-appc'),
'path-permission': /^(path|pathPrefix|pathPattern|permission|readPermissions|writePermissions)$/,
provider: /^(authorities|directBootAware|enabled|exported|grantUriPermissions|icon|initOrder|label|multiprocess|name|permission|process|readPermission|syncable|writePermission)$/,
receiver: /^(directBootAware|enabled|exported|icon|label|name|permission|process)$/,
service: /^(description|directBootAware|enabled|exported|icon|isolatedProcess|label|name|permission|process)$/,
service: /^(description|directBootAware|enabled|exported|icon|isolatedProcess|label|name|permission|process|foregroundServiceType)$/,
'uses-library': /^(name|required)$/,
'uses-sdk': /^(maxSdkVersion|minSdkVersion|targetSdkVersion)$/
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.List;

import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.service.quicksettings.Tile;
import org.appcelerator.kroll.KrollDict;
Expand Down Expand Up @@ -522,6 +523,25 @@ public class AndroidModule extends KrollModule
@Kroll.constant
public static final int IMPORTANCE_UNSPECIFIED = NotificationManagerCompat.IMPORTANCE_UNSPECIFIED;

@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_NONE = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_PHONE_CALL = ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_LOCATION = ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE =
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE;
@Kroll.constant
public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION =
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION;

protected RProxy r;
private LinkedList<BroadcastReceiverProxy> registeredBroadcastReceiverProxyList = new LinkedList<>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class ServiceProxy extends KrollProxy
private IntentProxy intentProxy;
private int notificationId;
private KrollProxy notificationProxy;
private int foregroundServiceType;

// Set only if the service is started via bindService as opposed to startService
private ServiceConnection serviceConnection = null;
Expand Down Expand Up @@ -130,7 +131,8 @@ public void stop()
}

@Kroll.method
public void foregroundNotify(int notificationId, KrollProxy notificationProxy)
public void foregroundNotify(int notificationId, KrollProxy notificationProxy,
@Kroll.argument(optional = true) int foregroundServiceType)
{
// Validate arguments.
if (notificationId == 0) {
Expand All @@ -145,6 +147,7 @@ public void foregroundNotify(int notificationId, KrollProxy notificationProxy)
{
this.notificationId = notificationId;
this.notificationProxy = notificationProxy;
this.foregroundServiceType = foregroundServiceType;
}
updateForegroundState();
}
Expand Down Expand Up @@ -268,6 +271,7 @@ public void run()
// Fetch the proxy's notification and ID.
int notificationId = 0;
Notification notificationObject = null;
int foregroundServiceType = 0;
try {
// Fetch notification settings from proxy.
synchronized (ServiceProxy.this)
Expand All @@ -282,6 +286,7 @@ public void run()
Method method = proxyClass.getMethod("buildNotification");
notificationObject = (Notification) method.invoke(object);
}
foregroundServiceType = ServiceProxy.this.foregroundServiceType;
}
}

Expand Down Expand Up @@ -357,7 +362,11 @@ public void run()
// Note: A notification will be shown in the status bar while enabled.
try {
if (isForeground) {
service.startForeground(notificationId, notificationObject);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
service.startForeground(notificationId, notificationObject, foregroundServiceType);
} else {
service.startForeground(notificationId, notificationObject);
}
} else {
service.stopForeground(true);
}
Expand Down
63 changes: 63 additions & 0 deletions apidoc/Titanium/Android/Android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,69 @@ properties:
type: Number
permission: read-only

- name: FOREGROUND_SERVICE_TYPE_MANIFEST
summary: |
A special value indicates to use all types set in manifest file.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_MANIFEST).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_NONE
summary: |
The default foreground service type if not been set in manifest file.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_NONE).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
summary: |
Constant corresponding to mediaPlayback in the R.attr.foregroundServiceType attribute. Music, video, news or other media playback.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_PHONE_CALL
summary: |
Constant corresponding to phoneCall in the R.attr.foregroundServiceType attribute. Ongoing phone call or video conference.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_PHONE_CALL).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_LOCATION
summary: |
Constant corresponding to location in the R.attr.foregroundServiceType attribute. GPS, map, navigation location update.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_LOCATION).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
summary: |
Constant corresponding to connectedDevice in the R.attr.foregroundServiceType attribute. Auto, bluetooth, TV or other devices connection, monitoring and interaction.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE).
type: Number
permission: read-only
since: "8.3.0"

- name: FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
summary: |
Constant corresponding to mediaProjection in the R.attr.foregroundServiceType attribute. Managing a media projection session, e.g for screen recording or taking screenshots.
description: |
See [ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION in the Android API Reference](https://developer.android.com/reference/android/content/pm/ServiceInfo.html#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION).
type: Number
permission: read-only
since: "8.3.0"

- name: FLAG_AUTO_CANCEL
summary: Cancel the notification when it is clicked by the user.
description: |
Expand Down
148 changes: 148 additions & 0 deletions apidoc/Titanium/Android/Service.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,148 @@ description: |
* [Android Services guide](https://docs.appcelerator.com/platform/latest/#!/guide/Android_Services)
* [Android Developer: Service](https://developer.android.com/reference/android/app/Service.html)
### Background Location Service Example
In order to obtain location events while the application is backgrounded a foreground service must be used.
Below is an example of a simple background location service.
tiapp.xml:
<android>
<manifest>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
</manifest>
<services>
<service url="locationService.js"/>
</services>
</android>
locationService.js:
console.log(`Background location service started.`);
// Create foreground service.
Ti.Android.currentService.foregroundNotify(
123,
Ti.Android.createNotification({
contentTitle: "Background Location Service",
contentText: "Obtaining location data...",
contentIntent: Ti.Android.createPendingIntent({
intent: Ti.App.Android.launchIntent || Ti.Android.createIntent(),
})
}),
Ti.Android.FOREGROUND_SERVICE_TYPE_LOCATION
);
app.js:
const win = Ti.UI.createWindow({ backgroundColor: 'gray', layout: 'vertical' });
const backgroundServiceBtn = Ti.UI.createButton({ title: 'START BACKGROUND SERVICE' });
const listView = Ti.UI.createListView();
let count = 1;
let service = null;
// Set accuracy to high
Ti.Geolocation.accuracy = Ti.Geolocation.ACCURACY_HIGH;
// Enable manual configuration via location providers.
Ti.Geolocation.Android.manualMode = true;
// Define a location provider.
Ti.Geolocation.Android.addLocationProvider(
Ti.Geolocation.Android.createLocationProvider({
name: Ti.Geolocation.Android.PROVIDER_GPS,
minUpdateDistance: 0,
minUpdateTime: 5000
})
);
function getLocation () {
// Create location event listener.
Ti.Geolocation.addEventListener('location', e => {
// Create new section for location data.
let section = Ti.UI.createListSection({ headerTitle: `#${count++} - ${new Date().toTimeString()}` });
if (e.success) {
if (e.coords) {
e = e.coords;
}
// Insert location data.
section.setItems([
{ properties: { title: 'LOCATION:\n' + e.latitude + ', ' + e.longitude, color: 'green' } },
{ properties: { title: 'ALTITUDE:\n' + e.altitude, color: 'green' } },
{ properties: { title: 'ACCURACY:\n' + e.accuracy, color: 'green' } }
]);
} else {
// Oops! Something bad happened.
section.setItems([
{ properties: { title: 'ERROR:\n' + e.error, color: 'red' } }
]);
}
// Add section to listView
listView.appendSection(section);
});
}
function startBackgroundLocationService() {
if (service) {
console.log('Starting background location service...');
service.start();
backgroundServiceBtn.title = 'STOP BACKGROUND SERVICE';
}
}
function stopBackgroundLocationService() {
if (service) {
console.log('Stopping background location service...');
service.stop();
backgroundServiceBtn.title = 'START BACKGROUND SERVICE';
}
}
win.addEventListener('open', () => {
// Request location permissions.
Ti.Geolocation.requestLocationPermissions(Ti.Geolocation.AUTHORIZATION_ALWAYS, e => {
if (e.success) {
getLocation();
} else {
alert('Could not obtain location permissions.');
}
});
});
backgroundServiceBtn.addEventListener('click', () => {
if (!service) {
// Create background location service.
const intent = Ti.Android.createServiceIntent({ url: 'locationService.js' });
service = Ti.Android.createService(intent);
// Android 10+ request background location permissions.
if (parseInt(Ti.Platform.version.split('.')[0]) >= 10) {
Ti.Android.requestPermissions([ 'android.permission.ACCESS_BACKGROUND_LOCATION' ], e => {
if (e.success) {
startBackgroundLocationService();
} else {
alert('Could not obtain background location permissions.');
}
});
} else {
startBackgroundLocationService();
}
} else {
stopBackgroundLocationService();
}
});
win.add([backgroundServiceBtn, listView]);
win.open();
extends: Titanium.Proxy
platforms: [android]
Expand Down Expand Up @@ -100,6 +242,12 @@ methods:
summary: Notification to display in the status bar. Cannot be null.
type: Titanium.Android.Notification

- name: foregroundServiceType
summary: Notification service type specified by <Titanium.Android.FOREGROUND_SERVICE_TYPE_*>.
type: Number
default: Titanium.Android.FOREGROUND_SERVICE_TYPE_NONE
optional: true

- name: start
summary: Starts the Service.
description: |
Expand Down

0 comments on commit 9ca5864

Please sign in to comment.