Android Studio - Implement Methods and parameter names - android

Java compiler does not preserve parameter names for any interface, unless newer compiler option -parameter is used (I am not sure how to use it with android studio) - refer example below.
Since java compiler does not preserve parameters names, Android Studio "code -> implement methods" is not able to generate code with original parameter names.
The question is, how to implement a library module so that Android Studio Menu, Code->Implement Methods correctly generates code with all the original parameter names.
For example, following is a simple class and an interface. This class is in a separate aar module. When application uses this AAR, implements TablaListener and asks AndroidStudio to generate interface methods stubs, the parameter names are not preserved.
Please note that proguard is NOT used.
Any ideas?
public class TablaCore {
public interface TablaListener {
/**
* #param params
* #param data
* #return
*/
boolean TablaCore_onAction(String params, byte[] data);
}
private static TablaListener mListener = null;
public static void setListener(TablaListener myListener) {
mListener = myListener;
}
public TablaListener getListener() {
return mListener;
}
}
It is easy to demonstrate by compiling and decompiling above class. This is decompiled version
public class TablaCore
{
private static TablaListener mListener = null;
public static void setListener(TablaListener myListener)
{
mListener = myListener;
}
public TablaListener getListener()
{
return mListener;
}
public static abstract interface TablaListener
{
public abstract boolean TablaCore_onAction(String paramMessageParams, byte[] paramArrayOfByte);
}
}

You have to include Android SDK source code.
Go to: File > Settings... > Apperance & Behavior > System Settings > Android SDK
On the tab SDK Platforms select Show Package Details and find and select appropriate Sources for Android XX. Unfortunately, currently for Android API 27, there is no sources, but there is for Sources for Android 26. Apply changes - the download window should appear automatically.
After downloading and restarting implementing methods should use proper names for method parameters.

Related

Implementing platform functionality in a static class - Xamarin.Forms

