Why UsageStats.mLaunchCount is not accessible in IDE? - android

I have all permissions in place and can get the list of apps, and their usage stats as a list of UsageStats instances. UsageStats has a public field called mLaunchCount, added on API 22 (based on git history of the file). Now I want to access this if the phone is running API 22+, but when I try to use it, the IDE complains Cannot resolve symbol mLaunchCount. If I try to access it via reflection, it works.
So basically this does not compile:
Log.d("test", "Count: " + usageStat.mLaunchCount);
While this does:
Field mLaunchCount = UsageStats.class.getDeclaredField("mLaunchCount");
int launchCount = (Integer)mLaunchCount.get(usageStat);
Log.d("Test", "Count: " + launchCount);
Any idea what's happening?
Thanks

Because mLaunchCount is not part of the Android SDK. In the source code, it is marked with the #hide annotation, used for things that are public for Java reasons but are not part of the SDK.

Related

Android printing with Brother SDK via WiFi throws ERROR_WRONG_LABEL despite selecting correct labelNameIndex

I've been trying to print with Brother Print SDK 3.5.1 on Android 8.1.0. I keep getting ERROR_WRONG_LABEL.
This is the code I use
void printPdf() {
// Specify printer
final Printer printer = new Printer();
PrinterInfo settings = printer.getPrinterInfo();
settings.printerModel = PrinterInfo.Model.QL_810W;
settings.port = PrinterInfo.Port.NET;
settings.ipAddress = "192.168.1.73";
settings.workPath = "storage/emulated/0/Download/";
// Print Settings
settings.labelNameIndex = LabelInfo.QL700.W62RB.ordinal();
settings.printMode = PrinterInfo.PrintMode.FIT_TO_PAGE;
settings.orientation = PrinterInfo.Orientation.PORTRAIT;
settings.isAutoCut = true;
printer.setPrinterInfo(settings);
// Connect, then print
new Thread(new Runnable() {
#Override
public void run() {
if (printer.startCommunication()) {
PrinterStatus result = printer.printPdfFile("/storage/emulated/0/Download/hello world red.pdf", 1);
if (result.errorCode != PrinterInfo.ErrorCode.ERROR_NONE) {
Log.d("TAG", "ERROR - " + result.errorCode);
}
printer.endCommunication();
}
}
}).start();
}
My printer model is QL-810W and I use the black and red W62 roll.
I've tried the Sample Application, where setting W62RB in labelNameIndex prints fine.
Changing the roll for different one with different width didn't help.
I've also tried iterating over numbers 0 to 50 and using them as labelNameIndex.
Based on this thread, I thought that the issue may be in specifying the workPath attribute. Setting workPath to getContext().getCacheDir().getPath() results in ERROR_OUT_OF_MEMORY instead of ERROR_WRONG_LABEL. Not specifying workPath and adding <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> to AndroidManifest.xml results in ERROR_WRONG_LABEL
EDIT
I've modified the Brother Sample app and uploaded it to GitHub. The app now launches Activity_PrintPdf.java by default where I inserted my printing code with hardcoded values at the beginning of onCreate method - this works fine and prints the PDF file as expected.
Then I created a new Empty Activity project in Android Studio, copy pasted the library, added the imports to build.gradle and copy pasted the permissions into AndroidManifest.xml. Then I copy pasted the printing code at the beginning of onCreate method in MainActivity.java. Running the app results in ERROR_WRONG_LABEL.
This is the modified working example app and this is the one that results in the error. I want to use the code as native module that I call from my React Native app, so it's important that I manage to set up the printing code from scratch rather than modifying the existing example app.
EDIT 2
I've inspected the library with a debugger: when executing printer.setPrinterInfo(mPrinterInfo) the library internally calls private boolean createWorkPath(String dirPath) of Printer object. On return from this method, the debugger shows Source code doesn't match the bytecode and seems to forget the created directory. This also internally sets mResult.errorCode = ErrorCode.ERROR_WORKPATH_NOT_SET. However, instead of rising any error here the code just silently proceeds, which later results in ERROR_WRONG_LABEL when trying to print. Running the same code snipper in the modified Sample app works fine.
I'd be grateful if you could help or suggest what to try next.
Thank you!
I've now fixed the issue, which was that the Brother library silently failed to create a temporarily folder and instead of reporting an error, it continued and failed later to read the label information. Based on this thread, it is now required to specify runtime file read and write permissions as opposed to the compile-time ones in AndroidManifest.xml.
Adding
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
at the beginning of onCreate before the printing code fixed the issue.

Clear parse installation cache on Android

