Explicit and Implicit intents - java

I'm a bit confused on Intents.
Why is
Intent implicit=new Intent(IDownload.class.getName());
an Implicit intent
while
Intent explicit=new Intent(implicit);
is an Explicit intent while it doesn't seem to add anything new in its definition. The system doesn't seem to draw any new information that wasn't previously provided by implicit intent above?
In the Android documentation (Intent types),
Explicit intents specify the component to start by name (the fully-qualified class name). You'll typically use an explicit intent to start a component in your own app, because you know the class name of the activity or service you want to start......
Intent implicit=new Intent(IDownload.class.getName()); seems to fulfill this requirement, but is still regarded as an Implicit intent, which as per the documentation:
Implicit intents do not name a specific component, but instead declare a general action to perform, which allows a component from another app to handle it.....
Intent explicit=new Intent(implicit); seems to contradict this, but is still regarded as an Explicit intent.
UPDATE - Sample implementation
ref : Binding to a Service
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
appContext=(Application)getActivity().getApplicationContext();
Intent implicit=new Intent(IDownload.class.getName());
List<ResolveInfo> matches=getActivity().getPackageManager()
.queryIntentServices(implicit, 0);
if (matches.size() == 0) {
Toast.makeText(getActivity(), "Cannot find a matching service!",
Toast.LENGTH_LONG).show();
}
else if (matches.size() > 1) {
Toast.makeText(getActivity(), "Found multiple matching services!",
Toast.LENGTH_LONG).show();
}
else {
ServiceInfo svcInfo=matches.get(0).serviceInfo;
try {
String otherHash=SignatureUtils.getSignatureHash(getActivity(),
svcInfo.applicationInfo.packageName);
String expected=getActivity().getString(R.string.expected_sig_hash);
if (expected.equals(otherHash)) {
Intent explicit=new Intent(implicit);
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
appContext.bindService(explicit, this, Context.BIND_AUTO_CREATE);
}
else {
Toast.makeText(getActivity(), "Unexpected signature found!",
Toast.LENGTH_LONG).show();
}
}
catch (Exception e) {
Log.e(getClass().getSimpleName(), "Exception trying to get signature hash", e);
}
}
}

The linked example includes these lines
Intent explicit=new Intent(implicit);
ComponentName cn=new ComponentName(svcInfo.applicationInfo.packageName,
svcInfo.name);
explicit.setComponent(cn);
The first line simply creates a new Intent instance that's a copy of the old one. While the variable might be explicit, at this point in time, it is still an implicit intent.
The second line creates a ComponentName object based on the single matching result from an earlier getActivity().getPackageManager().queryIntentServices(implicit, 0) call. At this point, explicit is still an implicit intent.
The third line is the magic. Once the specific ComponentName is assigned to the Intent instance, explicit truly becomes an explicit intent.

Related

How a client process (in android IPC with aidl) be aware of remote sever class?

In Android official Aidl document, the IPC client example declares an intent explicitly with the target "RemoteService.class". However, when server and client are not in the same package, client should not be aware of what is "RemoteService" if no dependency set. How does the example works?
ref: https://developer.android.com/guide/components/aidl.html
I searched for several working examples, and the intent is set with Action instead of the remote service class object.
In Android docs,
Intent intent = new Intent(Binding.this, RemoteService.class);
intent.setAction(IRemoteService.class.getName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
Currently, I expect this should be modified to:
Intent intent = new Intent("<remote-service-intent-filter-in-androidmanifest>");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
You are on the right path, but if you add the intent action at the manifest, then you should also mention the package name while binding the service.
intent.setPackage("<remote service package name>");
Caution: To ensure that your app is secure, always use an explicit
intent when starting a Service and don't declare intent filters for
your services. Using an implicit intent to start a service is a
security hazard because you cannot be certain of the service that
responds to the intent, and the user cannot see which service starts.
Beginning with Android 5.0 (API level 21), the system throws an
exception if you call bindService() with an implicit intent.
https://developer.android.com/guide/components/services
Snipplet:
Here is how I connect to a remote service on a different application with the setClassName API.
Note: This approach does not need the intent action at the manifest file.
At Client activity.
/**
* Init Service
*/
private void initService() {
if (mSampleService == null) {
Intent i = new Intent();
// set intent action
i.setAction("com.hardian.sample.aidl.ISampleService");
// mention package name with service's canaonical name
i.setClassName("com.hardian.sample", "com.hardian.sample.aidl.SampleAidlService");
// binding to a remote service
bindService(i, mSampleServiceConnection, Service.BIND_AUTO_CREATE);
}
}
At Service
/**
* {#inheritDoc}
*/
#Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind called");
if (ISampleService.class.getName().equals(intent.getAction())) {
return mSampleServiceBinder;
}
return null;
}