I am not sure of the terminology for what I'm looking to do, so sorry in advance!
I've found a FilePicker plugin for Xamarin.Forms (https://github.com/Studyxnet/FilePicker-Plugin-for-Xamarin-and-Windows) that implements device-specific functionality for selecting files via the CrossFilePicker class.
The way to use leverage this functionality would be something like
CrossFilePicker.Current.OpenFile("Filename.txt");
The most important part of this for me is that CrossFilePicker.Current is static and can be accessible from anywhere in the shared layer of my Xamarin.Forms app.
I need to implement a class with the same characteristics. I want to leverage device Accessibility functionality (i.e. determining if a screen reader is enabled) and I need to be able to do so with a static class.
My eventual plan is to then wrap this static class so that I can use it for unit tests too.
I don't want to import device libraries into my shared project.
TLDR: I need a static class that implements device-specific functionality.
Any help would be greatly appreciated! Thank you :)
EDIT:
Here are the files I have currently implemented in my project
IAccessibilityService Located in the shared .NET project
namespace Bitspace.Services
{
public interface IAccessibilityService
{
public bool IsScreenReaderEnabled();
public void Announcement(string message);
public void NavigationAnnouncement(string message);
}
}
DeviceAccessibility.cs Located in the shared .NET project
using System;
namespace Bitspace.Services
{
public class DeviceAccessibility
{
private static Lazy<IAccessibilityService> Implementation = new Lazy<IAccessibilityService>(() => CreateAccessibilityService(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
public static IAccessibilityService Current
{
get
{
var curr = Implementation.Value;
if (curr == null)
{
throw new Exception();
}
return curr;
}
}
private static IAccessibilityService CreateAccessibilityService()
{
return new DeviceAccessibilityImplementation();
}
}
}
DeviceAccessibilityImplementation.cs Located in the Android project
using Android.Runtime;
namespace Bitspace.Services
{
[Preserve (AllMembers = true)]
public class DeviceAccessibilityImplementation : IAccessibilityService
{
public bool IsScreenReaderEnabled()
{
return true;
}
public void Announcement(string message)
{
}
public void NavigationAnnouncement(string message)
{
}
}
}
If I try to build the project, I get an error on the return new DeviceAccessibilityImplementation(); line in DeviceAccessibility.cs that says DeviceAccessibility.cs(25, 24): [CS0246] The type or namespace name 'DeviceAccessibilityImplementation' could not be found (are you missing a using directive or an assembly reference?)
However, CTRL Clicking on that line takes me to the DeviceAccessibilityImplementation.cs

How to correctly implement and test Custom Lint Rules in Android Studio?

I'm following this tutorial and this Custom Detector Example in order to implement Custom Lint Rules. Basically what I've done is:
Create a new Android Project in Android Studio;
Create a java module for project created in step 1;
On module's build.gradle, import Lint API dependencies;
Create an Issue & IssueRegistry & CustomDetector;
Reference the IssueRegistry on module's build.gradle;
Create Unit tests;
My problem is, during the execution of my JUnits, I always receive "No Warning". When I debug the test, I can see that my Custom Detector isn't called, what am I doing wrong?
Strings.java
public class Strings {
public static final String STR_ISSUE_001_ID = "VarsMustHaveMoreThanOneCharacter";
public static final String STR_ISSUE_001_DESCRIPTION = "Avoid naming variables with only one character";
public static final String STR_ISSUE_001_EXPLANATION = "Variables named with only one character do not pass any meaning to the reader. " +
"Variables name should clear indicate the meaning of the value it is holding";
}
Issues.java
public class Issues {
public static final
Issue ISSUE_001 = Issue.create(
STR_ISSUE_001_ID,
STR_ISSUE_001_DESCRIPTION,
STR_ISSUE_001_EXPLANATION,
SECURITY,
// Priority ranging from 0 to 10 in severeness
6,
WARNING,
new Implementation(VariableNameDetector.class, ALL_RESOURCES_SCOPE)
);
}
IssuesRegistry.java
public class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry {
#Override
public List<Issue> getIssues() {
List<Issue> issues = new ArrayList<>();
issues.add(ISSUE_001);
return issues;
}
}
VariableNameDetector.java
public class VariableNameDetector extends Detector implements Detector.JavaScanner {
public VariableNameDetector() {
}
#Override
public boolean appliesToResourceRefs() {
return false;
}
#Override
public boolean appliesTo(Context context, File file) {
return true;
}
#Override
#Nullable
public AstVisitor createJavaVisitor(JavaContext context) {
return new NamingConventionVisitor(context);
}
#Override
public List<String> getApplicableMethodNames() {
return null;
}
#Override
public List<Class<? extends Node>> getApplicableNodeTypes() {
List<Class<? extends Node>> types = new ArrayList<>(1);
types.add(lombok.ast.VariableDeclaration.class);
return types;
}
#Override
public void visitMethod(
JavaContext context,
AstVisitor visitor,
MethodInvocation methodInvocation
) {
}
#Override
public void visitResourceReference(
JavaContext context,
AstVisitor visitor,
Node node,
String type,
String name,
boolean isFramework
) {
}
private class NamingConventionVisitor extends ForwardingAstVisitor {
private final JavaContext context;
NamingConventionVisitor(JavaContext context) {
this.context = context;
}
#Override
public boolean visitVariableDeclaration(VariableDeclaration node) {
StrictListAccessor<VariableDefinitionEntry, VariableDeclaration> varDefinitions =
node.getVariableDefinitionEntries();
for (VariableDefinitionEntry varDefinition : varDefinitions) {
String name = varDefinition.astName().astValue();
if (name.length() == 1) {
context.report(
ISSUE_001,
context.getLocation(node),
STR_ISSUE_001_DESCRIPTION
);
return true;
}
}
return false;
}
}
}
build.gradle
apply plugin: 'java'
configurations {
lintChecks
}
ext {
VERSION_LINT_API = '24.3.1'
VERSION_LINT_API_TESTS = '24.3.1'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.tools.lint:lint-api:$VERSION_LINT_API"
implementation "com.android.tools.lint:lint-checks:$VERSION_LINT_API"
testImplementation "com.android.tools.lint:lint-tests:$VERSION_LINT_API_TESTS"
}
jar {
manifest {
attributes('Lint-Registry': 'br.com.edsilfer.lint_rules.resources.IssueRegistry')
}
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
TestVariableNameDetector.java
private static final String ARG_DEFAULT_LINT_SUCCESS_LOG = "No warnings.";
#Override
protected Detector getDetector() {
return new VariableNameDetector();
}
#Override
protected List<Issue> getIssues() {
return Collections.singletonList(Issues.ISSUE_001);
}
public void test_file_with_no_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test.java", "public class Test {public String sampleVariable;}"))
);
}
public void test_file_with_variables_with_length_equals_01() throws Exception {
assertEquals(
ARG_DEFAULT_LINT_SUCCESS_LOG,
lintProject(java("assets/Test3.java", "public class Test {public String a;bnvhgvhj}"))
);
}
}
P.S.: on Java's module I do not have access to assetsor res folder, that is the reason why I've created a String.java and I'm using java(to, source) in my Unit test - I assumed that this java method does the same as the xml from the tutorial link I referenced at the top of this question.
It turned out that in my case the problem was with the JUnit itself. I think that the way I was attempting to simulate the file was wrong. The text below is part of the README.md of a sample project that I've created in order to document what I've learned from this API and answers the question in the title:
Create
Create a new Android Project;
Create a new Java Library Module - Custom Lint Rules are packaged into .jar libraries once they are ready, therefore the easiest way to implement them using them is inside a Java Module Library;
On module's build.gradle:
add target and source compatibility to Java 1.7;
add dependencies for lint-api, lint-checks and test dependencies;
add jar packing task containing two attributes: Manifest-Version and Lint-Registry, set the first to 1.0 and the second as the full path to a class that will later on contain the issue's catalog;
add a default tasl assemble;
[OPTIONAL]: add a task that will copy the generated .jar into ~/.android/lint;
Check REF001 and choose a Detector that best suits your needs, based on it create and implement a class to fulfill the Detector's role;
Still based on REF0001 chosen file, create and implement a Checker class, later referring to it inside Detector's createJavaVisitor() method;
for the sake of SRP, do not place Checker in the same file of Detector's class;
Copy the generated .jar file from build/lib to ~/.android/lint - if you added a task on build.gradle that does this you can skip this step;
Restart the computer - once created and moved into ~/.android/lint, the Custom Rules should be read by Lint next time the program starts. In order to have the alert boxes inside Android Studio, it is enough to invalidate caches and restart the IDE, however, to have your custom rules caught on Lint Report when ./gradlew check, it might be necessary to restart your computer;
Testing Detectors and Checkers
Testing Custom Rules is not an easy task to do - mainly due the lack of documentation for official APIs. This section will present two approaches for dealing with this. The main goal of this project is to create custom rules that will be run against real files, therefore, test files will be necessary for testing them. They can be places in src/test/resources folder from your Lint Java Library Module;
Approach 01: LintDetectorTest
Make sure you've added all test dependencies - checkout sample project's build.gradle;
Copy EnhancedLintDetectorTest.java and FileUtils.java into your project's test directory;
There is a known bug with Android Studio that prevents it from seeing files from src/test/resources folder, these files are a workaround for that;
EnhancedLintDetectorTest.java should return all issues that will be subject of tests. A nice way to do so is getting them from Issue Registry;
Create a test class that extends from EnhancedLintDetectorTest.java;
Implement getDetector() method returning an instance of the Detector to be tested;
Use lintFiles("test file path taking resources dir as root") to perform the check of the Custom Rules and use its result object to assert the tests;
Note that LintDetectorTest.java derives from TestCase.java, therefore, you're limited to JUnit 3.
Approach 02: Lint JUnit Rule
You might have noticed that Approach 01 might be a little overcomplicated, despite the fact that you're limited to JUnit 3 features. Because of that GitHub user a11n created a Lint JUnit Rule that allows the test of Custom Lint Rules in a easier way that counts with JUnit 4 and up features. Please, refer to his project README.md for details about how to create tests using this apprach.
Currently, Lint JUnit Rule do not correct the root dir for test files and you might no be able to see the tests passing from the IDE - however it works when test are run from command line. An issue and PR were created in order to fix this bug.
I'm not sure how to use the AST Api, however I'm personally using Psi and this is one of my lint checks that are working for me.
public final class RxJava2MethodCheckReturnValueDetector extends Detector implements Detector.JavaPsiScanner {
static final Issue ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE =
Issue.create("MethodMissingCheckReturnValue", "Method is missing the #CheckReturnValue annotation",
"Methods returning RxJava Reactive Types should be annotated with the #CheckReturnValue annotation.",
MESSAGES, 8, WARNING,
new Implementation(RxJava2MethodCheckReturnValueDetector.class, EnumSet.of(JAVA_FILE, TEST_SOURCES)));
#Override public List<Class<? extends PsiElement>> getApplicablePsiTypes() {
return Collections.<Class<? extends PsiElement>>singletonList(PsiMethod.class);
}
#Override public JavaElementVisitor createPsiVisitor(#NonNull final JavaContext context) {
return new CheckReturnValueVisitor(context);
}
static class CheckReturnValueVisitor extends JavaElementVisitor {
private final JavaContext context;
CheckReturnValueVisitor(final JavaContext context) {
this.context = context;
}
#Override public void visitMethod(final PsiMethod method) {
final PsiType returnType = method.getReturnType();
if (returnType != null && Utils.isRxJava2TypeThatRequiresCheckReturnValueAnnotation(returnType)) {
final PsiAnnotation[] annotations = method.getModifierList().getAnnotations();
for (final PsiAnnotation annotation : annotations) {
if ("io.reactivex.annotations.CheckReturnValue".equals(annotation.getQualifiedName())) {
return;
}
}
final boolean isMethodMissingCheckReturnValueSuppressed = context.getDriver().isSuppressed(context, ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, method);
if (!isMethodMissingCheckReturnValueSuppressed) {
context.report(ISSUE_METHOD_MISSING_CHECK_RETURN_VALUE, context.getLocation(method.getNameIdentifier()), "Method should have #CheckReturnValue annotation");
}
}
}
}
}
Checkout the many more I wrote here.

