[Q] Trouble with replacing boolean resource - Xposed General

I've been trying to disable emoji in Swype. Swype has a boolean resource helpfully called "enable_emoji_english_ldb". The obvious code to disable emoji is:
Code:
@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName(), Main.PREFS);
if (resparam.packageName.startsWith("com.nuance.swype") &&
prefs.getBoolean(Main.PREF_NO_SWYPE_EMOJI, false)
) {
XposedBridge.log("Disable swype emoji");
resparam.res.setReplacement(resparam.packageName, "bool", "enable_emoji_in_english_ldb", false);
}
}
But this doesn't work. I get the "Disable swype emoji" log message, but the resource is not changed, as I can check by hooking Resources.getBoolean() and noting that when it's called by Swype with the relevant resource ID, it returns true. And the emoji remain.
I can, of course, modify the return value of Resources.getBoolean(), and that works fine -- the annoying emoji are disabled -- but I don't understand why the setReplacement() route doesn't work.

Related

Access resources from the module itself

Okay, so I have some resources in my module/app. Say, a Drawable. Unfortunately, when I try to use them (to create a UI, say) from within handleLoadPackage, well, I can't. I can access R.drawable.abc just fine; but for some reason, all I get is a strange white rectangle instead of the actual Drawable.
How do I work around this issue? Thank you!
This is because your hook is not really part of your app - it runs in the hooked app. To access your own resources, you need to create a context for your app first.
You're going to need a general context first. If you can't get any from the class you're hooking, you can try AndroidAppHelper.currentApplication(). Then, use that context to create a context for your own app (see Context.createContext(...)). You can then access your resources using that context.
GermainZ said:
This is because your hook is not really part of your app - it runs in the hooked app. To access your own resources, you need to create a context for your app first.
You're going to need a general context first. If you can't get any from the class you're hooking, you can try AndroidAppHelper.currentApplication(). Then, use that context to create a context for your own app (see Context.createContext(...)). You can then access your resources using that context.
Click to expand...
Click to collapse
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
perseus0807 said:
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
Click to expand...
Click to collapse
Yeah, but that resource ID will always be resolved using the target app's resources. If you can't use the the drawable directly, you can try this:
Code:
private static String MODULE_PATH = null;
private int mFakeId = 0;
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
}
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("your.target.app"))
return;
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
mFakeId = resparam.res.addResource(modRes, R.drawable.ic_launcher);
}
Then use mFakeId in your callback, which should run after the resources have been initialized. addResource() generates an ID for you an sets up a resource replacement for that non-existent ID to the item in your own resources.
rovo89 said:
Yeah, but that resource ID will always be resolved using the target app's resources. If you can't use the the drawable directly, you can try this:
Code:
private static String MODULE_PATH = null;
private int mFakeId = 0;
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;
}
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("your.target.app"))
return;
XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
mFakeId = resparam.res.addResource(modRes, R.drawable.ic_launcher);
}
Then use mFakeId in your callback, which should run after the resources have been initialized. addResource() generates an ID for you an sets up a resource replacement for that non-existent ID to the item in your own resources.
Click to expand...
Click to collapse
This works, thank you so much! Bit stupid of me, I should have inferred this from the tutorial.
perseus0807 said:
I tried this:
Code:
Context c = tv.getContext().createPackageContext("package.name", Context.CONTEXT_IGNORE_SECURITY);
int resId = c.getResources().getIdentifier("ic_launcher", "drawable", "package.name");
dialog.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, resId);
No dice. I'm sure using c.getResources.getDrawable would work, but my dialog method requires the ID, not the Drawable itself.
Click to expand...
Click to collapse
To add to what rovo said, you could've use getDrawable(…) instead of getIdentifier(…) here, which would've given you Drawable (that you can then use) instead of a resource ID that won't work in the hooked app.
Thank you, it works!!

[Q] Still don't know how to reload prefs in realtime

