AccessibilityEvent.getPackageName() returns null - java

I am registering an AccessibilityService to listen for app changes. It works on all the phones I've tested but on the Galaxy A3 with Android 5.0 is failing because AccessibilityEvent.getPackageName() is returning null. The packageName should be set, as it is a regular app, downloaded from Google play, being interacted with.
Does anyone know why is this and how to fix it?
Below the relevant parts of my code.
<service
android:name=".presentation.view.services.LockService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service_config"/>
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
</service>
#Override
public void onServiceConnected() {
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
info.notificationTimeout = 100;
this.setServiceInfo(info);
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
event.getPackageName() // returns NULL;
}

As the documentation doesn't specifically explain this, I'll try to provide something that I've came up with after playing around with the AccessibilityService. I'm writing this just in case others are banging their heads because event.getPackageName() always returns null, but don't take my word for granted.
From the documentation of accessibility service, I see that you can set the attribute android:packageNames="#strings/blah_blah", where blah_blah is a comma separated list of package names that our service will observe.
In case we specify this attribute, we will get notified only for those specific packages and event.getPackageName() will return the proper package name.
If we leave this attribute unset or we specifically set this to null we will be notified of all events that occur, but we'll lose the ability to identify whichever specific package "generated" the accessibility event.
As this AccessibilityServiceInfo property can be dynamically set, you could in theory fetch a list of currently installed packages and set that as the packageNames that your service will monitor.
#Override
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = getServiceInfo();
// your other assignments
info.packageNames = new String[]{...};
setServiceInfo(info);
}

Related

Android Email intent not Working on Android 11 java [duplicate]