Android NDK making library available all through the app

I am diving into the ndk stuff and I've managed to make a simple library and call it from my activity.
In order to test it, in my Activity I have used:
static {
System.loadLibrary("my-lib");
}
public native static String callNativeFunction(Context context);
This means that calling MyActivity.callNativeFunction(context) does return the String value from my Cpp function.
I have 2 questions:
The loadLibrary is made in my main activity, however I want to be able for instance, to call the callNativeFunction function from an IntentService when the activity may be closed or from other places of my app. How can I properly load the library and have it available from all places of the app?
Since this is a simple function that I'll use in my project, is there anything else specific to do on release? From https://developer.android.com/studio/projects/add-native-code.html it seems that Gradle builds for all supported abis and adds them into the apk.
You need to load the library once. This can also be done in your Application class. Once the library is loaded, you can call the defined native methods from the appropriate classes defined in your cpp class
Application class :
public class SRApplication extends Application {
static {
System.loadLibrary("my-lib");
}
#Override
public void onCreate() {
super.onCreate();
}
}
CPP :
extern "C"
jstring
Java_com_example_service_ExampleService_getNativeString (
JNIEnv* env,
jobject /* this */) {
return env->NewStringUTF("yo");
}
Your Service :
package com.example.service.ExampleService;
public class ExampleService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
String nativeString = getNativeString();
return null;
}
public native String getNativeString();
}
If you don't have specific code for different cpu variants, you don't need to explicitly handle anything, the default
externalNativeBuild {
cmake {
cppFlags ""
}
}
is sufficient.

