I've been trying workarounds/dealing with this problem for weeks and I can't seem to get around it any longer. I have an Android Application that needs to change the device's name before creating a Peer to Peer network.
Because the method is hidden in Android's SDK, I am using reflection. The method I am trying to reflect is located here at line 1305: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
Here is my code trying the reflection:
public class HostStagingActivity extends Activity
{
WifiP2pManager.Channel myChannel; //This channel is created and passed to system services in order for WIFI_P2P to work
WifiP2pManager myManager; //This manager is declared to get the required channel object
...
#Override
protected void onCreate(Bundle savedInstanceState)
{
myManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);//passed to the myReceiver listener class
myChannel = myManager.initialize(this, getMainLooper(), null);//passed to the myReceiver listener class
...
Method method1 = null;
try
{
method1 = myManager.getClass().getMethod("setDeviceName", new Class[] {WifiP2pManager.Channel.class, String.class, WifiP2pManager.ActionListener.class });
method1.invoke(myChannel, NETWORK_NAME, new WifiP2pManager.ActionListener()
{
public void onSuccess()
{
Toast.makeText(getApplicationContext(), "DeviceName Changed Successfully!", Toast.LENGTH_SHORT).show();
//Code for Success in changing name
}
public void onFailure(int reason)
{
Toast.makeText(getApplicationContext(), "DeviceName Change Returned Failure!", Toast.LENGTH_SHORT).show();
//Code to be done while name change Fails
}
});
}
However, this causes runtime error:
Caused by: java.lang.IllegalArgumentException: expected receiver of type android.net.wifi.p2p.WifiP2pManager, but got android.net.wifi.p2p.WifiP2pManager$Channel
at java.lang.reflect.Method.invokeNative(Native Method)
Why is the method expecting a WifiP2pManager object when I am clearly passing it a WifiP2pManager.Channel class in my code? Just for fun, when I pass it the arguements it expects, it claims the method was expecting three arguements and I only gave it two.
Can anybody with more reflection experience help me out?
Related
I'm trying to make some utils functions to use in a bigger app later(download file from url, upload file to url etc)
So in MainActivity I have only 2 buttons that on click call static methods from Utils class.
However, I want on MainActivity to have some indicators of how things working on download/upload methods(connecting, connection success/fail, percent of download etc) so I put on MainActivity a TextView that will show that. I made an interface ICallback that contains void setConnectionStatus(String status) and from Utils class I use this to send to MainActivity the status.
Here are some parts of the code :
public class MainActivity extends AppCompatActivity implements ICallback {
Button btnDownloadDB, btnUploadDB, btnUploadPics;
TextView txtStatus;
ProgressBar pb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize stuffs
initViews();
//Setting listeners
btnDownloadDB.setOnClickListener(v -> {
txtStatus.setText(R.string.connecting);
pb.setVisibility(View.VISIBLE);
Utils.downloadFile(DOWNLOAD_DB, DB_FILE_NAME);
});
}
#Override
public void setConnectionStatus(String status) {
Log.d("MIHAI", status);
txtStatus.setText(status);
}
The interface :
public interface ICallback {
void setConnectionStatus(String status); }
And the Utils class :
public class Utils {
static ICallback callback= new MainActivity();
public static void downloadFile(String downloadURL, String fileName) {
IFileTransferClient client = ServiceGenerator.createService(IFileTransferClient.class);
Call<ResponseBody> responseBodyCall = client.downloadFile(downloadURL);
responseBodyCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d("MIHAI", "connection ok");
callback.setConnectionStatus("Connection successful");
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.d("MIHAI", "err...fail");
callback.setConnectionStatus("Connection failed. Check internet connection.");
}
});
}
The problem appear on MainActivity, when I try to set text of the txtStatus TextView getting a null reference error even if the txtStatus is initialized on initViews() method.
The Logs are working fine so I get the right status in MainActivity. I tried to initialize the TextView again in that function before seting the text and im getting : "java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:183)"
Is there any chance to make this work?
Thank you for reading.
Kind regards,
Mihai
There are multiple problems with your solution but the main one is this line:
static ICallback callback= new MainActivity();
First of all, never hold a static reference to Activity, Fragment, Context or any Context related classes. These classes are either bound to a Context or represent the Context itself. You may leak memory this way. But that is the other problem.
What is the actual problem in your code is that new MainActivity() in Utils class creates an absolutely different instance of MainActivity that has nothing to do with MainActivity that is responsible for displaying your UI in the runtime.
What you should do instead is pass an instance of ICallback to the function as an argument:
public static void downloadFile(String downloadURL, String fileName, ICallback callback) {
...
}
And remove static ICallback callback= new MainActivity();.
Note: when you pass a callback object to a function make sure when it is called your Activity is not in a finished state.
I wrote some code to get data from Firebase, but it shows some errors. I will explain the errors below and attach a picture.
First error when I put the mouse on it, message show:
'onStart()' in 'com.abdullrahman.eng.myapp.FriendsFragment' clashes with 'onStart()' in 'android.app.Fragment'; attempting to assign weaker access privileges ('protected'); was 'public'
Second error when I put the mouse on it, message show:
Method does not override method from its superclass
Can anyone solve these problems and help me?
The code:
#Override
protected void onStart() {
super.onStart();
FirebaseRecyclerAdapter<Friends, FriendsViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Friends, FriendsViewHolder>(
Friends.class,
R.layout.users_single_layout,
FriendsViewHolder.class,
mUsersDatabase
) {
#Override
protected void populateViewHolder(FriendsViewHolder friendsViewHolder, Friends friends, int position) {
friendsViewHolder.setDisplayName(friends.getName());
friendsViewHolder.setUserStatus(friends.getStatus());
friendsViewHolder.setUserImage(friends.getThumb_image(), mMainView.getContext());
final String user_id = getRef(position).getKey();
friendsViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent profileIntent = new Intent(getActivity(), UserProfile.class);
profileIntent.putExtra("user_id", user_id);
startActivity(profileIntent);
}
});
}
};
mUsersList.setAdapter(firebaseRecyclerAdapter);
}
Errors do say a lot about the problem you have.
The first error is caused by protected access modifier on the onStart() method, while it should be public.
It should be
#Override
public void onStart() {
super.onStart();
// rest of the code
}
instead of
#Override
protected void onStart() {
super.onStart();
// rest of the code
}
You can find some more information about the error reason in docs available on Oracle site.
The second problem is related to the definition of FirebaseRecyclerAdapter. Looks like there is no method like
protected void populateViewHolder(FriendsViewHolder friendsViewHolder, Friends friends, int position).
I'd suggest checking docs/sources of this class to get info about how the override method should look for the version of Firebase you are using in your project.
Also, as I can see you are using IntelliJ IDEA or some similar IDE, so you can use built-in feature to implement/override the correct method.
Change your protected modifier to public and it'll go away.
In Java, the overriding method can have the same or weaker access compared to the parent method... Here the parent class declares the method as public so you have no other choice but to override it with a public access modifier as it's the weakest access possible.
I'm programming an app with that Nuance SpeechKit that performs TTS. It requires the context (called from getApplicationContext()) to be passed into some functions. Unfortunately, I'm getting this error in my log: ANDROID_CONTEXT parameter is not passed in!!!
Let me give more background: There in a main activity, and it opens a dialog from a button. The dialog invokes the text-to-speech functionality. As a result, I call getApplicationContext() in the main activity and pass it to my DialogFragment as a parameter using setters. Unfortunately, I'm getting this error even though I am calling the setters. So what could be going wrong? Here's a bit of code:
In my main activity:
// Instance variables...
private SpeechKit speechKit;
private Context context;
protected void onCreate(Bundle savedInstanceState) {
...
context = getApplicationContext();
...
this.speechKit = SpeechKit.initialize(context,
"CORRECT_API_KEY",
"sslsandbox.nmdp.nuancemobility.net",
443,
true,
SpeechKitAPIKey);
speechKit.connect();
}
public void invokeDialog() {
...
dialogueFragment.setContext(context);
dialogueFragment.setSpeechKit(speechKit);
...
}
And here's my code for the dialog fragment:
public void setSpeechKit(SpeechKit speechKit) {
this.speechKit = speechKit;
}
private SpeechKit speechKit;
private Context context;
public void setSpeechKit(SpeechKit speechKit) {
this.speechKit = speechKit;
}
public void setContext(Context context) {
this.context = context;
}
// Called when a button is pushed...
public void narrateText(String voice, String phrase) {
Vocalizer vocalizer = speechKit.createVocalizerWithVoice(voice, this, handler);
vocalizer.speakString(phrase, context);
}
Now I have no idea why this error is called. The code compiles fine. Any suggestions please?
I had the same issue, turned out to be that my free account expired (it lasts 90 days).
You probably send the login that is not valid anymore.
I managed to get my headset buttons get recognized by my app when pressed, but one of the buttons needs to call a method that's in MyCustomActivity. The problem is onReceive's 1st parameter is a Context that cannot be cast to Activity and using a MyCustomActivity's inner class won't work in Android 4.1 unless it is static (which has the same problem of inability to access MyCustomActivity's method.
So the only option left for me (in order to support both 2.x and 4.1) is to pass the activity as a parameter to RemoteControlReceiver.
But how do I do that, when the only way to instantiate it is via:
private ComponentName mRemoteControlReceiver = new ComponentName(this, RemoteControlReceiver.class);
Which doesn't accept any additional parameters?
Any idea how to work around this limitation?
Note: If I try to define RemoteControlReceiver as having a constructor with a parameter, I receive the following exception:
E/AndroidRuntime(2836): java.lang.RuntimeException: Unable to instantiate receiver com.example.RemoteControlReceiver: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor
Caused by:
E/AndroidRuntime(2836): Caused by: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor
E/AndroidRuntime(2836): at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(2836): at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(2836): at android.app.ActivityThread.handleReceiver(ActivityThread.java:2205)
So it is clear that this new registerMediaButtonEventReceiver requirement (introduced in Android 4.1) expects an empty constructor.
Is there no way to work around this?
For example, is there a way to get a reference to the actual RemoteControlReceiver object (instantiated indirectly via mAudioManager.registerMediaButtonEventReceiver())? So that I can use an accessor to set a data-member of RemoteControlReceiver after it has been instantiated?
registerMediaButtonEventReceiver requires the BroadcastReceiver to be declared in the application manifest. This means that the receiver must be a standalone class, meaning it knows nothing about your current activity or service.
In order to get this message to your activity or service, you have a number of options:
Use a static global for the activity or service so the receiver can forward the message to it. This is generally not a good idea as it leads to leaks and isn't very adaptable when you want to change the code later. Statics are generally to be avoided.
Re-broadcast the message to a specific class, which happens to be an inner class of the activity or service you want to invoke. E.g. in the BroadcastReceiver for registerMediaButtonEventReceiver:
// Standalone class, declared in the manifest
public class ButtonReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
Intent intent = new Intent();
intent.setAction("com.foo.ACTION");
// Rebroadcasts to your own receiver.
// This receiver is not exported; it'll only be received if the receiver is currently registered.
context.sendBroadcast(intent);
}
}
And in your activity:
class MyActivity extends Activity {
private BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
MyActivity.this.onMessageReceived();
}
}
#Override
protected void onResume() {
registerReceiver(myReceiver, new IntentFilter("com.foo.ACTION"));
}
#Override
protected void onPause() {
unregisterReceiver(myReceiver);
}
private void onMessageReceived() {
}
}
Similar to the above method, it doesn't necessarily have to be a broadcast, it could be an Intent passed to the activity, depending on your use case. To do this instead of using sendBroadcast, you'd use startActivity (or startService if you're using a service).
So I have a bit of a problem. I am trying to make my app do things based on the message it receives through GCM. In this case it's supposed to make a sound by using the TextToSpeech class. It kind of works, but not the first time I send the message. I realise this is probably because TextToSpeech hasn't been instantiated, but I'm not sure how go to about and do that? I tried onInit(), but that didn't work at all.
Also, what is the best way to shut down TTS in my example?
Disclaimer: I come from a PHP background, and know very little Java. I try to learn by doing, so please forgive me if this is a silly question. Thanks in advance!
public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = "GCMIntentService";
public static TextToSpeech mtts;
public GCMIntentService() {
super(SENDER_ID);
}
#Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("message");
mtts = new TextToSpeech(context, null);
if (message.startsWith("makeSound")) {
mtts = new TextToSpeech(context, null);
mtts.setLanguage(Locale.US);
mtts.speak(message, TextToSpeech.QUEUE_FLUSH, null);
}
}
}
It doesn't work the first time because the initialization of TextToSpeech is asynchronous. You can not simple instantiate it and use it as you are doing. You should provide a callback to be called once the TextToSpeech has been initialized if you want to use it right away.
mTextToSpeech = new TextToSpeech( this, new TextToSpeech.OnInitListener()
{
#Override
public void onInit( int status )
{
// Check for status might not be initialized due to errors
// Configure language/speed
}
} );
It does work the rest of the times, because mtts is static. This means that is a class variable and is not destroyed/initialized when creating new instances of the service. By the second time you use this service, this variable was already initialized in the first service execution.