I've an extension function for opening an intent for my activities:
fun Activity.openIntent(action: String?, type: String?, uri: Uri?) {
Intent()
.apply {
action?.let { this.action = it }
uri?.let { this.data = it }
type?.let { this.type = it }
}
.also { intent ->
packageManager?.let {
if (intent.resolveActivity(it) != null)
startActivity(intent)
else
showToast(R.string.application_not_found)
}
}
}
My targetSdkVersion is 30. It gives me a warning in intent.resolveActivity(it):
Consider adding a queries declaration to your manifest when calling this method.
So What should I do to solve this warning?
The simplest solution is to get rid of resolveActivity(). Replace:
packageManager?.let {
if (intent.resolveActivity(it) != null)
startActivity(intent)
else
showToast(R.string.application_not_found)
}
with:
try {
startActivity(intent)
} catch (ex: ActivityNotFoundException) {
showToast(R.string.application_not_found)
}
This gives you the same result with a bit better performance, and it will get rid of the warning.
Another option would be to add the QUERY_ALL_PACKAGES permission. This may get you banned from the Play Store.
Otherwise, you will need to:
Build a list of every possible openIntent() call that your app may make
Add a <queries> element to your manifest that lists all of those possible Intent structures that you want to be able to use with resolveActivity()
Repeat this process periodically, in case you add new scenarios for openIntent()
So starting Android 11 (i.e, if your app targets Android 11) not all applications will be visible to your application. Some apps are visible by default but in order to access other applications through your application, you will have to declare queries in your manifest else your application will not be able to access them. You can read about that here.
So if your application targets Android 11 and is to access an application that may not be visible by default you will want to add queries for them in the manifest file.
In your case, this warning is not applicable as I believe you are using implicit intents to open other applications. Using implicit intents, other applications can be accessed irrespective of app visibility. If your app target Android 10 or lower you can suppress the warning as all apps are visible by default.
To suppress the lint warning you can either:
Add the suppress annotation, like so:
#SuppressLint("QueryPermissionsNeeded")
fun Activity.openIntent(action: String?, type: String?, uri: Uri?): Activity {
Add the following to your android block in your app module build gradle file:
lintOptions {
ignore "QueryPermissionsNeeded"
}
Replace
if (intent.resolveActivity(it) != null)
with
if (it.resolveActivity(intent, 0) != null)
and the warning will be gone.
From API level 30 package visibility is restricted. So add appropriate query in your AndroidManifest file outside <application> tag.
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
</queries>

How do I specify and add a custom printer in an Android app?

I'm creating an app for Android. Part of the desired app functionality is that the user can select a special printer (let's just call it Transfer Printer) which will pass on the document-to-be-printed to a process running on an external server.
What steps do I need to take to add a custom printer to the list of printers in the Android print panel, accessible from the Print option of the Overflow menu?
It is desirable to use the existing Android print panel functionality rather than, for example, an additional Share option in the App Selector because of user experience considerations; it won't be intuitive to the user to click Share rather than Print for the desired functionality.
Prior research
There is an existing similar question which has gathered little interest since it was posted some time ago. The asker has identified the PrintManager class as a lead but I believe that the PrintService class is likely to be more fruitful:
A print service is responsible for discovering printers, adding discovered printers, removing added printers, and updating added printers.
The same page details Declaration and Configuration of the print service. I've done so as below.
Attempted Execution
Declaration and Configuration
In AndroidManifest.xml:
...
<application
... >
...
<service
android:name=".TransferPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.printservice.PrintService" />
</intent-filter>
<meta-data
android:name="android.printservice"
android:resource="#xml/transfer_print_service" />
</service>
</application>
Meta-data
It's unclear to me exactly where the meta-data is supposed to be specified. From SERVICE_META_DATA section of the PrintService page:
This meta-data must reference a XML resource containing a print-service tag.
In res/xml/transfer_print_service.xml:
<print-service
android:label="TransferPrintService"
android:vendor="Company Ltd." />
TransferPrintService Class
This creates a custom PrinterDiscoverySession. My goal at this stage is to just get a printer appearing in the print panel and work from there.
public class TransferPrintService extends PrintService {
public TransferPrintService() {
}
#Override
public void onPrintJobQueued(PrintJob printJob) {
printJob.start();
printJob.complete();
}
#Override
public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
return new TransferPrinterDiscoverySession(this);
}
#Override
public void onRequestCancelPrintJob(PrintJob printJob) {
}
}
The service is started in a BroadcastReceiver on an ACTION_BOOT_COMPLETED intent.
TransferPrinterDiscoverySession Class
This actually creates the custom printer.
public class TransferPrinterDiscoverySession extends PrinterDiscoverySession {
private transferPrintService printService;
private static final String PRINTER = "Transfer Printer";
public transferPrinterDiscoverySession(TransferPrintService printService) {
this.printService = printService;
}
#Override
public void onStartPrinterDiscovery(List<PrinterId> printerList) {
PrinterId id = printService.generatePrinterId(PRINTER);
PrinterInfo.Builder builder =
new PrinterInfo.Builder(id, PRINTER, PrinterInfo.STATUS_IDLE);
PrinterInfo info = builder.build();
List<PrinterInfo> infos = new ArrayList<>();
infos.add(info);
addPrinters(infos);
}
#Override
public void onStopPrinterDiscovery() {
}
#Override
public void onValidatePrinters(List<PrinterId> printerIds) {
}
#Override
public void onStartPrinterStateTracking(PrinterId printerId) {
PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId,
PRINTER, PrinterInfo.STATUS_IDLE);
PrinterCapabilitiesInfo.Builder capBuilder =
new PrinterCapabilitiesInfo.Builder(printerId);
capBuilder.addMediaSize(PrintAttributes.MediaSize.ISO_A4, true);
capBuilder.addResolution(new PrintAttributes.Resolution(
"Default", "Default", 360, 360), true);
capBuilder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
+ PrintAttributes.COLOR_MODE_MONOCHROME,
PrintAttributes.COLOR_MODE_COLOR);
capBuilder.setMinMargins(PrintAttributes.Margins.NO_MARGINS);
PrinterCapabilitiesInfo caps = capBuilder.build();
builder.setCapabilities(caps);
PrinterInfo info = builder.build();
List<PrinterInfo> infos = new ArrayList<PrinterInfo>();
infos.add(info);
addPrinters(infos);
}
#Override
public void onStopPrinterStateTracking(PrinterId printerId) {
}
#Override
public void onDestroy() {
}
}
Major Concerns
This doesn't produce an additional printer option.
Is the arrangement of documents correct? Specifically, having the <print-service> tag in a separate XML document under res? Trying to place the tag anywhere in the AndroidManfiest.xml document produces IDE errors.
How do I call into the TransferPrintService? As an example, suppose I'm in Chrome, I open the Overflow menu, and select Print... Which PrintService is invoked? How do I make sure it's mine?
Am I on completely the wrong track here?
The trick I was missing was actually enabling the Print Service via the Android Settings menu. Actually doing this wasn't as straightforward as I would have hoped as the device manufacturer had removed the setting from the menu. It should be right under Accessibility in the System section of the menu.
I ended up installing the Cloud Print app by Google, which gave me access to the Print Service settings temporarily (to enable the Cloud Print service). Once in here I noticed that my own service was, in fact, present.
For posterity: To avoid un-installing and re-installing Cloud Print every time you want to change the Print Service settings, use the following SQLite3 commands, either with adb shell or from Terminal Emulator (or similar):
sqlite3 data/data/com.android.providers.settings/databases/settings.db
You should now have access to the Settings database and be using the SQLite3 command line shell. The settings of interest are located in the secure table and are enabled_print_services and enabled_on_first_boot_system_print_services. You can check if these settings already exist by using:
.dump secure
If they don't, then use the following commands:
INSERT INTO secure VALUES(<id>, 'enabled_on_first_boot_system_print_services', 'com.companyname.appservice/com.companyname.appservice.TransferPrintService');
INSERT INTO secure VALUES(<id>, 'enabled_print_services', 'com.companyname.appservice/com.companyname.appservice.TransferPrintService');
You should, of course, replace 'com.companyname.appservice' with your own package and 'TransferPrintService' with your own print service. If these setting names do already exist, and your print service isn't listed, then you'll need to UPDATE instead of INSERT INTO:
UPDATE secure SET value = '<existing print services>:<new print service>' WHERE name = 'enabled_on_first_boot_system_print_services';
UPDATE secure SET value = '<existing print services>:<new print service>' WHERE name = 'enabled_print_services';
You'll need to make sure to include any existing print services as part of the UPDATE command; listed print services are separated by a colon ":".
Reboot the device to apply the updates to the settings database.