How to access Android resources e.g. dimensions in libgdx?

I need to get dimensions defined in XML files under Android folder from classes in Core directory of libgdx.
I know I can use assets folder for accessing drawables. But how about dimensions or strings?
You can't access this kind of platform specific information directly in the core project. But this can be achieved by interfacing these functions with a Facade, providing a platform-specific implementation for each target.
In libgdx-wiki there is an example for platform specific interfaces.
public interface PlatformSpecificStuff {
public String getString();
public int getDimensions();
}
// In -Android project
public class AndroidSpecificStuff implements PlatformSpecificStuff {
public String getString(){
// do whaterver you want with the resources.
return "string resource";
}
public int getDimensions(){
// do whaterver you want with the resources.
return 42;
}
}
// In -Desktop project
public class DesktopSpecificStuff implements PlatformSpecificStuff {
public String getString(){
// there is no xml or any andorid specific resource
return null;
}
public int getDimensions(){
// there is no xml or any andorid specific resource
return 0;
}
}
public class MyGame implements ApplicationListener {
private final PlatformSpecificStuff pss;
public MyGame(PlatformSpecificStuff pss) {
this.pss= pss;
}
}
Update: I don't know what are trying to accomplish by getting the dimensions as android resources. But you are probably in wrong track. If you want to support multiple screen sizes, have a look at viewports

How does exactly custom Shadow objects work in Robolectric?

