queryIntentActivities() data Intent extras - java

My aim is to get the available TTS voice engines and their respective available languages. I can successfully get the available engines with the following code:
PackageManager pm = getPackageManager();
Intent ttsIntent = new Intent();
ttsIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
// ttsIntent.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
ArrayList<String> engineInfo = new ArrayList<String>();
List<ResolveInfo> list = pm.queryIntentActivities(ttsIntent,
PackageManager.GET_META_DATA);
for (ResolveInfo appInfo : list) {
engineInfo.add(appInfo.activityInfo.applicationInfo.packageName);
engineInfo.add(appInfo.activityInfo.applicationInfo.loadLabel(pm)
.toString());
}
The commented out line above requests a data extra for the intent. I've done many searches, but can find no examples of queryIntentActivities() using 'getExtra' or 'putExtra' in this way.
Can I therefore assume, that this isn't an option and I need to go back to read some basics somewhere of why this isn't possible!?
If I make any attempt to retrieve the data, I get a null pointer, even if I'm checking if it's !=null which is a little odd?
appInfo.activityInfo.applicationInfo.metaData.getEtcEtc...
Any help would be much appreciated, thanks in advance.
postPutEXTRA: API level is 9 so getEngines() isn't available to me, although that would involve initialising a tts object for each one, so not ideal anyway.
postPutEDIT: I tried the following within the loop to extract associated uid's, but it didn't work for all but one of the voice engines I tested.
ApplicationInfo ai = null;
try {
ai = pm.getApplicationInfo(appInfo.activityInfo.applicationInfo.packageName, 0);
} catch (NameNotFoundException e) {
e.printStackTrace();
return;
}
String[] voices = pm.getPackagesForUid(ai.uid);

I've gone through a similar process, but if I remember correctly, you can't simply fire of an intent query to get the available languages. That absolutely makes sense, because what languages are available depends on what voices and other packages the user will have installed with the TTS engine. In other words: it's not some static parameter that you query for, but requires to be resolved at runtime.
In order to get the available voices (and other information) for a TTS engine, you'll need to actually start/initialise it. That means you need to do something like:
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
And then retrieve the data in onActivityResult(...):
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != MY_DATA_CHECK_CODE) return;
// here you should be able to get the info from 'data', i.e.
ArrayList<String> voices = getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
}
Note that I copy-pasted most of above from an article on the Android dev blog.
It's been a while since I last played with TTS, so if above doesn't work out, feel free to leave a comment and I'll have a closer look at the implementation I made back then.

I couldn't solve the specific title of this question, but I did get the information I wanted of the available voice engines and their languages on the device, without user interaction, in the following way:
private void getEngines() {
Intent ttsIntent = new Intent();
ttsIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
ArrayList<String> engineInfo = new ArrayList<String>();
PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(ttsIntent,
PackageManager.GET_META_DATA);
for (ResolveInfo appInfo : list) {
engineInfo.add(appInfo.activityInfo.applicationInfo.packageName);
ttsIntent
.setPackage(appInfo.activityInfo.applicationInfo.packageName);
ttsIntent
.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
startActivityForResult(ttsIntent, 33);
}
if (DE.BUG) {
MyLog.d("engineInfo: " + engineInfo.size() + " : "
+ engineInfo.toString());
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 33 && data != null) {
ArrayList<String> available = data
.getStringArrayListExtra("availableVoices");
if (DE.BUG) {
MyLog.i("Voices: " + available.toString());
}
}
}
I've left in my custom logging class/method to show where I'm capturing the data. You can rearrange the above to put all of the data into one or more global array list(s).

Related

Android 10 - Using PackageManager to install new version of apk instead of Intents doesn't really work

