Android - Preferences that work on both 2.2 and 4.0 [duplicate] - sharedpreferences

Trying the different preference activities in the ApiDemos for Android 4.0, I see in the code that some methods are deprecated in PreferencesFromCode.java, for example.
So my question is: if I use PreferenceFragment, will it work for all version or only 3.0 or 4.0 and up?
If so, what should I use that works for 2.2 and 2.3 as well?

PreferenceFragment will not work on 2.2 and 2.3 (only API level 11 and above). If you want to offer the best user experience and still support older Android versions, the best practice here seems to be to implement two PreferenceActivity classes and to decide at runtime which one to invoke. However, this method still includes calling deprecated APIs, but you can't avoid that.
So for instance, you have a preference_headers.xml:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
<header android:fragment="your.package.PrefsFragment"
android:title="...">
<extra android:name="resource" android:value="preferences" />
</header>
</preference-headers>
and a standard preferences.xml (which hasn't changed much since lower API levels):
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:title="...">
...
</PreferenceScreen>
Then you need an implementation of PreferenceFragment:
public static class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
And finally, you need two implementations of PreferenceActivity, for API levels supporting or not supporting PreferenceFragments:
public class PreferencesActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
addPreferencesFromResource(R.xml.other);
}
}
and:
public class OtherPreferencesActivity extends PreferenceActivity {
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_headers, target);
}
}
At the point where you want to display the preference screen to the user, you decide which one to start:
if (Build.VERSION.SDK_INT < 11) {
startActivity(new Intent(this, PreferencesActivity.class));
} else {
startActivity(new Intent(this, OtherPreferencesActivity.class));
}
So basically, you have an xml file per fragment, you load each of these xml files manually for API levels < 11, and both Activities use the same preferences.