I'm a newbee to xposed module development. I searched sometimes in this forum and viewed some open-source mudules on github, but I still don't know how to reload prefs in my module.
I try to change the setting repeatedly, but logcat always displays that prefs returned a false. My code is here below:
This is the main class loaded by xposed:
Code:
public class Main implements IXposedHookLoadPackage, IXposedHookZygoteInit, IXposedHookInitPackageResources{
private static XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
....
SOME HACKS
....
private BroadcastReceiver xReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
context = mContext;
if (intent.getAction().equals("xxx.xxx.SETTING_CHANGED")){
prefs.makeWorldReadable(); // Wether this line is added or not, the result is same.
prefs.reload();
Log.d(TAG, String.valueOf(prefs.getBoolean("key", false)));
}
}
};
}
This is the setting screen class:
Code:
public class Setting extends PreferenceActivity implements OnSharedPreferenceChangeListener{
ListPreference lp;
ListPreference _lp;
EditTextPreference etp;
CheckBoxPreference cbp;
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.setting);
getPreferenceManager().setSharedPreferencesMode(MODE_WORLD_READABLE);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
}
@SuppressWarnings("deprecation")
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Intent intent = new Intent();
intent.setAction("xxx.xxx.SETTING_CHANGED");
Setting.this,sendBroadcast(intent);
}
From memory, I believe the XSharedPreferences constructor takes the preferences' file name, which is usually something like "com.mypackage_preferences" and not only "com.mypackage".
Note that you don't need to use makeWorldReadable in your module since you're doing that in your preferences activity.
GermainZ said:
From memory, I believe the XSharedPreferences constructor takes the preferences' file name, which is usually something like "com.mypackage_preferences" and not only "com.mypackage".
Note that you don't need to use makeWorldReadable in your module since you're doing that in your preferences activity.
Click to expand...
Click to collapse
Thanks for your reply. But I can't understand clearly and I'm even more confused.
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
This works well. Read preferences correctly.
In the source code of XSharedPreference class. The function "XSharedPreferences(String packageName)" will call "XSharedPreferences(String packageName, String prefFileName)" and complete the default preferences file name. I don't think I need to use full path and file name?
I still don't know how to reload settings correctly. Could you give me a copy of detailed code to work perfectly?
Awating your reply.
neverweep said:
Thanks for your reply. But I can't understand clearly and I'm even more confused.
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
This works well. Read preferences correctly.
In the source code of XSharedPreference class. The function "XSharedPreferences(String packageName)" will call "XSharedPreferences(String packageName, String prefFileName)" and complete the default preferences file name. I don't think I need to use full path and file name?
I still don't know how to reload settings correctly. Could you give me a copy of detailed code to work perfectly?
Awating your reply.
Click to expand...
Click to collapse
If I'm not mistaken, this:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
… will load an "empty" preferences file (because it won't exist) so everything will have the default value. Try something like this instead:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName() + "_preferences");
As for reloading preferences, you seem to be doing it correctly (XSharedPreferences.reload method), it's just that it's not working as intended for the reason explained above.
No, neverweep's code:
Code:
XSharedPreferences prefs = new XSharedPreferences(Main.class.getPackage().getName());
is good. I also use such code in my modules and everything works.
Rovo89 has code to add "_preferences" automatically (link: https://github.com/rovo89/XposedBri...bv/android/xposed/XSharedPreferences.java#L37)
I also had some trouble figuring out how to reload prefs in realtime, but thank's to this thread and to some attempts, I could finally figure it out.
Here's the code if this can help someone (plus the preference management is very simple so it should be a very easy example to follow):
on github (cannot post links yet):
Code:
lrq3000/XposedJitteryGyroFix/blob/14d7e93949bbfd5cfc31ce30482eff9860c35a49/GyroscopeNoiseFilter/src/net/kajos/gyronoisefilter/GyroscopeNoiseFilter.java#L50

access app context

hi all.
i created an xposed module but, inside my app, i also have what a standard android app has, like res/strings.xml.
now, even if the entry point is IXposedHookLoadPackage.handleLoadPackage() and then i don't have an android app context, i sometimes need to access my app package name and to my app strings but context.getPackageName() and context.getResources() don't return what i want.
what's the proper way to do it?
thanks a lot.
Do you already have a context? If not, you can use AndroidAppHelper.currentApplication().
To create a context for your application from an existing context, use Context.createPackageContext.
GermainZ said:
Do you already have a context? If not, you can use AndroidAppHelper.currentApplication().
To create a context for your application from an existing context, use Context.createPackageContext.
Click to expand...
Click to collapse
hi and thanks for your reply.
i tried this code but it returns "android" as the package name:
Code:
Context context = AndroidAppHelper.currentApplication();
String packageName = context.getPackageName();
am i missing something?
This is my way of accessing my app context:
Java:
XposedBridge.hookAllConstructors(<the class you want to hook>, new XC_MethodHook() {
[user=439709]@override[/user]
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
log("initializing");
//The context that you mod (usually android)
//It happens that the constructor of the constructor to be hooked has the context as the first argument
//I think AndroidAppHelper.currentApplication() is a more general way to do it
Context modContext = (Context) param.args[0];
//Context of your app
Context appContext = modContext.createPackageContext(
PACKAGE_NAME, Context.CONTEXT_IGNORE_SECURITY);
Resources modRes = modContext.getResources();
Resources appRes = appContext.getResources();
...
}
});
where PACKAGE_NAME is a static field like this
Java:
public static final String PACKAGE_NAME = <Your class>.class.getPackage().getName();
Not sure if there are other alternatives. This is just my way to doing it in my modules
This helped a lot! Thanks!

Having Trouble Changing a Color

I'm trying to change the color of text from black to white. Modding the app with smali, I have narrowed it down to this portion of code. Where this.b(-16777216); would be the color black.
Code:
@Override
public void t() {
super.t();
this.p.setVisibility(8);
this.q.setVisibility(8);
this.n.setVisibility(8);
this.o.setVisibility(8);
this.u.setVisibility(8);
this.t.setVisibility(8);
this.r.setVisibility(8);
this.w.setVisibility(8);
this.x.setVisibility(8);
this.z.setVisibility(8);
this.C.setVisibility(8);
this.B.setVisibility(8);
this.D.setVisibility(8);
this.i.setVisibility(8);
this.k.setVisibility(8);
this.b(-16777216);
this.B.setOnClickListener(null);
this.D.setOnClickListener(null);
this.I = false;
}
So far this is where I am at. Logs not throwing any errors.
Edit.. I've also placed param.thisObject in place of null with no luck as well.
Code:
public class ClassName implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.app.package"))
return;
findAndHookMethod("com.app.package.blah.blah.Class", lpparam.classLoader, "t", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedHelpers.setIntField(null, "b", Color.WHITE);
}
});
}
}
Try using Xposedhelpers.callMethod(param.thisObject, "b", Color.WHITE) instead of setIntField.
set***Field methods are for the purpose of setting values of field members of object.
"b" is not a field member. It's a method (function).
C3C076 said:
Try using Xposedhelpers.callMethod(param.thisObject, "b", Color.WHITE) instead of setIntField.
set***Field methods are for the purpose of setting values of field members of object.
"b" is not a field member. It's a method (function).
Click to expand...
Click to collapse
OMG THANK YOU! It worked perfectly. I've been trying everything since Monday. Since I know what works and re-reading the section on the wiki about the callMethod helper, it makes a lot more sense now. Thanks again .
C3C076 said:
Try using Xposedhelpers.callMethod(param.thisObject, "b", Color.WHITE) instead of setIntField.
set***Field methods are for the purpose of setting values of field members of object.
"b" is not a field member. It's a method (function).
Click to expand...
Click to collapse
One last thing.. before I spend days trying to figure it out again lol. I'm getting a cannot cast to android....TextView with the following:
Edit: I think it is because I forgot to add the following:
Code:
String text = tv.getText().toString();
tv.setText(text);
I'll test when I get home.
Code:
XposedHelpers.findAndHookMethod("com.app.package.blah.blah.Class", lpparam.classLoader, "a", "com.app.package.blah.blah.AnotherClass", TextView.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
TextView tv = (TextView) param.thisObject;
tv.setTextColor(Color.WHITE);
}
});
This is what the portion of code looks like for the text color I am trying to change: textView.setTextColor(-16777216);
Code:
@Trace
private void a(FeedItem feedItem, TextView textView) {
this.b(chatFeedItem, textView);
if (feedItem instanceof StatefulChatFeedItem && (feedItem.Y() || feedItem.Z())) {
textView.setTextColor(this.b.getResources().getColor(2131230734));
return;
}
textView.setTextColor(-16777216);
}
Nevermind.. didn't work
93Akkord said:
Nevermind.. didn't work
Click to expand...
Click to collapse
Arguments of a function you are hooking are accessed using param.args array, not param.thisObject.
param.thisObject refers to the instance of an object you are working with. In your case,
it's an instance of "com.app.package.blah.blah.Class" class.
So proper way of getting that TextView which is a second argument of a function you are hooking is:
TextView tv = (TextView) param.args[1];
C3C076 said:
Arguments of a function you are hooking are accessed using param.args array, not param.thisObject.
param.thisObject refers to the instance of an object you are working with. In your case,
it's an instance of "com.app.package.blah.blah.Class" class.
So proper way of getting that TextView which is a second argument of a function you are hooking is:
TextView tv = (TextView) param.args[1];
Click to expand...
Click to collapse
Oh wow.. thanks. That worked. I actually tried TextView tv = (TextView) param.args[0];, among numerous other combinations, and never thought to try [1]. It makes me feel happy and sad at the same time lol. I appreciate the help :highfive:
dupe
param.args array is zero based and TextView is a second argument of a function. so:
- param.args[0] is FeedItem argument
- param.args[1] is TextView argument

