I have an Android App with working Auto capabilities which has been rejected from the store because:
"Your app does not support all of the required voice commands.
For example, your app does not contain an Intent filter for action "android.media.action.MEDIA_PLAY_FROM_SEARCH"."
So i looked to at least make the app respond to "play music on X app" but by looking at the documentation I can't get it to work. I added this to my main activity (which is not the launcher activity, it controls the service via binder)
<intent-filter>
<action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
which triggers a MediaBrowserServiceCompatwhich is initialized this way:
manifest:
<service
android:name=".Services.RadioService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
part of the code:
#Override
public void onCreate() {
super.onCreate();
audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mSession = new MediaSessionCompat(this, "RService");
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
| MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);
mSession.setCallback(mCallback);
mSessionConnector = new MediaSessionConnector(mSession);
setSessionToken(mSession.getSessionToken());
setMediaBrowser();
initializeReceiver();
C_F_App.createNotificationChannelPlayer(getApplicationContext());
rNController = new RadioNotificationController(this, mSession, getApplicationContext());
}
final MediaSessionCompat.Callback mCallback =
new MediaSessionCompat.Callback() {
#Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
playForAuto(mediaId);
}
#Override
public void onPlayFromSearch(String query, Bundle extras) {
super.onPlayFromSearch(query, extras);
Log.d("Test", "Test");
}
#Override
public void onStop() {
playPause();
}
#Override
public void onPlay() {
playPause();
}
#Override
public void onPause() {
playPause();
}
};
public void setMediaBrowser() {
mediaBrowser = new MediaBrowserCompat(getApplicationContext(),
new ComponentName(getApplicationContext(), this.getClass()),
new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
super.onConnected();
}
#Override
public void onConnectionSuspended() {
super.onConnectionSuspended();
}
#Override
public void onConnectionFailed() {
super.onConnectionFailed();
}
}, null);
mediaBrowser.connect();
}
#Override
public long getSupportedPrepareActions() {
return PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID |
PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID |
PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH |
PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH;
}
Auto capabilities work fine.
When i try to call this app on Auto from the emulator to play music it just does't do anything, it does not say errors or other stuff, it just closes google speak and then returns to the normal display. The method onPlayFromSearch is never called and it should be the one Auto would call if a voice command is sent.
Can someone help me figure out what i'm doing wrong? Google doc is pretty useless this way, Google UAP doesen't look to have this capability like the old version but looking at the old UAP version i cannot figure out what I'm doing wrong.
PS. The app is not published yet with this functionality
Thanks in advance.
To clear the review process, you’ll only need to get “Play X” (while the app is running in the foreground) working. After adding the intent filter, and if the app handles the search query while its running, I’d kick off another app review.
Otherwise, I believe it’s more of a Google Assistant feature that handles “Play X on Y” when the app is not in the foreground. I don’t have all the details on this, but the assistant may be querying their servers for the app name and package. If it isn’t published yet, it may not work locally.
EDIT
The best way to test your intent filter to clear the app review is to call the intent from adb on the mobile device with:
adb shell am start -a android.media.action.MEDIA_PLAY_FROM_SEARCH
If your app appears in the following media list drawer, the intent filter is setup correctly. You should then see the onPlayFromSearch "Test" log fire when "Play music" is sent while the app is running on Android Auto via the Desktop Head Unit.
Related
I have read here how can I listen to hardware when Activity is in Running state:
#Override
public boolean onKeyDown(int keycode, KeyEvent e) {
switch(keycode) {
case KeyEvent.KEYCODE_MENU:
doSomething();
return true;
}
return super.onKeyDown(keycode, e);
}
I want to use "trigger" button to open/bring forward my app (my MainActivity), if it is in the background (activity stopped) or not running at all (not launched yet or shut down).
Questions:
Can I listen to the hardware keyboard from Android Service and open/bring forward Activity?
How can I do that?
My application is a Barcode/RFID tool that will run on devices like this:
Industrial environment where it will work needs something like "run app immediately and scan" functionality, no matter what is on the screen. Ideally it would be awseome if it could wake up whole device to make warehouse work easier.
This is why I need this.
So at this moment (september 2022) Android Development Guide says, that there are restrictions on starting activities from the background. There are some exceptions.
In my case (inventory management app with dedicated barcode/RFID reader hardware) I should make use of exception for dedicated devices or use some Device Policy Controller:
The app is a device policy controller running in device owner mode.
Example use cases include fully managed enterprise devices, as well as dedicated devices like digital signage and kiosks.
https://developer.android.com/work/dpc/dedicated-devices
However, for development purposes I have tried the AccessibilityService, as suggested here: link
I have added startActivity when the key 293 is pressed (trigger key on device). In comparison to answer linked above - I have choosen diffrent name for service class. Rename has to be done also in XML file.
src/AccessibilityKeyDetectorService.java
public class AccessibilityKeyDetectorService extends AccessibilityService {
private final String TAG = "AccessKeyDetector";
#Override
public boolean onKeyEvent(KeyEvent event) {
Log.d(TAG,"Key pressed via accessibility is: "+event.getKeyCode());
if (event.getKeyCode() == 293)
{
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // this is required to open Activity from service
startActivity(intent);
}
return super.onKeyEvent(event);
}
#Override
protected void onServiceConnected() {
Log.i(TAG,"Service connected");
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
#Override
public void onInterrupt() {
}
}
My res/xml/accessibility_service.xml:
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFlags="flagRequestFilterKeyEvents"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackAllMask"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:settingsActivity=""
android:packageNames="pl.globalsystem.rflow"
android:canRequestFilterKeyEvents="true" />
And my manifest entry:
<service android:name=".services.AccessibilityKeyDetectorService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service" />
</service>
And my MainActivity method for accessibility permissions:
public boolean checkAccessibilityPermission() {
int accessEnabled=0;
try {
accessEnabled = Settings.Secure.getInt(this.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
if (accessEnabled==0) {
/** if not construct intent to request permission */
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/** request permission via start activity for result */
startActivity(intent);
return false;
} else {
return true;
}
}
I have developed small app to detect changes in network like on, off or connection change Wifi to Ethernet, whenever app closed or running in all cases.
Provided code working for me upto Nuget 7, when testing app in Oreo 8 background services not working when app terminated.
How can I get it work in Oreo?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Intent vpnServiceIntent = new Intent(getBaseContext(), MyService.class);
startForegroundService(vpnServiceIntent);}}
WifiReceiver.java file
public class WifiReceiver extends BroadcastReceiver {
static final String CONNECTIVITY_CHANGE_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (CONNECTIVITY_CHANGE_ACTION.equals(action)) {
if (!isConnected()) {
if (context != null) {
Toast.makeText(context," Not connected...",Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(context,"connected...",Toast.LENGTH_LONG).show();
}
}
}
}
MySevice.java file
public class MyService extends Service
{
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
IntentFilter filter = new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
WifiReceiver receiver =new WifiReceiver();
registerReceiver(receiver,filter);
return START_STICKY;
}
}
Manifest.xml file
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
<receiver android:name=".WifiReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
<service android:name=".MyService" />
</application>
Note: If I call startForeground(101, notification); in onCreate of MyService class, my above code is working but showing permanent notification icon on top most status bar, that I don't want at all.
since Android 8.0 (API level 26) it is basically impossible to run background service while app is not visible because of Battery optimizations and security reasons.
It sucks, many useful apps can not run and work normally.
They recommend to use ForegroundService which requires to show notification.
It would be almost okay, but these ForegroundServices also gets killed after some time.
To avoid killing them you need to make BatteryOptimization prompt so user would let service running in background without killing.
But it is not over yet... Services is still being killed on most of Manufactures like Samsung, Huawei and so on because they has they own badly implemented BatteryOptimizations running parallel with native one... and if user want some app avoid to be killed while running in background it has to go long way to settings find provider specific settings and let app run....
here is an example how to change these provider specific settings on Slack
I think it is worst thing that happened to Android.....
I'd like to know if there is a way maybe with firebase or appsFLyer etc or natively to know if user came from a deeplink and installed my application is there a way to know the deeplink ?
Basically i'd like an intent passed to me after the user installs the app from the play store (assuming the user got to the play store from a deepLink or some referer link).
Lets imagine the user did not have my automobile app but saw an ad about cars that i had put up. he then clicks that ad and is directed to install my app. after installing my app i'd like the user to see the car they were looking at initially in the ad.
Good example was described in that post
You have to create specific BroadcastReceiver
Add in Manifest:
<receiver android:name="your.package.InstallListener" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
and create BroadcastReceiver class which will catch deeplink
public class InstallListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String rawReferrerString = intent.getStringExtra("referrer");
if (rawReferrerString != null) {
Log.i("MyApp", "Received the following intent " + rawReferrerString);
}
}
}
And add referrer parameter to url using js like this:
var fallbackFunction = function() {
window.location.replace('market://details?id=io.branch.testbed&referrer=specialparam');
};
var addIFrame = function() {
var iframe = document.createElement("iframe");
iframe.style.border = "none";
iframe.style.width = "1px";
iframe.style.height = "1px";
iframe.src = 'your_uri_scheme://';
document.body.appendChild(iframe);
};
addIFrame();
setTimeout(fallbackFunction, 250);
By the way if that method does not work you can try to get specific parameter of phone in js (like vendor, model, etc) and save it in cloud database like firebase. Then after user installed application (PACKAGE_INSTALLED BroadcastReceiver or just first launch) you can check database for last data for his specific parameter and get his deeplink.
If you dont have too much users that will work correctly enough.
I'm developing a simple Kiosk Mode application for Android 6.0. I have everything working on devices from Xiaomi, HTC, Lenovo, etc. but I can't get one feature working on any Samsung device.
The feature is automatic closing of every system system dialog using
Intent closeDialog = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(closeDialog);
This is broadcasted from a Service.
On my other, non-samsung devices, everything works and all system dialogs closes, but on any Samsung device (S5, S6 edge, ...) this broadcast gets ignored and for example the app drawer stays opened.
I've observed that even using ADB to broadcast this intent, app drawer stays opened, but for example the shutdown device dialog gets closed if I broadcast this from adb.
Please note that this is not malicious action in context of this software, this is for client, that requires this feature and it is completely solicited.
I've done research on Samsung Knox, but we would have to obtain their license for using the Knox Standard SDK and that is not something that is in the scope of this project.
So my question is: Do you have any idea how to make this work (closing the app drawer with ACTION_CLOSE_SYSTEM_DIALOGS intent) on samsung devices with Knox installed?
Thanks.
Try doing it like this :
#Override
public void onWindowFocusChanged(boolean focus) {
super.onWindowFocusChanged(focus);
if (! focus) {
Intent close= new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
sendBroadcast(close);
}
}
The problem with the most commonly found solution is that dialogs are totally different window, so you need to override the onWindowsFocusChanged on them as well. That can lead to a lot of work, plus the menu also open the same flaw to call the power menu.
The best solution I found is to implement a service to close the system dialogs when a long press of the power key is detected. I tested on couple of LG devices (Android 5.1.1 and 6.0) and calling this from a service was working just fine:
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
But on Samsung tablets (Android 5.1.1) it did not work, after trying few different solutions with no luck. I finally got it working when send an intent to an activity and make the activity send the broadcast:
Add this to your manifest:
<service
android:name="com.mypackage.services.PowerButtonService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.SYSTEM_ALERT_WINDOW"
android:stopWithTask="true"/>
<activity
android:name="com.mypackage.activities.CloseSystemDialogActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="CloseSystemDialogActivity.ACTION_CLOSE_DIALOGS"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
The service:
public class PowerButtonService extends Service {
private View view;
private WindowManager windowManager;
#Override
public void onCreate() {
super.onCreate();
LinearLayout linearLayout = new LinearLayout(getApplicationContext()) {
#SuppressWarnings("unused")
public void onCloseSystemDialogs(String reason) {
if ("globalactions".equals(reason)) {
final Intent intent = new Intent(CloseSystemDialogActivity.ACTION_CLOSE_DIALOGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
};
linearLayout.setFocusable(true);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(100, 100, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_FULLSCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.START | Gravity.CENTER_VERTICAL;
view = LayoutInflater.from(this).inflate(R.layout.empty, linearLayout);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
windowManager.addView(view, params);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
// VERY IMPORTANT
windowManager.removeView(view);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
return START_STICKY;
}
}
Dummy activity:
public class CloseSystemDialogActivity extends Activity {
public static final String ACTION_CLOSE_DIALOGS = "CloseSystemDialogActivity.ACTION_CLOSE_DIALOGS";
#Override
protected void onCreate(#Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
finish();
}
}
Notice that you NEED to START and STOP the service otherwise when your application is not running (out of kiosk mode) you might not be able to call the power menu if you need to.
I have implemented a NotificationListenerService, yet it is not working. Here is the service:
public class NotificationListener extends NotificationListenerService {
#Override
public void onCreate() {
Log.d("MYAPP", "Created");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.d("MYAPP", "Notification");
}
}
I have implemented this in my manifest file:
<service android:name="com.rodrigopontes.whatsappbubbles.NotificationListener"
android:label="Test"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
This is how I initialize it from my MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
startService(new Intent(this, NotificationListener.class));
}
Thank you for the help!
After many wasted hours I found the answer for this. This is an issue with Android see here
Welcome to Android :)
I had the same problem. It turned out my app was not allowed to receive notification as it did not have permission. So I enabled the notification permission by following-
Intent intent=new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivity(intent);
Above code opens a notification permission page then select your app and allow.
Maybe you haven't provided the Notification Access to your app. Try doing that and it should work
In my case the NotificationListner was not working after reboot. I am working with android 10 i have find out solution on this.
#Override
public void onListenerConnected() {
super.onListenerConnected();
Log.i("tag","Listner conneted");
tryReconnectService();
}
public void tryReconnectService() {
toggleNotificationListenerService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ComponentName componentName =
new ComponentName(getApplicationContext(), Whatsapp_recorder.class);
//It say to Notification Manager RE-BIND your service to listen notifications again inmediatelly!
requestRebind(componentName);
}
}
private void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(this, Whatsapp_recorder.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, Whatsapp_recorder.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
Try this it is working for me
Some comments here mention other solutions, some of which led me to this:
https://gist.github.com/xinghui/b2ddd8cffe55c4b62f5d8846d5545bf9
The problem is, if I understand it correctly, that the service process is running, but is completely disconnected from, well, anything.
The gist is a service class which checks if your NotificationListenerService process is actually running and "connected" to whatever takes care of notifications inside the OS and if it isn't, restarts it (or rather enables the component, as starting it the usual way is exactly what's broken).
I've tried this solution on Android 7.1.1, where I was unable to use the NotificationListenerService, and it worked.