#Mef Your answer can be simplified even more so that you do not need both of the PreferencesActivity and OtherPreferencesActivity (having 2 PrefsActivities is a PITA).
I have found that you can put the onBuildHeaders() method into your PreferencesActivity and no errors will be thrown by Android versions prior to v11. Having the loadHeadersFromResource() inside the onBuildHeaders did not throw and exception on 2.3.6, but did on Android 1.6. After some tinkering though, I found the following code will work in all versions so that only one activity is required (greatly simplifying matters).
public class PreferencesActivity extends PreferenceActivity {
protected Method mLoadHeaders = null;
protected Method mHasHeaders = null;
/**
* Checks to see if using new v11+ way of handling PrefFragments.
* #return Returns false pre-v11, else checks to see if using headers.
*/
public boolean isNewV11Prefs() {
if (mHasHeaders!=null && mLoadHeaders!=null) {
try {
return (Boolean)mHasHeaders.invoke(this);
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
return false;
}
#Override
public void onCreate(Bundle aSavedState) {
//onBuildHeaders() will be called during super.onCreate()
try {
mLoadHeaders = getClass().getMethod("loadHeadersFromResource", int.class, List.class );
mHasHeaders = getClass().getMethod("hasHeaders");
} catch (NoSuchMethodException e) {
}
super.onCreate(aSavedState);
if (!isNewV11Prefs()) {
addPreferencesFromResource(R.xml.preferences);
addPreferencesFromResource(R.xml.other);
}
}
#Override
public void onBuildHeaders(List<Header> aTarget) {
try {
mLoadHeaders.invoke(this,new Object[]{R.xml.pref_headers,aTarget});
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
}
}
This way you only need one activity, one entry in your AndroidManifest.xml and one line when you invoke your preferences:
startActivity(new Intent(this, PreferencesActivity.class);
UPDATE Oct 2013:
Eclipse/Lint will warn you about using the deprecated method, but just ignore the warning. We are using the method only when we have to, which is whenever we do not have v11+ style preferences and must use it, which is OK. Do not be frightened about Deprecated code when you have accounted for it, Android won’t remove deprecated methods anytime soon. If it ever did occur, you won’t even need this class anymore as you would be forced to only target newer devices. The Deprecated mechanism is there to warn you that there is a better way to handle something on the latest API version, but once you have accounted for it, you can safely ignore the warning from then on. Removing all calls to deprecated methods would only result in forcing your code to only run on newer devices — thus negating the need to be backward compatible at all.

There's a newish lib that might help.
UnifiedPreference is a library for working with all versions of the
Android Preference package from API v4 and up.

Problem with previous answers is that it will stack all preferences to a single screen on pre-Honecomb devices (due to multiple calls of addPreferenceFromResource()).
If you need first screen as list and then the screen with preferences (such as using preference headers), you should use Official guide to compatible preferences

I wanted to point out that if you start at http://developer.android.com/guide/topics/ui/settings.html#PreferenceHeaders and work your way down to the section for "Supporting older versions with preference headers" it will make more sense. The guide there is very helpful and does work well. Here's an explicit example following their guide:
So start with file preference_header_legacy.xml for android systems before HoneyComb
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:title="OLD Test Title"
android:summary="OLD Test Summary" >
<intent
android:targetPackage="example.package"
android:targetClass="example.package.SettingsActivity"
android:action="example.package.PREFS_ONE" />
</Preference>
Next create file preference_header.xml for android systems with HoneyComb+
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="example.package.SettingsFragmentOne"
android:title="NEW Test Title"
android:summary="NEW Test Summary" />
</preference-headers>
Next create a preferences.xml file to hold your preferences...
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<CheckBoxPreference
android:key="pref_key_auto_delete"
android:summary="#string/pref_summary_auto_delete"
android:title="#string/pref_title_auto_delete"
android:defaultValue="false" />
</PreferenceScreen>
Next create the file SettingsActivity.java
package example.project;
import java.util.List;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class SettingsActivity extends PreferenceActivity{
final static String ACTION_PREFS_ONE = "example.package.PREFS_ONE";
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String action = getIntent().getAction();
if (action != null && action.equals(ACTION_PREFS_ONE)) {
addPreferencesFromResource(R.xml.preferences);
}
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Load the legacy preferences headers
addPreferencesFromResource(R.xml.preference_header_legacy);
}
}
#SuppressLint("NewApi")
#Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.preference_header, target);
}
}
Next create the class SettingsFragmentOne.java
package example.project;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.preference.PreferenceFragment;
#SuppressLint("NewApi")
public class SettingsFragmentOne extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
AndroidManifest.xml, added this block between my <application> tags
<activity
android:label="#string/app_name"
android:name="example.package.SettingsActivity"
android:exported="true">
</activity>
and finally, for the <wallpaper> tag...
<wallpaper xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/description"
android:thumbnail="#drawable/ic_thumbnail"
android:settingsActivity="example.package.SettingsActivity"
/>

I am using this library, which has an AAR in mavenCentral so you can easily include it if you are using Gradle.
compile 'com.github.machinarius:preferencefragment:0.1.1'

Related

Flurry push - issue with Kotlin

I am not finding an easy way to integrate Flurry push with Kotlin.
I added the first parts of the auto installation. and I get red lines under key parts of the script.
Mainly .withFlurryMessagingListener(flurryMessagingListener)
seems it can't find flurryMessagingListener
val flurryMessagingOptions = FlurryMarketingOptions.Builder()
.setupMessagingWithAutoIntegration()
.withDefaultNotificationChannelId()
.withDefaultNotificationIconResourceId(R.drawable.ic_dialog_alert)
.withDefaultNotificationIconAccentColor()
.withFlurryMessagingListener(flurryMessagingListener)
.build()
The other issue is I don't want to put an .withDefaultNotificationChannelId(). According to the how to on their website - which seem out of date. I don't need to yet it tells me I have too.
Question why could this not be as easy as iOS version - that was a lot easier to install. But if anyone has a how to install with Kotlin - since Flurry support has not gotten back to me I would be grateful.
You need to define your listener. E.g.,
import com.flurry.android.marketing.messaging.FlurryMessagingListener;
FlurryMessagingListener flurryMessagingListener = new FlurryMessagingListener() {
#Override
public boolean onNotificationReceived(FlurryMessage flurryMessage) {
return false;
}
#Override
public boolean onNotificationClicked(FlurryMessage flurryMessage) {
return false;
}
#Override
public void onNotificationCancelled(FlurryMessage flurryMessage) {
}
#Override
public void onTokenRefresh(String s) {
}
#Override
public void onNonFlurryNotificationReceived(Object o) {
}
};
And No, it's not required to define your own channel ID (by withDefaultNotificationChannelId). Flurry SDK will apply the default if no explicitly defined.

Is it possible to add completion items to a Microsoft Language Server in runtime?

I am trying to develop a IntelliJ plugin which provides a Language Server with help of lsp4intellij by ballerina.
Thing is, i've got a special condition: The list of completion items should be editable in runtime.
But I've not found any way to communicate new completionItems to the LanguageServer process once its running.
My current idea is to add an action to the plugin which builds a new jar and then restarts the server with the new jar, using the Java Compiler API.
The problem with that is, i need to get the source code from the plugin project including the gradle dependencies accessable from the running plugin... any ideas?
If your requirement is to modify the completion items (coming from the language server) before displaying them in the IntelliJ UI, you can do that by implementing the LSP4IntelliJ's
LSPExtensionManager in your plugin.
Currently, we do not have proper documentation for the LSP4IntelliJ's extension points but you can refer to our Ballerina IntelliJ plugin as a reference implementation, where it has implemented Ballerina LSP Extension manager to override/modify completion items at the client runtime in here.
For those who might stumble upon this - it is indeed possible to change the amount of CompletionItems the LanguageServer can provide during runtime.
I simply edited the TextDocumentService.java (the library I used is LSP4J).
It works like this:
The main function of the LanguageServer needs to be started with an additional argument, which is the path to the config file in which you define the CompletionItems.
Being called from LSP4IntelliJ it would look like this:
String[] command = new String[]{"java", "-jar",
"path\\to\\LangServer.jar", "path\\to\\config.json"};
IntellijLanguageClient.addServerDefinition(new RawCommandServerDefinition("md,java", command));
The path String will then be passed through to the Constructor of your CustomTextDocumentServer.java, which will parse the config.json in a new Timer thread.
An Example:
public class CustomTextDocumentService implements TextDocumentService {
private List<CompletionItem> providedItems;
private String pathToConfig;
public CustomTextDocumentService(String pathToConfig) {
this.pathToConfig = pathToConfig;
Timer timer = new Timer();
timer.schedule(new ReloadCompletionItemsTask(), 0, 10000);
loadCompletionItems();
}
#Override
public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completion(CompletionParams completionParams) {
return CompletableFuture.supplyAsync(() -> {
List<CompletionItem> completionItems;
completionItems = this.providedItems;
// Return the list of completion items.
return Either.forLeft(completionItems);
});
}
#Override
public void didOpen(DidOpenTextDocumentParams didOpenTextDocumentParams) {
}
#Override
public void didChange(DidChangeTextDocumentParams didChangeTextDocumentParams) {
}
#Override
public void didClose(DidCloseTextDocumentParams didCloseTextDocumentParams) {
}
#Override
public void didSave(DidSaveTextDocumentParams didSaveTextDocumentParams) {
}
private void loadCompletionItems() {
providedItems = new ArrayList<>();
CustomParser = new CustomParser(pathToConfig);
ArrayList<String> variables = customParser.getTheParsedItems();
for(String variable : variables) {
String itemTxt = "$" + variable + "$";
CompletionItem completionItem = new CompletionItem();
completionItem.setInsertText(itemTxt);
completionItem.setLabel(itemTxt);
completionItem.setKind(CompletionItemKind.Snippet);
completionItem.setDetail("CompletionItem");
providedItems.add(completionItem);
}
}
class ReloadCompletionItemsTask extends TimerTask {
#Override
public void run() {
loadCompletionItems();
}
}
}

textView.setMovementMethod in Android data binding

I want to achieve clickable link in a textview. I am now migrating all my UI to Android data binding and was wondering how to achieve it.
textView.setMovementMethod.
Any suggestions?
Best,
SK
I found out a way to do it. here it is
Create a static method and add BindingAdapter annotation preferably in a separate class
#BindingAdapter("app:specialtext")
public static void setSpecialText(TextView textView, SpecialText specialText) {
SpannableString ss = new SpannableString(specialText.getText());
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
Log.d("tag", "onSpecialClick: ");
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(true);
}
};
ss.setSpan(clickableSpan, specialText.getStartIndex(), ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setText(ss);
}
In your binding layout file
<variable
name="specialText"
type="com.example.test.data.SpecialText" />
<TextView
android:id="#+id/st_textView"
app:specialtext="#{specialText}"/>
In your activity or fragment where you are using the layout
SpecialText specialText = new TCText();
specialText.setStartIndex(15);
specialText.setText(getActivity().getString(R.string.special_text));
binding.setSpecialText(tcText);
Let me know if you know any better way to do it. Thanks !