[Q] How to update a textView every second.

I have a textview that shows your amount of money and i would like the ui to update every second to visually show how much money you have in real time. Would i be able to accomplish this with a timer?, and if so what would it look like.
Hello,
You should do inside your class, outside of any method:
Code:
TextView tv;
String calculatedString;
MyAsyncTask mAsync = null;
Timer timer = null;
TimerTask task = null;
private class MyAsyncTask extends AsyncTask<String, Void, String> {
public MyAsyncTask(){
}
@Override
protected String doInBackground(String... params) {
//Background operation in a separate thread
//Write here your code to run in the background thread
//calculate here whatever you like
calculatedString = ....;
return null;
}
@Override
protected void onPostExecute(String result) {
//Called on Main UI Thread. Executed after the Background operation, allows you to have access to the UI
tv.setText(calculatedString);
}
@Override
protected void onPreExecute() {
//Called on Main UI Thread. Executed before the Background operation, allows you to have access to the UI
}
}
inside the onCreate after super and setContentView:
Code:
tv = (TextView) findViewById(R.id.tv); //your tv id here
final Handler handler = new Handler();
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
public void run() {
MyAsyncTask mAsync = new MyAsyncTask();
mAsync.execute();
}
});
}
};
timer.schedule(task, 0, 1000); //Every 1 second
If you still need help, feel free to ask
I have attached 2 screenshots showing the errors i was given after inputting. Any idea how to fix this?
mmdeveloper10 said:
Hello,
You should do inside your class, outside of any method:
Code:
TextView tv;
String calculatedString;
MyAsyncTask mAsync = null;
Timer timer = null;
TimerTask task = null;
private class MyAsyncTask extends AsyncTask<String, Void, String> {
public MyAsyncTask(){
}
@Override
protected String doInBackground(String... params) {
//Background operation in a separate thread
//Write here your code to run in the background thread
//calculate here whatever you like
calculatedString = ....;
return null;
}
@Override
protected void onPostExecute(String result) {
//Called on Main UI Thread. Executed after the Background operation, allows you to have access to the UI
tv.setText(calculatedString);
}
@Override
protected void onPreExecute() {
//Called on Main UI Thread. Executed before the Background operation, allows you to have access to the UI
}
}
inside the onCreate after super and setContentView:
Code:
tv = (TextView) findViewById(R.id.tv); //your tv id here
final Handler handler = new Handler();
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
handler.post(new Runnable() {
public void run() {
MyAsyncTask mAsync = new MyAsyncTask();
mAsync.execute();
}
});
}
};
timer.schedule(task, 0, 1000); //Every 1 second
If you still need help, feel free to ask
Click to expand...
Click to collapse
Hello,
For the first image:
You have to import the AsyncTask.
add this
Code:
import android.os.AsyncTask;
with the other imports in your java file.
For your second image:
You haven't wrote the line
Code:
setContentView(R.layout.activity_main);
under your super.onCreate(savedInstanceState); and then the code I said above (I said that on my post )
inside onCreate, where activity_main is your xml layout. And you should have inside that layout, a TextView with an id "tv" (or whatever you like)
and then:
Code:
tv = (TextView) findViewById(R.id.tv); //your tv id here
R.id.tv must much the id you have in your layout. Replace it with the actual id of your TextView. If your id is "tv", write R.id.tv, if it is "mytv" write R.id.mytv. ( I said that on my post also)
Can you show your layout file? (XML - your activity_main.xml). You said that you have a TextView Where is your TextView?
Ok I fixed the problems stated and now only have these two errors remaining
mmdeveloper10 said:
Hello,
For the first image:
You have to import the AsyncTask.
add this
Code:
import android.os.AsyncTask;
with the other imports in your java file.
For your second image:
You haven't wrote the line
Code:
setContentView(R.layout.activity_main);
under your super.onCreate(savedInstanceState); and then the code I said above (I said that on my post )
inside onCreate, where activity_main is your xml layout. And you should have inside that layout, a TextView with an id "tv" (or whatever you like)
and then:
Code:
tv = (TextView) findViewById(R.id.tv); //your tv id here
R.id.tv must much the id you have in your layout. Replace it with the actual id of your TextView. If your id is "tv", write R.id.tv, if it is "mytv" write R.id.mytv. ( I said that on my post also)
Can you show your layout file? (XML - your activity_main.xml). You said that you have a TextView Where is your TextView?
Click to expand...
Click to collapse
Have you imported this?
Code:
import java.util.logging.Handler;
If so, change it to
Code:
import android.os.Handler;
Im not at the computer but I think that should solve my issue I will keep you updated
Sent from my HTC6525LVW using XDA Free mobile app
Hey thanks so much its working perfectly now :good:

Categories

Resources