Re-use the same activity

imagine this stack situation :
A - B- C - D - B , A, B and C are activities, but D is a service.
Basically, i have a service (D) and from that service i want to call an already existing activity (B).
I know that if I want to re-use an activity, all i got to do is to use the flags (or change the manifest) SingleTop (will re-use the activity if it is already on top) or SingleTask (will re-use the activity whether it is on top or not).
The problem is that since i am inside a Service, i will have to add the flag FLAG_ACTIVITY_NEW_TASK, so that i can call an activity. Also, i added SingleTask as a launch mode in my manifest so that that activity will be re-used.
This works fine since it re-uses the same activity and comes back to the method onNewIntent(Intent intent). The problem is that everything that i put as an extra on that intent comes as null. I try to send 2 strings and 2 booleans through that intent and all of them reach the onNewIntent(Intent intent) as null.
How can i solve this ? Do I have to do something inside the onNewIntent(Intent intent) method, before getting the extras ? Are there any better alternatives ?
PS:
I heard about the StartActivityForResult or something similar. That would only work 50% of the times, since this is for a " chat-like" application.
So I will be on the "chat", from where I go to another activity where i can select something to send. After that, i will go to the service, where the transfer is done, and then back to the "chat".
But when I receive something, I am already on the "chat", so the startActivityForResult wouldn't work in this case (the service to receive would be running on the background + I don't wan't to finish the receiving part, because i want to be always listening for something).
Here is the code from the service where i try to re-launch the single activity :
Intent transfRec=new Intent(ServerComm.this ,TransferRecordActivity.class);
transfRec.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
transfRec.putExtra("receivedFilePath",appName+".apk");
transfRec.putExtra("joinMode","appselection");
transfRec.putExtra("nameOfTheApp",appName);
transfRec.putExtra("received",false);
transfRec.putExtra("isHotspot",isHotspot);
startActivity(transfRec);
Here is the code of my onNewIntent(Intent intent) :
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
System.out.println("I am here in the new intent");
if(intent==null){
System.out.println("Intent is null inside the new intent method !!!");
}
tmpFilePath=getIntent().getStringExtra("receivedFilePath");
System.out.println("The tmpFilePath is : "+tmpFilePath);
received=getIntent().getBooleanExtra("received",true);
nameOfTheApp=getIntent().getStringExtra("nameOfTheApp");
isHotspot=getIntent().getStringExtra("isHotspot");
System.out.println("O received boolean esta a : : : "+received);
textView.setVisibility(View.GONE);
receivedFilePath= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/"+tmpFilePath;
System.out.println("transfer REcord Activity _: the received file path is :::: " +receivedFilePath);
getReceivedApps();
adapter= new HighwayTransferRecordCustomAdapter(this,listOfItems);
receivedAppListView.setAdapter(adapter);
EDIT: As you guys can see i check if the intent is null, and it is not, since it doesn't do the system.out.println that there is on that condition !
The issue is that you are calling getIntent() within your onNewIntent(). From the docs for getIntent():
Return the intent that started this activity.
Therefore, you are getting the intent provided to onCreate(). To get the intent supplied to onNewIntent(), you simply use intent which is provided in the method signature:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
tmpFilePath=intent.getStringExtra("receivedFilePath");
...
}

