I'm trying to create a custom attribute that behaves like tools:context, that is with
Android Studio auto complete functionallity
Project classname reference
Support for auto refactory in case I change my class directory
This is my resources.xml
<declare-styleable name="RecyclerView">
<attr name="adapter" format="string"></attr>
</declare-styleable>
This is the usage
<example.com.br.appname.RecyclerView
android:id="#+id/accounts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
app:adapter="example.com.br.appname.AccountAdapter" >
</example.com.br.appname.RecyclerView>
I've tried to use the format reference but it didn't compile as well.
Error:(17, 22) String types not allowed (at 'adapter' with value 'example.com.br.appname.AccountAdapter').
I don’t think this is possible currently. Other similar custom attrs I can think of, for instance app:layout_behavior from the design library, or simply app:layoutManager from RecyclerView all require the full classname, with none of your requirements.
It might be better to store these in a strings resource file, and remember to check it when refactoring class names.
You can consider filing a feature request, since Android Studio has this functionality in special cases (tools:context, class in <view> and <fragment> tags, classes in Manifest...), but I doubt they would add a new attribute format just for this.
so...
apparently, YOU CAN!
Google does this too.
Android Studio understands that the class is being referenced from XML
i.e.
Refactor > Rename works
Find Usages works
and so on...
don't specify a format attribute in .../src/main/res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
use it in some layout file .../src/main/res/layout/activity__main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
parse the class in your view initialization code .../src/main/java/.../MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}
Related
I am trying to create a binding for a library, Firebase.Messaging since the current one on Nuget is not up to date, but one class out of this .aar library is not being added to the binding.
This is the RemoteMessage.class.
When I decompile the firebase-messaging.aar I do see the RemoteMessage.class present in the .aar file but when I add this .aar file to my bindingproject and run I get following error:
Warning BG8604: top ancestor RemoteMessage not found for nested type Firebase.Messaging.RemoteMessage.Builder. (BG8604)
This means that for instance the method
public override void OnMessageReceived(RemoteMessage message)
will not be added to my binding because RemoteMessage is not found. I have tried to manually add this to my binding by using <add-note ... but no luck so far.
In my binding I have added references to firebase-common, firebase-iid, googleplayservices-basement, googleplayservices-tasks.
Does anyone have an idea how I can force my binding to recognise the RemoteMessage class and add this to the binding?
Edit:
What I do is create a new Xamarin Binding project for android, add the aar file to the Jars folder & update the Metadata.xml
This is the metadata.xml I am currently using:
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<!-- Rename Namespaces -->
<attr path="/api/package[#name='com.google.firebase.messaging']" name="managedName">Firebase.Messaging</attr>
<!-- Remove *zz* obfuscated classes, interfaces, methods, etc. -->
<remove-node path="/api/package/class[contains(#name, 'zz')]" />
<remove-node path="/api/package/interface[contains(#name, 'zz')]" />
<remove-node path="/api/*/*/method[contains(#name, 'zz')]" />
<remove-node path="/api/*/*/field[contains(#name, 'zz')]" />
<!-- Fix params for some inherited parcelable types -->
<attr path="/api/*/*/method[#name='createFromParcel']/parameter[#name='p0']" name="managedName">source</attr>
<attr path="/api/*/*/method[#name='newArray']/parameter[#name='p0']" name="managedName">size</attr>
<attr path="/api/*/*/method[#name='writeToParcel']/parameter[#name='p0']" name="managedName">dest</attr>
<attr path="/api/*/*/method[#name='writeToParcel']/parameter[#name='p1']" name="managedName">flags</attr>
</metadata>
Nothing in the additions folder, nor the EnumFields or EnumMethods, currently I am using the firebase-messaging-11.8.0.aar for this binding.
After the Binding this is the result I see in the Assembly Browser:
I have figured out why RemoteMessage could not be bound and thus for several methods & classes the top ancestor could not be found.
The GooglePlay Basement & Tasks libraries have changed. This caused an internal problem because the obfuscated class RemoteMessage relies on was now invisible once more, the metadata I was using relied on 11.4.2 but since 11.8.0 this obfuscated class is not found in the Tasks library anymore but in the Basement library, also under a different name.
I have added
<attr path="/api/package[#name='com.google.android.gms.internal']/class[#name='zzbfm']" name="obfuscated">false</attr>
<!-- Fix plural name - legacy compatibility -->
<attr path="/api/package[#name='com.google.android.gms.common.api']/class[#name='Status']" name="managedName">Statuses</attr>
<attr path="/api/package[#name='com.google.android.gms.common.api']/class[#name='Status']" name="extends">Java.Lang.Object</attr>
<add-node path="/api/package[#name='com.google.android.gms.common.api']/class[#name='Status']">
<method abstract="false" deprecated="not deprecated" final="true" name="describeContents" native="false" return="int" static="false" synchronized="false" visibility="public"></method>
</add-node>
To the Metadata.xml, zzbfm is the class RemoteMessage relies on & thus is the one that needs to be unobfuscated.
After updating this in my Basement Metadata.xml & rebinding the Firebase.Messaging library RemoteMessage is now properly bound in the library and accessible.
I'd like to implement MvxRecyclerView, but I get following exception during runtime in SetContentView():
System.NotSupportedException: Could not activate JNI Handle 0x32700041
(key_handle 0xb29d17e8) of Java type
'mvvmcross/droid/support/v7/recyclerview/MvxRecyclerView' as managed
type 'MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView'.
I use the latest NuGet packages of Xamarin.Android.Support.. (23.3.0) and MvvmCross (4.1.6 / 4.1.7).
Any idea what causes this exception?
More information now on the issue from Ken Kosmowski:
https://github.com/MvvmCross/MvvmCross-AndroidSupport/issues/252
Use the workaround by #kjeremy referenced there, till the issue got fixed:
"The workaround consists of adding Resources\values\attrs.xml file to your Droid project with the following content:"
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="MvxRecyclerView">
<attr name="MvxItemTemplateSelector" format="string" />
</declare-styleable>
</resources>
For completion purpose.
With MVVMCross 4.2.0
You should remove the attrs.xml and change all of your MvxItemTemplateSelector references to MvxTemplateSelector.
I am trying to write some tests with classes that use roboguice. Unfortunately it seems that guice is not injecting anything at all.
My setup is like so...
I am using Intellij
I have 2 intellij modules (1 for code, 1 for tests)
I have 2 seperate module classes that extends AbstractModule(roboguice class) that define the bindings
I have 2 roboguice.xml files that point to the package like this...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="roboguice_modules">
<item>com.evertaletest</item>
</string-array>
</resources>
Does this setup seems correct? Any class that is injected is null at the moment
I believe the <item> entry needs to give the name of your module class, not simply the package.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="roboguice_modules">
<item>com.evertaletest.YourModuleForCode</item>
</string-array>
</resources>
But that will only be relevant for your production code. For tests, you will have to set your module in code instead of using XML.
In your test setup:
RoboGuice.setBaseApplicationInjector(application,
RoboGuice.DEFAULT_STAGE, new YourModuleForTest());
I've read the documentation about Fragments in the Android Developer Guide and I've seen that sometimes they specify the class to instantiate with the Fragment tag attribute android:name and sometime they use the class: attribute:
<fragment
android:name="com.example.news.ArticleReaderFragment"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment
class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
android:id="#+id/titles"
android:layout_weight="1"
android:layout_width="0px"
android:layout_height="match_parent" />
Are android:name and class: interchangeable? If I use the autocompletion function in Eclipse, they both show the same documentation tip (i.e. the attribute provides the class name to be instantiated). Maybe you must use the second one when the class to be instantiated has a name which is different from the java file name, like TitlesFragment which is in the FragmentLayout.java file? Or can I use the syntax package.fileDOTjava$Class also with the android:name attribute?
I'd like to have some documentation for XML tags and attributes as for Android Java Classes (I've asked about it in another question).
As Activity.onCreateView source says:
String fname = attrs.getAttributeValue(null, "class");
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
if (fname == null) {
fname = a.getString(com.android.internal.R.styleable.Fragment_name);
}
That seemingly means that program looks "class" attribute first. And on fail looks "name" attribute.
So as far as it's true using "class" if more efficient.
Are android:name and class: interchangeable?
Presumably, yes. I have only used class, and that seems to be what most of Google's examples use, but I do see where they use android:name in some samples. Unfortunately, there is no formal and complete documentation for <fragment>.
Sorry all experts are here, I may be wrong but as per my knowledge android:name attribute of fragment is used to find fragment, when we use getFragmentByTag() method of fragmentManager class.
also android:class attribute is used to find fragment class which we generally include for static fragment.
Hope this will help..
thanks
I recently split my project and created a library project and a main project. Having a preferences screen that has custom attributes, i removed the preferences with custom attributes from my preferences.xml, put them into their own xml files, included them back into the preferences.xml file and redefined the individual files in the main project (the process is detailed in the answer to another question here.
The project build and run properly. However, I am getting a RuntimeException whenever i try to open the preferences screen. Removing the prefs with custom attrs fixes the problem, so i have traced it back to there. Unfortunately there is no useful information in the exception.
attrs.xml (exists in lib project)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="numberpickerPref">
<attr name="maxValue" format="integer" />
<attr name="minValue" format="integer" />
</declare-styleable>
</resources>
preferences.xml (also in lib project)
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="#string/pref_VibrationSettingsTitle">
<CheckBoxPreference android:key="#string/pref_vibrateFlagKey"
android:title="#string/pref_VibrateTitle"
android:summary="#string/pref_VibrateSummary"
android:defaultValue="false" />
<include layout="#layout/pref_vibrate_on" />
<include layout="#layout/pref_vibrate_off" />
</PreferenceCategory>
</PreferenceScreen>
pref_vibrate_off.xml (defined in both lib and main projects) (only diff is the my namespace, one points to the lib, the other the main project)
<?xml version="1.0" encoding="utf-8"?>
<!-- Stupid workaround because Android still has a bug where custom attributes in a library cause the executable project
problems when building:
https://stackoverflow.com/questions/4461407/help-with-a-custom-view-attributes-inside-a-android-library-project -->
<com.me.numberpicker.NumberPickerPreference
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:my="http://schemas.android.com/apk/res/com.me.myapp.lib"
android:key="#string/pref_vibrateOffPeriodKey"
android:title="#string/pref_VibrateOffTitle"
android:summary="#string/pref_VibrateOffSummary"
my:maxValue="#string/MaxVibratorOffPeriodInS"
my:minValue="#string/MinVibratorOffPeriodInS"
android:defaultValue="#string/DefaultVibratorOffPeriodInS" />
MyPreferencesActivity.java
public class MegalarmPreferencesActivity extends PreferenceActivity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
All the strings are properly defined in the lib project.
If i'm missing anything, let me know please. The preferences were working fine until i split up my project into a lib and main.
Many thanks!
Sean
PreferenceActivity.onCreate can throw RuntimeExceptions internally that the IDE might catch (if configured to do so), but they are also caught internally. For instance, a missing layout_width tag in the default theme seems to cause an exception.
If the RuntimeException is not percolating back to your code don't worry about it. If it is, could you update your question to include the exception message and stack trace?