does anyone know how to clear the data from the parse installation on the newer library version 1.10? In 1.8 you could do it via reflection by calling clear from memory, like described in this answer: ParseObject mergeREST raise ConcurrentModificationException
I am deleting the parse installation from web, and I also need to clear the ram cache on the android phone and I can't find a way to do it. Any ideas?
Solved by making a package in my project named com.parse, in it I've placed a file named ParseEasyAccess.java , it contains the following method:
public static void clearParse() {
ParseInstallation.getCurrentInstallationController().clearFromDisk();
ParseInstallation.getCurrentInstallationController().clearFromMemory();
}
You can call this from anywhere in the app and it will clear all the parse installation data from RAM & disk.
The accepted answer will not work for sdk version 1.13.1.
The only way to access those methods is like this:
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
Class clazz = installation.getClass();
Method[] methods = clazz.getDeclaredMethods();
Method method1 = clazz.getDeclaredMethod("getCurrentInstallationController");
method1.setAccessible(true);
Object result = method1.invoke(installation);
Method method2 = result.getClass().getDeclaredMethod("clearFromDisk");
method2.setAccessible(true);
String result2=(String) method2.invoke(result);
Method method3 = result.getClass().getDeclaredMethod("clearFromMemory");
method3.setAccessible(true);
String result3=(String) method3.invoke(result);

Baidu maps on Android: access key does not work for location searching

I'm creating an Android app for a chinese client and they need map integration, so Google maps is not an option since all Google services are blocked in China. I'm trying to use Baidu maps, which is called Baidu LBS (location-based services) cloud.
Getting a basic map with no overlays to work was relatively easy. The process is described here (in Chinese, but the code speaks for itself if you don't understand the language). Downloading the latest Baidu Android SDK (v3.2.0 at time of writing) and integrating it into my Eclipse project as a library was no problem, but don't trust the documentation in that link too much even though it is the official one. Their examples often contain code that wouldn't even compile. The name of the .jar file for example was completely different from what you see in their screenshot.
Oh and also their .jar library is obfuscated which is super annoying to work with :-(
I needed to register a Baidu account and go to their control center to generate a key. To create an access key ("ak") for mobile you need to enter the SHA1 fingerprint of the keystore which signs your app, followed by the package name specified in your manifest.
Then I added the generated key to my manifest under the tag
<meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="xxx...xxx" />
I then copied code from their sample project's CloudSearchActivity because I have specific coordinates that I would like to display. I implemented the CloudListener interface as shown:
#Override
public void onGetSearchResult(final CloudSearchResult result, final int error)
{
Log.w("onGetSearchResult", "status=" + result.status + ". size=" + result.size + ". total=" + result.total + ". error=" + error);
if(null != result && null != result.poiList && 0 < result.poiList.size())
{
mBaiduMap.clear();
final BitmapDescriptor bitmapDescriptor=BitmapDescriptorFactory.fromResource(R.drawable.icon_address_grey);
LatLng latitudeLongitude;
LatLngBounds.Builder builder=new Builder();
for(final CloudPoiInfo info : result.poiList)
{
latitudeLongitude=new LatLng(info.latitude, info.longitude);
final OverlayOptions overlayOptions=new MarkerOptions().icon(bitmapDescriptor).position(latitudeLongitude);
mBaiduMap.addOverlay(overlayOptions);
builder.include(latitudeLongitude);
}
final LatLngBounds bounds=builder.build();
MapStatusUpdate mapStatusUpdate=MapStatusUpdateFactory.newLatLngBounds(bounds);
mBaiduMap.animateMapStatus(mapStatusUpdate);
}
}
And I added code to launch a query (also copied from their sample project):
#Override
public View onCreateView(final LayoutInflater layoutInflater, final ViewGroup viewGroup,
final Bundle savedInstanceState)
{
// initialize needs to be called
SDKInitializer.initialize(getApplication());
CloudManager.getInstance().init(MyFragment.this);
view=(ViewGroup)layoutInflater.inflate(R.layout.fragment_map, viewGroup, false);
mMapView=(MapView)view.findViewById(R.id.baiduMapView);
mBaiduMap=mMapView.getMap();
NearbySearchInfo info=new NearbySearchInfo();
info.ak="xxx...xxx";
info.geoTableId=12345;
info.tags="";
info.radius=30000;
info.location="116.403689,39.914957";
CloudManager.getInstance().nearbySearch(info);
return view;
}
Unfortunately I keep getting a status value of 102 from the server (according to this API page that means STATUS_CODE_SECURITY_CODE_ERROR. Now I don't know what to do.
Things that I don't understand:
Why do I need to repeat my access key ("ak") when building the query? Is it not enough to have it in the manifest once?
What is this "geoTableId" value in the query supposed to be?
Any ideas?
After many hours of research I have made some progress on the open questions.
The reason for the "ak" field in a cloud search query is not duplication, it is in fact a different access key. Somewhere in a hidden place Baidu says that access keys "for mobile" will not work for these cloud searches, you need an ak "for server". So the solution is to go back to the Baidu control center and create another key "for server". This key needs to be used in the query, while the "for mobile" key needs to remain in the manifest.
geoTableId is an identifier of your account, not unsimilar to the access keys. It is a (currently) 5 digit number that you need to obtain in the Baidu control center. The other keys were generated in the tab titled "API控制台" (API control desk), but for the geoTableId you need to switch to the tab called "数据管理" (data management). There I think I needed to press the "创建" (~create) button on top left, then enter a name, select "是" (yes) where they ask if this is for release (not sure about that translation) and then click "保存" (save). After this, your freshly generated number is displayed in the top field in parentheses behind the name you chose just now.
These steps have allowed me to send "successful" queries where the server answers with status 0 (STATUS_CODE_SUCCEED). However, so far all the answers I get are empty, I have yet to find a query which produces a non-empty answer. If anyone manages to do that, please let me know!

Clearing the data of another application

I'm writing an application that should be able clear the private data of any other application. If you're wondering about the use case, its along the lines of an MDM/MAM client. I'd like to selectively wipe application data (vs. a full device wipe).
I came across the following API call in the Android source code.
ActivityManager.clearApplicationUserData(String packageName,IPackageDataObserverobserver)
The odd part is, that this is not really available to you as part of the SDK . (So eclipse will give you hell for trying to use it). However, it is present (see here),you can invoke it via reflection. I'm still however, unable to get hold of the IPackageDataObserver interface.
Is there a better way of doing this? I know it CAN be done since I've seen products like MaaS360 do a selective wipe of applications' data.
Any suggestions?
UPDATE
Based on what #lechlukasz has outlined below... the following code can execute...but you do finally land up with a SecurityException, since the package manager revokes the CLEAR_APP_USER_DATA permission when the app is installed.
Class<?> iPackageDataObserverClass= Class.forName("android.content.pm.IPackageDataObserver");
Class<ActivityManager> activityManagerClass=ActivityManager.class;
ActivityManager activityManager=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
Method clearDataMethod=activityManagerClass.getMethods()[0];
Object iPackageDataObserverObject = Proxy.newProxyInstance(
MyApp.class.getClassLoader(), new Class[]{iPackageDataObserverClass},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Log.i("Proxy", method.getName() + ": " + Arrays.toString(args));
return null;
}
});
clearDataMethod.invoke(activityManager, "com.example.test",iPackageDataObserverObject);
So this works, insofar as the method can be called. No luck on actually being able to clear the data itself. :-(
The method you point isn't static method, so in order to call it you would need the ActivityManager instance, which would be the trickiest part, even if you have root privileges. I can't help you with that.
But as for instantiating IPackageDataObserver, I've managed to do this without special privileges, using standard refrection API:
Class ipdoClass = Class.forName("android.content.pm.IPackageDataObserver");
Object observer = Proxy.newProxyInstance(
MyApp.class.getClassLoader(), new Class[]{ipdoClass},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Log.i("Proxy", method.getName() + ": " + Arrays.toString(args));
return null;
}
});
Your code should work, but you need to add appropriate permission in your manifest (CLEAR_APP_USER_DATA) and sign Your application with the platform key.