How to query for the default SpeechRecognizer

How to find out the ComponentName of the default system speech recognizer, i.e. the one that is returned when createSpeechRecognizer(Context context) is called? (Actually, I only need to find out which input languages it supports, so if there is an answer only to that, then I'd appreciate it as well.)
The framework solves this by
String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.VOICE_RECOGNITION_SERVICE);
(See the source code of SpeechRecognizer.)
However, this solution does not seem to be available to a third party app.
However, this solution does not seem to be available to a third party app.
I assume you came to such conclusion because Settings.Secure.VOICE_RECOGNITION_SERVICE is not a public API. However, Settings.Secure.getString() requires name of the row to lookup in secure table for the second argument. So, you can simply provide the actual the name of the row you are looking for: "voice_recognition_service".
That's, you can use the same code from SpeechRecognizer with slight change:
String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
"voice_recognition_service");
Hope this helps.
UPDATE (I misread the original question)
SpeechRecognizer isn't the thing doing the speech processing, the Intent you pass to SpeechRecognizer, however, is (via startListening(Intent intent)). That intent uses RecognizerIntent.ACTION_RECOGNIZE_SPEECH and, AFAIK, can be detected in the old-fashioned way.
To detect defaults, try resolving the Intent that you want the find the default for but with the PackageManager.MATCH_DEFAULT_ONLY set.
Untested code:
String detectDefaultSpeechRecognizer(Context context) {
final Intent speechIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
// 1: Try to find the default speech intent
final ResolveInfo defaultResolution = context.getPackageManager().resolveService(speechIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (defaultResolution != null) {
final ActivityInfo activity = defaultResolution.activityInfo;
if (!activity.name.equals("com.android.internal.app.ResolverActivity")) {
//ResolverActivity was launched so there is no default speech recognizer
return "";
}
}
// 2: Try to find anything that we can launch speech recognition with. Pick up the first one that can.
final List<ResolveInfo> resolveInfoList = context.getPackageManager().queryIntentServices(speechIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (!resolveInfoList.isEmpty()) {
speechIntent.setClassName(resolveInfoList.get(0).activityInfo.packageName, resolveInfoList.get(0).activityInfo.name);
return resolveInfoList.get(0).activityInfo.packageName;
}
return "";
}
OLD ANSWER
Check out GAST, it has a way to check if a language is supported in a speech recognizer.
https://github.com/gast-lib/gast-lib/blob/master/library/src/root/gast/speech/SpeechRecognizingActivity.java#L70
You could also try to manually check the <recognition-service> metadata tag.
http://developer.android.com/reference/android/speech/RecognitionService.html#SERVICE_META_DATA
If you only want to find out which input languages the default system speech recognizer supports (createSpeechRecognizer (Context context)), there is a more straightforward way to do it.
All you need to do is using a RecognizerIntent.getVoiceDetailsIntent intent that will check the default system speech recognizer languages:
Intent intent = RecognizerIntent.getVoiceDetailsIntent(getApplicationContext());
if (intent != null) {
ctx.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
#Override
public void onReceive(Context context, final Intent intent) {
Log.d(TAG,
"Receiving Supported Speech Recognition Languages broadcast "
+ intent);
final Bundle extra = getResultExtras(false);
if ((getResultCode() == Activity.RESULT_OK)
&& (extra != null)
&& (mHandler != null)
&& ((extra
.containsKey(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES)) || (extra
.containsKey(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE)))) {
List<String> supportedLanguages = extra
.getStringArrayList(RecognizerIntent.EXTRA_SUPPORTED_LANGUAGES);
String prefLang = extra
.getString(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE);
}
}
},
null, Activity.RESULT_OK, null, null);
}