detector.isOperational() always false on android

I'm using the new google plays service: Barcode detector, for this porposue I'm following this tutorial : https://search-codelabs.appspot.com/codelabs/bar-codes
But when I run the application on my real device(Asus Nexus 7), the text view of the app always is showing me "Couldn't set up the detector" and i don't know how to make it work >< ...
Here some code for fast debugging:
public class DecoderBar extends Activity implements View.OnClickListener{
private TextView txt;
private ImageView img;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_decoder);
Button b = (Button) findViewById(R.id.button);
txt = (TextView) findViewById(R.id.txtContent);
img = (ImageView) findViewById(R.id.imgview);
b.setOnClickListener(this);
}
// [...]
#Override
public void onClick(View v) {
Bitmap myBitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(),R.drawable.popi);
img.setImageBitmap(myBitmap);
BarcodeDetector detector = new BarcodeDetector.Builder(getApplicationContext())
.setBarcodeFormats(Barcode.DATA_MATRIX | Barcode.QR_CODE)
.build();
if(!detector.isOperational()){
// Always show this message, so, never is operational!
txt.setText("Could not set up the detector!");
return;
}
Frame frame = new Frame.Builder().setBitmap(myBitmap).build();
SparseArray<Barcode> barcodes = detector.detect(frame);
Barcode thisCode = barcodes.valueAt(0);
txt.setText(thisCode.rawValue);
}
}
It looks like the first time barcode detector is used on each device, some download is done by Google Play Services. Here is the link:
https://developers.google.com/vision/multi-tracker-tutorial
And this is the excerpt:
The first time that an app using barcode and/or face APIs is installed
on a device, GMS will download a libraries to the device in order to
do barcode and face detection. Usually this is done by the installer
before the app is run for the first time.
I had this problem now. You can't update the Google Play Services. After I used the same as on the tutorial it works.
compile 'com.google.android.gms:play-services:7.8+'
Here is what was my case.
I was using BarcodeDetector for decoding QR codes from imported images. On 4 my testing devices it was working fine. On one was not reading anything from bitmap. I thought this might be incompatibility with android 5.0 but this was not the case. After hours of investigation I finally noticed that detector.isOperational(); returns false. The reason was:
The first time that an app using barcode and/or face APIs is installed on a device, GMS will download a libraries to the device in order to do barcode and face detection. Usually this is done by the installer before the app is run for the first time.
I had wi-fi off on that testing device. After turning it on and relaunching app, detector became operational and started decoding bitmaps.
To use the API, it's necessary to have internet connection, I had connection to my ADSL but not resolved DNS. Fixing that problem make my app works
Sometimes detector dependencies are downloaded when the app runs for the first time and not when the app installs. I too faced the same issue, the problem is either your network connection is weak or you don't have enough storage for download say 10% of the total space though it does not take that much space but downloads from Google Play Services does require good amount of storage and don't forget to clear cache(Simple check try to update any application from playstore). Refer this Github thread for more information.
Check your storage! make sure it is over 10%
That fixed my problem, and I answered it here too...
https://stackoverflow.com/a/43229272/6914806
you must not forget this:
add this to your AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<meta-data
android:name="com.google.android.gms.vision.DEPENDENCIES"
android:value="ocr"/>