android compatibility. I am confused when using Build.VERSION_CODES

Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
I write code like this, I used the sdk4.0 to compile this android program, so it didn't cause compile error. When I run this program in my phone that running android 2.3.4, it run well.
Why? I am confused that version 2.3.4 (api level 10) has Build.VERSION_CODES.ICE_CREAM_SANDWICH property? And when I used sdk2.3.4 will cause compile error.
More
I test some code like these below,
private ScaleGestureDetector mScaleGestureDetector;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
mScaleGestureDetector = new ScaleGestureDetector(this, new MyOnScaleGestureListener());
}
this code will run well on android 1.6 api level 4, but
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
Log.d(TAG, "getx(0): " + event.getX(0));
}
this program run failed on android 1.6 api level 4.
They both run on android 2.3.4 well.
why? (In ScaleGestureDetector class use the event.getX(0) (since api level 5) too)
I test some code more..
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Fragment f = new Fragment();
}
When I run it on android 1.6 emulator it throw java.lang.VerifyError, but on my phone running android 2.3.4 it throws java.lang.NoClassDefFoundError.
Why??
It's not as strange as it seems. It has to do with how Java treat constant values of primitives. During compilation, the value of the constant is put in the byte code, not a reference to the actual constant.
For example:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
will actucally be translated by the compiler to:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + 14);
so the reference to the actual constant (and class) is removed.
Regarding the code that doesn't run for you, it has to do with that the MotionEvent.getX(int n) method wasn't available until api level 5. Before that, multitouch wasn't supported and thus no need for any other method than getX().
It doesn't matter if you actually call the method that doesn't exist. The error is appearing while the class is being loaded and verified by the platform. You most likely get a VerifyError in the log, since it will discover that you're trying to call a non-existent method during the verification.
On the other hand, if you try to use a class that doesn't exist, you will get a ClassNotFoundException instead. Note that sometimes, a class exists in Android even if the documentation doesn't say so. Some classes existed in early versions of Android but weren't exposed until later. Some have even gone the other way.
So:
Trying to use a class that doesn't exist - ClassNotFoundException
Trying to use a method that doesn't exist on a class that exists - VerifyError
(When it comes to using Fragments, they are available for earlier versions with the standalone Android Support Library)

Categories

Resources