Intent not working as expected when used with Class name

I have this following code, in Class named Menu which extends from ListActivity.
When I click on listitem the Toast line is getting executed properly but nothing happens after that, I guess code in catch block is getting executed and that Intent part is throwing some exception. Do you see any problem with this definition ?
protected void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
try {
Toast.makeText(Menu.this, "Clicked!!", 5000).show();
Class myClass = Class.forName("com.palye.first.MainActivity");
Intent menuI = new Intent(Menu.this, myClass);
startActivity(menuI);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
You do know if you own the Activity you can create an explicit intent:
Intent intent = new Intent(Menu.this, MainActivity.class);
then you don't need the try catch either
Sometimes the
Class myClass = Class.forName("com.example.first.MainActivity");
isn't enough, because it will not load the class. I need to make a myClass.newInstance(); and than use the myClass, even if I am not using the created object.
Try it , maybe it will solve your problem.
Why don't you try MainActivity.class instead? e.g., Intent menuI = new Intent(Menu.this, MainActivity.class);
Can you post the logcat output?
Somewhere I saw a few package names are excluded from loading, deploying, forgot which ones. As I remember one is the "android" other is the "internal" or "example" . Because I am switching / mixing the iOS / Android / Java EE I am not sure where isn't allowed and which one.
Try to rename / refactor the "example" to "my" package, maybe it will solve. For Android Market for sure isn't allowed to deploy with "example"

How can I start a new android activity using class name in a string?

I'm having a problem with an android application that I'm working on.
My application has several sections and the next screen that loads is based on a string. So, screen 1 of section 1 would be, S1S1.
My question is, how can I start an activity based on a string. I have S1S1 saved in a string, let us call it next activity. Rather than having to type S1S1.class, I need it to come from the string. I've tried everything I can think of and google hasn't helped much.
Some things I've tried are
Intent myIntent = new Intent(nextactivity);
Intent myIntent = new Intent(v.getContext(), getClass().getName().valueOf(nextactivity));
Intent myIntent = new Intent(v.getContext(), Class.forName(nextactivity));
and tried running with
startActivityForResult(myIntent, 0);
but nothing seems to work. Any ideas?
Here is a code by which you can start activity using the name of the activity
String activityToStart = "com.example.MainActivity";
try {
Class<?> c = Class.forName(activityToStart);
Intent intent = new Intent(this, c);
startActivity(intent);
} catch (ClassNotFoundException ignored) {
}
EDIT
Here class name will be full name of the class with the package name.
For example if your package name will be x.y.z and if you have Activity name called A then the full name of the Activity A will be x.y.z.A.
An even better way (and one that is used in the system to launch Browser.apk along with other apps that aren't bundled with AOSP):
Intent intent = new Intent();
intent.setClassName("com.android.browser","com.android.BrowserActivity");
context.startActivity(intent);
Alternatively, if you want to check that you can start the Activity from the command line, you can do something like this from your shell:
adb shell
am start com.android.browser/.BrowserActivity
I am not aware of solution but i have an alternative.. the way similar to div hide and show in web pages.
if your s1s1 is to loaded low content have them in a linearlayout and keep their visibility gone on loading form s1. when you click on s1 to reach s1s1 hide s1 and set the params of visibility to "visible".
By doing this you can avoid creating a separate activity and this way is also easy to navigate back.
Use Enums!
public enum SectionActivity {
S1S1(MyS1Activity.class),
S1S2(S2Activity.class);
private Class<? extends Activity> activityClass;
private SectionActivity(Class<? extends Activity> clazz) {
this.activityClass = clazz;
}
public Class<? extends Activity> getActivity {
return activityClass;
}
}
Then somewhere in your code:
SectionActivity act = SectionActivity.valueOf(string);
Intent intent = new Intent(this, act.getActivity());
startActivity(intent);

Categories

Resources