The way my apk updates currently is through the new apk installing itself over the old one(while not overwriting the local database or settings).
So since Android 10(API 29) ACTION_VIEW was deprecated so this doesn't really work anymore:
Intent intent = new Intent(Intent.ACTION_VIEW);
//output file is the apk downloaded earlier
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-archive");
startActivity(intent);
Following different answers I've found over the internet I need to start using PackageInstaller instead. Looking at the demo found in the Android docs I get to this:
Intent intent = new Intent(PSMentorActivity.this,InstallApkSessionApi.class);
intent.putExtra("apkFile",outputFile);
this.startActivity(intent);
Where InstallApkSessionApi contains a button that when pressed should start the install window:
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = packageInstaller.createSession(params);
session = packageInstaller.openSession(sessionId);
addApkToInstallSession(file, session);
// Create an install status receiver.
Context context = InstallApkSessionApi.this;
Intent intent = new Intent(context, InstallApkSessionApi.class);
intent.setAction(PACKAGE_INSTALLED_ACTION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
IntentSender statusReceiver = pendingIntent.getIntentSender();
// Commit the session (this will start the installation workflow).
session.commit(statusReceiver);
Simple enough, albeit more complicated compared to the past. The response I get in onNewIntent is always:
case PackageInstaller.STATUS_FAILURE:
Toast.makeText(this, "Install failed!10 " + status + ", " + message,
Toast.LENGTH_SHORT).show();
break;
where the status and message take the following values: status=1, message= "INSTALL_FAILED_INTERNAL_ERROR: Permission denied".
So I assumed it was a problem with permissions. I already had permissions in place over reading and writing to storage. Other related permissions are: INSTALL_PACKAGES and REQUEST_INSTALL_PACKAGES which I cannot use due to the first not being intended for third party uses and the latter being signature level.
Is there something I am terribly missing or is there not a way for me to update my Apk without going through google play?
I've faced the same issue and tried the same way(PackageInstaller) just like you did. It doesn't work.
So, here is a solution for you,
if(android.os.Build.VERSION.SDK_INT >= 29){
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(Uri.fromFile(outputFile));
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
}else{
Intent intent = new Intent(Intent.ACTION_VIEW);
//output file is the apk downloaded earlier
intent.setDataAndType(Uri.fromFile(outputFile), "application/vnd.android.package-
archive");
startActivity(intent);
}
I have tested in Android 10 (Google Pixel 2, One+7, Samsung s10) it's working fine. If you are still facing any problem please let me know.
Note: Your version code must be greater than the old one.
So after help and some more searching the great internet, I've been able to make both methods work for me. I still don't really understand what causes the problem. Previously I was creating the Uri used in the intent from the file directly with Uri.fromFile(file). This works for anything under Android 10. The new way I handle this is by using a provider and getting the file using that.
Current method, to be used for a while until I can properly implement PackageManager installation(still have some issues to fix).
Uri urlapk = FileProvider.getUriForFile(this,BuildConfig.APPLICATION_ID+".fileProvider",outputFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(urlapk, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
Using PackageManager just as specified in my question with the following difference (the inputStream is now being build from an Uri):
private void addApkToInstallSession(Uri uri, PackageInstaller.Session session)
throws IOException {
try (OutputStream packageInSession = session.openWrite("package", 0, -1);
InputStream is = getContentResolver().openInputStream(uri)) {
byte[] buffer = new byte[16384];
int n;
while ((n = is.read(buffer)) >= 0) {
packageInSession.write(buffer, 0, n);
}
}
}
The main problem I have with PackageManager is that it closes my app while it installs providing no default feedback to the user. So I guess I will have to "make" that feedback. I will move on to PackageManager as ACTION_VIEW is deprecated for apks.

Android: open video picker and filter videos seen by length

This is my code for opening up the picker for Videos and Images - however I need to not show Videos that are longer than 5 minutes in length. Is this possible?
public void startChoosePhotoFromLibrary() {
if (checkOrRequestExternalStoreagePermission()) {
if (Build.VERSION.SDK_INT < 19) {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/* video/*");
startActivityForResult(photoPickerIntent, PICK_PHOTO_ACTIVITY_REQUEST_CODE);
} else {
Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);
photoPickerIntent.setType("*/*");
photoPickerIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
startActivityForResult(photoPickerIntent, PICK_PHOTO_ACTIVITY_REQUEST_CODE);
}
}
}
This is my code for opening up the picker for Videos and Images
ACTION_PICK does not use MIME types. ACTION_PICK picks from a collection of content, where that collection is identified by the Uri that you supply in the Intent.
Also, MIME types do not have spaces in them.
Is this possible?
Not via those Intent actions, or via any content-selection mechanism that is part of the Android SDK.
You are welcome to query the MediaStore for videos, and there may be a way to filter such videos by length. But then you would need to present your own UI for allowing the user to choose something from the query results (e.g., ListView, RecyclerView).

Android code to dial or calll a string containing alphabets *123*abc# instead of numbers (*123#)?

I’m unable to dial or call a string containing alphabets in android.
For Example:
I want to dial a string *123*abcd#.
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+"*123*abcd#")); //This string is not getting dialed
try
{
startActivity(callIntent);
}
catch (android.content.ActivityNotFoundException ex)
{
Toast.makeText(getApplicationContext(),"yourActivity is not founded",Toast.LENGTH_SHORT).show();
}
String *123*abcd# is not getting dialed or called. Please help to solve the same.
String s = "*123*abcd#";
if ((s.startsWith("*")) && (s.endsWith("#"))) {
callstring = s.substring(0, s.length() - 1);
callstring = callstring + Uri.encode("#");
}
Intent i = new Intent(android.content.Intent.ACTION_CALL, Uri.parse("tel:" + callstring));
startActivity(i);
your need to encode the # before using it above code will do the trick.
To use Intent.ACTION_CALL you need to add Permission
<uses-permission android:name="android.permission.CALL_PHONE"/> and since its a Dangerous permission you need to handle it Runtime for Android 6 and above.
You can rather use Intent.ACTION_DIAL which doesnt require any permission and will open the Phone app with the given number typed already, the user has to initiate the call.
Here is how to achieve this.
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:*123*abcd#"));
startActivity(intent);

Array with images from drawable and intent

I have a list of images:
private int[] images = {
R.drawable.blue_icon_left_foot,
R.drawable.blue_icon_right_foot,
R.drawable.blue_icon_left_hand,
R.drawable.blue_icon_right_hand,
R.drawable.green_icon_left_foot,
R.drawable.green_icon_right_foot,
R.drawable.green_icon_left_hand,
R.drawable.green_icon_right_hand,
R.drawable.red_icon_left_foot,
R.drawable.red_icon_right_foot,
R.drawable.red_icon_left_hand,
R.drawable.red_icon_right_hand,
R.drawable.yellow_icon_left_foot,
R.drawable.yellow_icon_right_foot,
R.drawable.yellow_icon_left_hand,
R.drawable.yellow_icon_right_hand};
I want to let the user pick an image from the gallery by using an Intent:
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), 1);
One of the images of the array should then be replaced with the new image given by the user. When I get the image from the user, I only have a URI of the image:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == 1) {
Uri imageUri = data.getData();
//Insert image into images list
}
}
Is it possible to somehow get an integer ID of the image so that I can insert the image into the same list as the images from the drawable folder?
Or should I instead try to store a list of URI's (if it is possible to get URI's of the images from the drawable folder)?
Or is there a third solution that is completely different and much better?
You can generate ids with View.generateViewId() if you are using API 17 or larger.
From the main documentation:
Generate a value suitable for use in setId(int). This value will not
collide with ID values generated at build time by aapt for R.id.
Returns a generated ID value
You can check this answer to see what are the alternatives when you are using a lower API: Android: View.setID(int id) programmatically - how to avoid ID conflicts?
I think what you are trying to do is somewhat misguided. The resources in the /res directory are compile time resources that you bundle with your project. The image that a person selects via an intent from their device is a run time file that will vary by user, device, etc. You are better off not trying to treat them the same. Save the user selections as dataUris or strings and keep the resources as ints.

starting another activity from SpeechRecognition service in android?

I am working on android application, which recognize the voice and convert the voice into text as a background process, it is working fine yet, but my next step is to open different built-in activities like message, gallery, email camera through intents,
my code is
public void onResults(Bundle b) {
ArrayList<String> as = b
.getStringArrayList(sr.RESULTS_RECOGNITION);
String res = "";
for (int i = 0; i < as.size(); i++) {
Log.e("OnResult", as.get(i));
res += as.get(i);
}
if (res.equalsIgnoreCase("message")) {
Intent i = new Intent(Intent.ACTION_SEND);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(i);
}
}
I am getting exception at this :(
Anyone help, I would appreciate , peace,

Categories

Resources