If I write a custom Shadow for my Activity, and registering it with RobolectricTestRunner, will the framework intercept the Activity with my custom Shadow whenever it's started?
Thanks.
The short answer is no.
Robolectric is selective about what classes it intercepts and instruments. At the time of this writing, the only classes that will be instrumented must have a fully qualified classname match one of these selectors:
android.*
com.google.android.maps.*
org.apache.http.impl.client.DefaultRequestDirector
The whole reason for Robolectric's existence is that the classes provided in the Android SDK jar throw exceptions when invoked in a JVM (i.e. not on an emulator or device). Your application's Activity has source that is not 'hostile' (it probably does not throw exceptions when the methods or constructors are invoked). Robolectric's intended purpose is to allow you to put your application's code under test, which would otherwise not be possible due to the way the SDK is written. Some of the other reasons why Robolectric was created were:
The SDK does not always have methods that would allow you to query the state of the Android objects manipulated by your application's code. Shadows can be written to provide access to this state.
Many of the classes and methods in the Android SDK are final and/or private or protected, making it difficult to create the dependencies needed by your application code that would otherwise be available to your application code.
The code could clearly be changed to shadow any class. There has been talk in the past about extracting the shadowing features into a standalone library, to assist writing tests using some other test-hostile api.
Why do you want to shadow your Activity?
This has significantly changed with Robolectric 2. You can specify custom shadows in the configuration instead of writing your own TestRunner.
For example:
#Config(shadows = {ShadowAudioManager.class, ShadowContextWrapper.class})
Yes, if you subclass the RobolectricTestRunner, add a custom package to the constructor and load your Shadow classes in the bindShadowClasses method. No need to use the android.* package trick.
(Note: this is with robolectric-1.1)
There are a number of hooks provided in the RobolectricTestRunner#setupApplicationState that you can override.
Here's my implementation of the RobolectricTestRunner.
import org.junit.runners.model.InitializationError;
import com.android.testFramework.shadows.ShadowLoggerConfig;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricTestRunner;
public class RoboRunner extends RobolectricTestRunner {
public RoboRunner(Class<?> clazz) throws InitializationError {
super(clazz);
addClassOrPackageToInstrument("package.you're.creating.shadows.of");
}
#Override
protected void bindShadowClasses() {
super.bindShadowClasses(); // as you can see below, you really don't need this
Robolectric.bindShadowClass(ShadowClass.class);
}
}
More methods you can subclass (from RobolectricTestRunner.class)
/**
* Override this method to bind your own shadow classes
*/
protected void bindShadowClasses() {
}
/**
* Override this method to reset the state of static members before each test.
*/
protected void resetStaticState() {
}
/**
* Override this method if you want to provide your own implementation of Application.
* <p/>
* This method attempts to instantiate an application instance as specified by the AndroidManifest.xml.
*
* #return An instance of the Application class specified by the ApplicationManifest.xml or an instance of
* Application if not specified.
*/
protected Application createApplication() {
return new ApplicationResolver(robolectricConfig).resolveApplication();
}
Here's where they're called in the Robolectric TestRunner:
public void setupApplicationState(final RobolectricConfig robolectricConfig) {
setupLogging();
ResourceLoader resourceLoader = createResourceLoader(robolectricConfig);
Robolectric.bindDefaultShadowClasses();
bindShadowClasses();
resourceLoader.setLayoutQualifierSearchPath();
Robolectric.resetStaticState();
resetStaticState();
DatabaseConfig.setDatabaseMap(this.databaseMap);//Set static DatabaseMap in DBConfig
Robolectric.application = ShadowApplication.bind(createApplication(), resourceLoader);
}
As an update, I have been able to create shadows of my own classes, as long as am careful to bind the shadow class before any possible loader acts on that class. So, per the instructions, in the RoboRunner I did:
#Override protected void bindShadowClasses() {
Robolectric.bindShadowClass(ShadowLog.class);
Robolectric.bindShadowClass(ShadowFlashPlayerFinder.class);
}
Did I mention that I'm cheating a bit? The original answer above is (of course) correct. So I use this for my real class:
package android.niftyco;
public class FlashPlayerFinder {
.. .
And my mock (shadow) is in back in my test package, as one might expect:
package com.niftyco.android.test;
#Implements(FlashPlayerFinder.class)
public class ShadowFlashPlayerFinder {
#RealObject private FlashPlayerFinder realFPF;
public void __constructor(Context c) {
//note the construction
}
#Implementation
public boolean isFlashInstalled() {
System.out.print("Let's pretend that Flash is installed\n");
return(true);
}
}
Might be late, but from here: org.robolectric.bytecode.Setup, you might find further detail about what classes are instrumented.
public boolean shouldInstrument(ClassInfo classInfo) {
if (classInfo.isInterface() || classInfo.isAnnotation() || classInfo.hasAnnotation(DoNotInstrument.class)) {
return false;
}
// allow explicit control with #Instrument, mostly for tests
return classInfo.hasAnnotation(Instrument.class) || isFromAndroidSdk(classInfo);
}
public boolean isFromAndroidSdk(ClassInfo classInfo) {
String className = classInfo.getName();
return className.startsWith("android.")
|| className.startsWith("libcore.")
|| className.startsWith("dalvik.")
|| className.startsWith("com.android.internal.")
|| className.startsWith("com.google.android.maps.")
|| className.startsWith("com.google.android.gms.")
|| className.startsWith("dalvik.system.")
|| className.startsWith("org.apache.http.impl.client.DefaultRequestDirector");
}

Categories

Resources