Gradle : how to use BuildConfig in an android-library with a flag that gets set in an app

My (gradle 1.10 and gradle plugin 0.8)-based android project consists of a big android-library that is a dependency for 3 different android-apps
In my library, I would love to be able to use a structure like this
if (BuildConfig.SOME_FLAG) {
callToBigLibraries()
}
as proguard would be able to reduce the size of the produced apk, based on the final value of SOME_FLAG
But I can't figure how to do it with gradle as :
* the BuildConfig produced by the library doesn't have the same package name than the app
* I have to import the BuildConfig with the library package in the library
* The apk of an apps includes the BuildConfig with the package of the app but not the one with the package of the library.
I tried without success to play with BuildTypes and stuff like
release {
// packageNameSuffix "library"
buildConfigField "boolean", "SOME_FLAG", "true"
}
debug {
//packageNameSuffix "library"
buildConfigField "boolean", "SOME_FLAG", "true"
}
What is the right way to builds a shared BuildConfig for my library and my apps whose flags will be overridden at build in the apps?
As a workaround, you can use this method, which uses reflection to get the field value from the app (not the library):
/**
* Gets a field from the project's BuildConfig. This is useful when, for example, flavors
* are used at the project level to set custom fields.
* #param context Used to find the correct file
* #param fieldName The name of the field-to-access
* #return The value of the field, or {#code null} if the field is not found.
*/
public static Object getBuildConfigValue(Context context, String fieldName) {
try {
Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
To get the DEBUG field, for example, just call this from your Activity:
boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");
I have also shared this solution on the AOSP Issue Tracker.
Update: With newer versions of the Android Gradle plugin publishNonDefault is deprecated and has no effect anymore. All variants are now published.
The following solution/workaround works for me. It was posted by some guy in the google issue tracker:
Try setting publishNonDefault to true in the library project:
android {
...
publishNonDefault true
...
}
And add the following dependencies to the app project that is using the library:
dependencies {
releaseCompile project(path: ':library', configuration: 'release')
debugCompile project(path: ':library', configuration: 'debug')
}
This way, the project that uses the library includes the correct build type of the library.
You can't do what you want, because BuildConfig.SOME_FLAG isn't going to get propagated properly to your library; build types themselves aren't propagated to libraries -- they're always built as RELEASE. This is bug https://code.google.com/p/android/issues/detail?id=52962
To work around it: if you have control over all of the library modules, you could make sure that all the code touched by callToBigLibraries() is in classes and packages that you can cleave off cleanly with ProGuard, then use reflection so that you can access them if they exist and degrade gracefully if they don't. You're essentially doing the same thing, but you're making the check at runtime instead of compile time, and it's a little harder.
Let me know if you're having trouble figuring out how to do this; I could provide a sample if you need it.
I use a static BuildConfigHelper class in both the app and the library, so that I can have the packages BuildConfig set as final static variables in my library.
In the application, place a class like this:
package com.yourbase;
import com.your.application.BuildConfig;
public final class BuildConfigHelper {
public static final boolean DEBUG = BuildConfig.DEBUG;
public static final String APPLICATION_ID = BuildConfig.APPLICATION_ID;
public static final String BUILD_TYPE = BuildConfig.BUILD_TYPE;
public static final String FLAVOR = BuildConfig.FLAVOR;
public static final int VERSION_CODE = BuildConfig.VERSION_CODE;
public static final String VERSION_NAME = BuildConfig.VERSION_NAME;
}
And in the library:
package com.your.library;
import android.support.annotation.Nullable;
import java.lang.reflect.Field;
public class BuildConfigHelper {
private static final String BUILD_CONFIG = "com.yourbase.BuildConfigHelper";
public static final boolean DEBUG = getDebug();
public static final String APPLICATION_ID = (String) getBuildConfigValue("APPLICATION_ID");
public static final String BUILD_TYPE = (String) getBuildConfigValue("BUILD_TYPE");
public static final String FLAVOR = (String) getBuildConfigValue("FLAVOR");
public static final int VERSION_CODE = getVersionCode();
public static final String VERSION_NAME = (String) getBuildConfigValue("VERSION_NAME");
private static boolean getDebug() {
Object o = getBuildConfigValue("DEBUG");
if (o != null && o instanceof Boolean) {
return (Boolean) o;
} else {
return false;
}
}
private static int getVersionCode() {
Object o = getBuildConfigValue("VERSION_CODE");
if (o != null && o instanceof Integer) {
return (Integer) o;
} else {
return Integer.MIN_VALUE;
}
}
#Nullable
private static Object getBuildConfigValue(String fieldName) {
try {
Class c = Class.forName(BUILD_CONFIG);
Field f = c.getDeclaredField(fieldName);
f.setAccessible(true);
return f.get(null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Then, anywhere in your library where you want to check BuildConfig.DEBUG, you can check BuildConfigHelper.DEBUG and access it from anywhere without a context, and the same for the other properties. I did it this way so that the library will work with all my applications, without needing to pass a context in or set the package name some other way, and the application class only needs the import line changed to suit when adding it into a new application
Edit: I'd just like to reiterate, that this is the easiest (and only one listed here) way to get the values to be assigned to final static variables in the library from all of your applications without needing a context or hard coding the package name somewhere, which is almost as good as having the values in the default library BuildConfig anyway, for the minimal upkeep of changing that import line in each application.
For the case where the applicationId is not the same as the package (i.e. multiple applicationIds per project) AND you want to access from a library project:
Use Gradle to store the base package in resources.
In main/AndroidManifest.xml:
android {
applicationId "com.company.myappbase"
// note: using ${applicationId} here will be exactly as above
// and so NOT necessarily the applicationId of the generated APK
resValue "string", "build_config_package", "${applicationId}"
}
In Java:
public static boolean getDebug(Context context) {
Object obj = getBuildConfigValue("DEBUG", context);
if (obj instanceof Boolean) {
return (Boolean) o;
} else {
return false;
}
}
private static Object getBuildConfigValue(String fieldName, Context context) {
int resId = context.getResources().getIdentifier("build_config_package", "string", context.getPackageName());
// try/catch blah blah
Class<?> clazz = Class.forName(context.getString(resId) + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
}
use both
my build.gradle
// ...
productFlavors {
internal {
// applicationId "com.elevensein.sein.internal"
applicationIdSuffix ".internal"
resValue "string", "build_config_package", "com.elevensein.sein"
}
production {
applicationId "com.elevensein.sein"
}
}
I want to call like below
Boolean isDebug = (Boolean) BuildConfigUtils.getBuildConfigValue(context, "DEBUG");
BuildConfigUtils.java
public class BuildConfigUtils
{
public static Object getBuildConfigValue (Context context, String fieldName)
{
Class<?> buildConfigClass = resolveBuildConfigClass(context);
return getStaticFieldValue(buildConfigClass, fieldName);
}
public static Class<?> resolveBuildConfigClass (Context context)
{
int resId = context.getResources().getIdentifier("build_config_package",
"string",
context.getPackageName());
if (resId != 0)
{
// defined in build.gradle
return loadClass(context.getString(resId) + ".BuildConfig");
}
// not defined in build.gradle
// try packageName + ".BuildConfig"
return loadClass(context.getPackageName() + ".BuildConfig");
}
private static Class<?> loadClass (String className)
{
Log.i("BuildConfigUtils", "try class load : " + className);
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
private static Object getStaticFieldValue (Class<?> clazz, String fieldName)
{
try { return clazz.getField(fieldName).get(null); }
catch (NoSuchFieldException e) { e.printStackTrace(); }
catch (IllegalAccessException e) { e.printStackTrace(); }
return null;
}
}
For me this is the ONLY ONE AND ACCEPTABLE* SOLUTION TO determine the ANDROID APPLICATION BuildConfig.class:
// base entry point
// abstract application
// which defines the method to obtain the desired class
// the definition of the application is contained in the library
// that wants to access the method or in a superior library package
public abstract class BasApp extends android.app.Application {
/*
* GET BUILD CONFIG CLASS
*/
protected Class<?> getAppBuildConfigClass();
// HELPER METHOD TO CAST CONTEXT TO BASE APP
public static BaseApp getAs(android.content.Context context) {
BaseApp as = getAs(context, BaseApp.class);
return as;
}
// HELPER METHOD TO CAST CONTEXT TO SPECIFIC BASEpp INHERITED CLASS TYPE
public static <I extends BaseApp> I getAs(android.content.Context context, Class<I> forCLass) {
android.content.Context applicationContext = context != null ?context.getApplicationContext() : null;
return applicationContext != null && forCLass != null && forCLass.isAssignableFrom(applicationContext.getClass())
? (I) applicationContext
: null;
}
// STATIC HELPER TO GET BUILD CONFIG CLASS
public static Class<?> getAppBuildConfigClass(android.content.Context context) {
BaseApp as = getAs(context);
Class buildConfigClass = as != null
? as.getAppBuildConfigClass()
: null;
return buildConfigClass;
}
}
// FINAL APP WITH IMPLEMENTATION
// POINTING TO DESIRED CLASS
public class MyApp extends BaseApp {
#Override
protected Class<?> getAppBuildConfigClass() {
return somefinal.app.package.BuildConfig.class;
}
}
USAGE IN LIBRARY:
Class<?> buildConfigClass = BaseApp.getAppBuildConfigClass(Context);
if(buildConfigClass !- null) {
// do your job
}
*there are couple of things need to be watched out:
getApplicationContext() - could return a context which is not an App ContexWrapper implementation - see what Applicatio class extends & get to know of the possibilities of context wrapping
the class returned by final app could be loaded by different class loaders than those who will use it - depends of loader implementation and some principals typical (chierarchy, visibility) for loaders
everything depends on the implemmentation of as in this case simple DELEGATION!!! - the solution could be more sophisticetaded - i wanted only to show here the usage of DELEGATION pattern :)
** why i downwoted all of reflection based patterns because they all have weak points and they all in some certain conditions will fail:
Class.forName(className); - because of not speciified loader
context.getPackageName() + ".BuildConfig"
a) context.getPackageName() - "by default - else see b)" returns not package defined in manifest but application id (somtimes they both are the same), see how the manifest package property is used and its flow - at the end apt tool will replace it with applicaton id (see ComponentName class for example what the pkg stands for there)
b) context.getPackageName() - will return what the implementaio wants to :P
*** what to change in my solution to make it more flawless
replace class with its name that will drop the problems wchich could appear when many classes loaded with different loaders accessing / or are used to obtain a final result involving class (get to know what describes the equality between two classes (for a compiler at runtime) - in short a class equality defines not a self class but a pair which is constituted by the loader and the class. (some home work - try load a inner class with different loader and access it by outer class loaded with different loader) - it would turns out that we will get illegal access error :) even the inner class is in the same package has all modificators allowing access to it outer class :) compiler/linker "VM" treats them as two not related classes...

IWorkbenchPart.openEditor() not opening custom editor

I'm designing an Eclipse plugin designed around a new perspective with an editor that stores code/comment snippets upon highlighting them. The parts to it include: the perspective, the editor, and a mouselistener.
I have the perspective made and can open it. I have the editor class code constructed, however, on programmatically opening the editor via IWorkbenchPart.openEditor() my custom editor does not seem to be initialized in any way. Only the default Eclipse editor appears. I can tell because my custom mouse events do not fire.
I used the vogella tutorial as a reference.
Why is my editor's init() method not being called upon being opened? I can tell it is not since the print statement in both init() and createPartControl() are not executed.
In googling this problem, I found a number of hits but they all revolved around error messages encountered (can't find editor, can't find file, ...). I am getting no error messages, just unexpected behaviour. So those articles were useless.
(I would ideally like a TextViewer instead, since I don't want them editing the contents in this mode anyway, but I decided to start here.)
Code below.
Perspective:
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPerspectiveFactory;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
public class PluginPerspective implements IPerspectiveFactory {
#Override
public void createInitialLayout(IPageLayout layout) {
layout.setEditorAreaVisible(true);
layout.setFixed(true);
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IEditorInput iei = page.getActiveEditor().getEditorInput();
try
{
// Open Editor code nabbed from Vogella tutorial.
// He creates an action to do so - I force it to happen when the
// perspective is created.
// I get the name of the current open file as expected.
System.out.println(iei.getName());
page.openEditor(iei, myplugin.PluginEditor.ID, true);
// This message prints, as expected.
System.out.println("open!");
} catch (PartInitException e) {
throw new RuntimeException(e);
}
}
}
Editor: (Removed the other basic editor stubs (isDirty, doSave) since they are not pertinent)
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.texteditor.ITextEditor;
public class PluginEditor extends EditorPart implements MouseListener {
public static final String ID = "myplugin.plugineditor";
#Override
public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
// TODO Auto-generated method stub
System.out.println("editor init!");
setSite(site);
setInput(input);
}
#Override
public void createPartControl(Composite parent) {
// TODO Auto-generated method stub
System.out.println("editor partcontrol!");
//TextViewer tv = new TextViewer(parent, 0);
//tv.setDocument(getCurrentDocument());
}
#Override
public void mouseDoubleClick(MouseEvent e) {
// TODO Auto-generated method stub
// nothing?
}
#Override
public void mouseDown(MouseEvent e) {
// TODO Auto-generated method stub
// grab start location?
System.out.println("down!");
}
#Override
public void mouseUp(MouseEvent e) {
// TODO Auto-generated method stub
// do stuff!
System.out.println("up!");
}
// to be used for grabbing highlight-selection grabbing later
public IDocument getCurrentDocument() {
final IEditorPart editor = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
.getActiveEditor();
if (!(editor instanceof ITextEditor)) return null;
ITextEditor ite = (ITextEditor)editor;
IDocument doc = ite.getDocumentProvider().getDocument(ite.getEditorInput());
return doc;
//return doc.get();
}
}
Have you registered your editor within your plugin.xml?
<extension
point="org.eclipse.ui.editors">
<editor
default="false"
id="myplugin.plugineditor"
name="name">
</editor>
</extension>
Also, you may want to implement IEditorInput to have specific input for your editor.