I have an activity that I'm starting:
Intent intent = new Intent(this, CreateItemDetailsActivity.class);
if(storeId != null) {
intent.putExtra(Identifiers.STORE_ID, storeId);
}
intent.putExtra(Identifiers.ITEM_NAME, name);
intent.putExtra(Identifiers.ITEM_DESCRIPTION, description);
startActivity(intent);
This works, but now I need to return data to the original activity, so I change startActivity call to:
startActivityForResult(intent, CREATE_ITEM_RESULT);
(CREATE_ITEM_RESULT is just a random integer number 63463657 I made up)
However, my app crashes without neither onCreate methods (I've implemented and put a breakpoint in both) being called:
Uncaught exception in thread main: java.lang.IllegalStateException: Could not execute method for android:onClick
android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:389)
(the code is called in a button click handler, hence onClick)
I've seen App crashes when calling startActivityForResult which has the same problem but the accepted answer suggests removing a line, recipe.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);, which I don't have.
Why am I getting a crash on startActivityForResult while startActivity works perfectly?
(My min SDK level is 16, target and compile SDK levels are 27, and I'm running Android 8.1 emulator)
If have not seen any documentation on this issue, but try a four digit int value should get rid of the error.
I have read Google docs for startActivityForResult and I have not found any reference to a maximum int size. Even the code documentation states:
#param requestCode If >= 0, this code will be returned in the
onActivityResult() when the activity exists
So, no mention of a maximum int value. Odd...
EDIT
Thanks to #MidasLefko for finding this information:
RequestCodes can only be a max of 0xffff (65535). So you are probably
calling startActivityForResult(intent, REQUEST_CODE); and REQUEST_CODE
is greater than 65535.
on this website:
https://code-examples.net/en/q/db5947
In the non-support Activity class implementation of startActivityForResult there is no restriction on the maximum int for requestCode, as long as it is positive. However FragmentActivity (and by extension AppCompatActivity) overrides this behavior (documentation):
void startActivityForResult (Intent intent, int requestCode)
Modifies the standard behavior to allow results to be delivered to fragments. This imposes a restriction
that requestCode be <= 0xffff.
In the source code of FragmentActivity one finds the following helpful comment:
A hint for the next candidate request index. Request indicies are ints
between 0 and 2^16-1 which are encoded into the upper 16 bits of the
requestCode for Fragment.startActivityForResult(...) calls. This
allows us to dispatch onActivityResult(...) to the appropriate
Fragment. Request indicies are allocated by allocateRequestIndex(...).
ints in java are 32 bit, FragmentActivity uses 16 to determine which fragment to send the result to and the other 16 are available for the developer.
Related
I tried to implement a platform-independent wrapper for sharing files that takes a filename and an Action as a callback. Despite it does work, I am not content with my Android solution at all.
What I am currently doing is: I am registering my callback with my MainActivity, which returns an ID which I can use to identify the callback
int callbackId = MainActivity.Instance.RegisterCallback(completionHandler);
Then I start ActionShare intent with
MainActivity.Instance.StartActivityForResult(Intent.CreateChooser(intent, "Select App"), 123 | (callbackId << 8));#
The 123 is my identifier for the action.
In my MainActivity in OnActivityResult I am getting and calling my callback with
var callbackId = (requestCode & 0b1111111100000000) >> 8;
if (this.RemoveCallbackById(callbackId, out var callback))
{
callback();
}
Is there a better way implementing callbacks for intents in Xamarin.Android? I believe there must be, but being constrained to passing only a single 16 bit integer to StartActivityForResult that will be passed to OnActivityResult the possibilities seem quite limited to me.
I'm trying to use enableSystemApp method to activate default system apps after provisioning device with the app that is set to device owner mode.
There are two methods to do this:
1) void enableSystemApp (ComponentName admin, String packageName) - in this case you need to pass package name explicitly as String. It works fine, the app gets enabled.
For example, calling this
devicePolicyManager.enableSystemApp(deviceAdminComponent, "com.google.android.gm");
enables default Gmail client, which is disabled after provisioning.
2) int enableSystemApp (ComponentName admin, Intent intent) - in this case, you need to pass an implicit intent and Android should enable all system apps that match this intent. In addition, this method returns int number of apps that match the intent. And here's the problem - I can't get this method to work, it always returns 0 and doesn't enable anything.
Here's the snippet I'm trying to use:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
int i = devicePolicyManager.enableSystemApp(deviceAdminComponent, intent);
It does not work and i == 0 in this case. What am I doing wrong?
Any help is appreciated!
Under the hood, the method that accepts an intent queries to get the list of activities that respond to that intent and then loops through the list passing in the package name string to enable the package. It's similar to doing this:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_APP_EMAIL);
List<ResolveInfo> infoes = getPackageManager()
.queryIntentActivities(intent, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
for (ResolveInfo info in infoes) {
devicePolicyManager.enableSystemApp(deviceAdminComponent, info.activityInfo.packageName);
}
Since you are able to enable the app using the package name string, the fault most likely lies in the way the intent is being resolved - which is supported by the fact that it always returns 0.
It is counter-intuitive, but my suspicion is that the application does not resolve the ACTION_MAIN intent because the app is disabled. Have you tried a less generic intent? I would try the following
Intent i;
// #1
// This goes full circle, but I expect it should work
i = getPackageManager().getLaunchIntentForPackage("com.google.android.gm")
// #2
i = new Intent(Intent.ACTION_SEND).setPackageName("com.google.android.gm");
// #3
// Generic, but should resolve _all_ email apps - not just the default one.
// The mailto schema filters out non-email apps
i = new Intent(Intent.ACTION_VIEW , Uri.parse("mailto:"));
Option #1 and #2 are more academic. Both require the package name at which point you may as well use the string overload of enableSystemApp. Option #3 is my best guess for something generic that might still work, but it's possible that it still won't work because the app is disabled.
Note: I find it interesting that enableSystemApp only passes the MATCH_DIRECT_BOOT_AWARE and MATCH_DIRECT_BOOT_UNAWARE flags when querying activities that can resolve the intent, because the MATCH_DISABLED_COMPONENTS and MATCH_SYSTEM_ONLY flags seem much more relevant in this situation.
On our application there's a service that is normally started during Application.OnCreate (directly calling context.startService) and also later on via AlarmManager (refactor is in progress to migrate some of its work to JobScheduler).
Our application also have a BroadcastReceiver that gets launched with its direct intent.
Given the new limitations in Android Oreo (https://developer.android.com/about/versions/oreo/android-8.0-changes.html) we're having an issue as follows:
app/process is in background/dead
BroadcastReceiver gets fired by the OS
Application.onCreate() executes before the BroadcastReceiver
Application.onCreate() code tries to run the Service
this leads to crash with "IllegalStateException: Not allowed to start service Intent".
I'm aware of the new recommended ways of launching a Service as answered by CommonsWare here https://stackoverflow.com/a/44505719/906362, but for this specific case, I simply want to have if(process in foreground) { startService }. I'm currently using the following method and it seems to work:
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static boolean isProcessInForeground_V21(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.AppTask> tasks = am.getAppTasks();
return tasks.size() > 0;
}
But I can't find the exact checks Android Oreo is doing (I got as far as here https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ContextImpl.java on the startServiceCommon method, but from there requireForeground flag seems to go to some native implementation)
So my question:
For the specific purpose of Android Oreo new limitations, how to check if my process is foreground before calling startService?
To continue your investigation: (TL;DR: see between horizontal lines at the bottom)
Disclaimer, I don't know too much about Android, I just like digging in the source code.
Note: you can also navigate the code in Android Studio if you jump to file instead of class:
or searching for text in Project and Libraries.
IActivityManager is defined by AIDL, that's why there are no sources for it:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/IActivityManager.aidl#145
Based on how AIDL needs to be implemented I found that ActivityManagerService extends IActivityManager.Stub (God bless Google indexing).
Note I also found this, which might be an interesting read if you're really interested how things work internally.
https://programmer.group/android-9.0-source-app-startup-process.html
ActivityManagerService sources reveal that in Oreo startService is forwarded to ActiveServices which is located in the same package.
Assuming we're looking for an exception like this:
java.lang.IllegalStateException: Not allowed to start service Intent {...}: app is in background uid UidRecord{af72e61 u0a229 CAC bg:+3m52s273ms idle procs:1 seq(0,0,0)}
we have to continue down the rabbit hole: requireForeground gets assigned to fgRequired parameter and the message is here. The condition to allow this depends on the start mode returned by ActivityManagerService.getAppStartModeLocked(packageTargetSdk = 26 or greater, disabledOnly = false, forcedStandby = false).
There are 4 start modes:
APP_START_MODE_NORMAL (needs to be different than this, i.e. !=)
APP_START_MODE_DELAYED (this is ok, i.e. return null)
APP_START_MODE_DELAYED_RIGID
APP_START_MODE_DISABLED
Ephemeral apps will immediately return APP_START_MODE_DISABLED, but assuming this is a normal app, we end up in appServicesRestrictedInBackgroundLocked.
Note: this is where some of the whitelist mentioned in https://stackoverflow.com/a/46445436/253468 is decided.
Since all branches but last return APP_START_MODE_NORMAL, this redirects to appRestrictedInBackgroundLocked where we find our most likely suspect:
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
if (packageTargetSdk >= Build.VERSION_CODES.O) {
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
So the reason for denial is simply targeting O. I think the final answer to your question of how the OS decides if your app is foreground or background is this condition in getAppStartModeLocked
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || alwaysRestrict || uidRec.idle) {
My guess is that a missing record means it's not running (but then how is it starting a service?!), and idle means it's backgrounded. Notice that in my exception message the UidRecord is saying that it's idle and has been backgrounded for 3m52s.
I peeked into your getAppTasks and it's based on TaskRecord.effectiveUid, so I'm guessing that's quite close to listing UidRecords for your app.
Not sure if this helps, but I'll post it anyway, so if anyone wants to investigate more, they have more info.
I am developing android native applications. My requirement is to get the current activity of the device like running,in_vehicle,still and so on. I used ActivityRecognitionAPI and set the pendingintent to receive the activity changes through IntentService. I gave 5 seconds interval for each updated. Its failed to provide the activity changes in certain period of time and again started providing activity. After that i preferred Awareness SnapshotAPI to get the activity state. Its also same result, failed to provide the activity regularly. Both APIs sometimes providing and many times not. I used GooglePlayServices 10.2.0 version for my developemnt. Anyone tell what is the reason for these things and solution to get regular activity updates..
Thanks in advance.
I am trying to show my app user's activity like he is walking, running and so on. This is my requirement.
Way 1:
//Google API client using ActivityRecognition.API
Intent intent = new Intent(mContext, RequestActivityService.class);
PendingIntent callbackIntent = PendingIntent.getService(mContext, 111,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(googleApiClient,5000, callbackIntent);
public class RequestActivityService extends IntentService {
public RequestActivityService() {
super("RequestActivityService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (ActivityRecognitionResult.hasResult(intent)) {
ActivityRecognitionResult result = ActivityRecognitionResult
.extractResult(intent);
DetectedActivity mostProbableActivity = result
.getMostProbableActivity();
int confidence = mostProbableActivity.getConfidence();
int activityType = mostProbableActivity.getType();
String activityName = getNameFromType(activityType);
}
}
** I tried this way to get activity updates. It started providing the activity info, but Intent service not called sometimes and again started firing the intent. So I am not able to show the correct activity information.
Way 2:
//Google API client using Awareness.API
Awareness.SnapshotApi.getDetectedActivity(googleApiClient).
setResultCallback(new ResultCallback<DetectedActivityResult>() {
#Override
public void onResult(#NonNull DetectedActivityResult detectedActivityResult) {
if (!detectedActivityResult.getStatus().isSuccess()) {
Log.i("Could not get the activity","");
return;
}
ActivityRecognitionResult ar = detectedActivityResult.getActivityRecognitionResult();
DetectedActivity probableActivity = ar.getMostProbableActivity();
String activityType = RequestActivityService.getNameFromType(probableActivity.getType());
int activityConfidence = probableActivity.getConfidence();
}
});
** I tried this another way, using this Api we can call this method recursively and get the activity information frequently. But Its also sometime providing and sometimes detectedActivityResult.getStatus() not success. If I am trying to get the status code and status message, status code returns 15 and status message will be null.
Both these ways are failed to give regular updates of activity and more over I used GooglePlayService 10.2.0 version. I have tested the GoogleSamples of ActivityRecognition from Github. That is also same result. I don't know what do to achieve my requirement. Hope now u can understand my requirement and things which I faced.
ActivityRecognitionAPI from Google doesn't give updates regularly. From what I have noticed, if there is a change in the confidence level of the activity or the activity changes altogether, then it'll send an update. Even then, it'll take time to ensure the data read isn't just noise of some kind.
I'm not sure why you need regular updates, but I would suggest changing the logic to accept the last given activity as the current activity, and only act upon the changes (i.e., STILL -> IN_VEHICLE).
Call this method again after getting response:
I would suggest to set detectionIntervalMillis to 0. That will lead to get data ASAP.( be aware of battery consumption )
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(googleApiClient,o, callbackIntent);
if there are no changes for long time, it will not send updates till change occur...
The documentation says "Checks whether the TTS engine is busy speaking."
But I just implemented a call to isSpeaking() in an onUtteranceCompletedListener, where I have at least 10 pending utterances and in none of them did I received true.
Assuming that isSpeaking() actually works as documented, I must conclude that I am calling it incorrectly.
What are the points in which calling TextToSpeech.isSpeaking() returns a valid result?
Answering myself, thanks to coming across this question (also unanswered):
Problem with isSpeaking() when using Text-to-Speech on Android
The source code of the TtsService class shows:
public boolean isSpeaking() {
return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
}
Which means the TTS engine not only must be speaking but its utterances queue size must be greater than 0.