Not ready for backup request right now

Whenever a user installs my app for the first time an instalationID is generated randomly (say Instalationid =123). I am trying to back up this instalationID, so that when the user uninstalls and then installs my app the old instalationID (123) will be assigned to him again, rather than a new one. After all, he is the same user.
I have a shared preferences file, called SESSION_INFO_PREFERENCE_KEY, that holds the instalationID and I try to back up the shared preferences file. The back up manager is:
public class ADCBackupAgent extends BackupAgentHelper {
// The name of the SharedPreferences file instalation ID
static final String INSTID = "SESSION_INFO_PREFERENCE_KEY"; //SESSION_INFO_INSTALLATION_UID
// A key to uniquely identify the set of backup data
static final String INSTID_BACKUP_KEY = "inst_id";
// Allocate a helper and add it to the backup agent
#Override
public void onCreate() {
Log.i("OnCreate Method","!!!!!!ON create called!!!!!!!!!!");
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, INSTID);
addHelper(INSTID_BACKUP_KEY, helper);
}
}
Also, whenever the instalationID is generated for the first time I perform a call to the Back up manager such as:
//this pointing to the current Activity
BackupManager backUpManager = new BackupManager(this);
backUpManager.dataChanged();
For the sake of completenes:
<application
android:icon="#drawable/application_icon"
android:label="#string/app_name"
android:name="MyAppName"
android:theme="#style/FragmentTheme"
android:allowBackup="true"
android:backupAgent=".ADCBackupAgent">
...
<meta-data android:name="com.google.android.backup.api_key" android:value="AEdPqr..." />
</application>
My code works perfectly fine when I do the back up on the local transport (using the bmgr tool), however, when I try to back up the data on Google's cloud strage I get:
04-28 15:12:24.305: W/BackupTransportService(390): Not ready for backup request right now: [OperationScheduler: enabledState=true lastSuccess=2014-04-03/15:10:49 moratoriumSet=2014-04-28/14:43:18 moratorium=2014-04-29/14:43:18 trigger=1969-12-31/19:00:00]
Furthermore, the onCreate() Method is never called.
This does not happen when I back up the data on the local transport, i.e., onCreate() is called and "Not ready for backup request right now" does not appear.

Creating an Android Lock Screen App.

How to create a lock-screen app that acts as a lock for android mobile. I did find one, But it was poorly constructed code wise and if I pressed the physical home key, it unlocked, making the application pointless.
I did come across a forum stating some method of blocking home button functionality was removed in Android 4.x
Yet, I have an awesome idea for a lock-screen but no ground to get started. If anyone has any knowledge on the subject, I'd love to hear it.
Thanks all :-)
Yes, it is possible. This is a simple lock screen Source Code from GitHub
Creating an app that works like a lock is no big deal but as you said for Home key issue, I would suggest you go on and develop the App as much as you need and the only final area you would get stuck is the home key control so, try to find some tricky way to get the control of home key and make it as an app launcher for your lock app. It is not very complicated but kinda tricky though. I will post you, if I can find any Home-key access source codes
PS:
Here is the tutorial for accessing Home Key
I found the home key override somewhere. Add these lines in the App Manifest.
Following two lines will do the magic
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
and override this method in your activity
#Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_HOME)
{
Log.i("Home Button","Clicked");
}
if(keyCode==KeyEvent.KEYCODE_BACK)
{
finish();
}
return false;
}
Keep in mind that I didn't test these codes or methods, just tried to help you (you might find some drawbacks).
PS: based on the votes I can guarantee that my suggestion is working and you can develop such app with the above help :)

Categories

Resources