Bring Android app to front of other windows - android

I have an Android app that launches automatically when a USB stick is plugged into the phone. This works well and as expected.
The trouble is, on many android phones, the "My Files" app launches as well when it sees a usb stick inserted. The "My Files" app comes up after my app, so it covers up my app window.
Is there a way to either bring my app window in front of the My Files window or get it to come up after the My Files window?
I've tried different priority values as mentioned in to bring app to front on receive of a call but it doesn't seem to help.
The receiver code I am using is:
public class MediaReceiver extends BroadcastReceiver {
private static final String TAG = MediaReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
LogHelper.log(TAG, "Intent received, action = " + action);
AppEx appEx = (AppEx)context.getApplicationContext();
if (action.equals("android.hardware.usb.action.USB_DEVICE_ATTACHED")) {
if(appEx.isActivityVisible()) {
context.sendBroadcast(new Intent(BaseActivity.ACTION_STICK_ATTACHED));
} else {
Intent i = new Intent(context, SplashActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
appEx.stickAttached();
} else if (action.equals("android.hardware.usb.action.USB_DEVICE_DETACHED")) {
appEx.stickDetached();
context.sendBroadcast(new Intent(BaseActivity.ACTION_STICK_DETACHED));
}
}
}
And, in my AndroidManifest.xml, I have:
<receiver android:name="com.android.myApp.receivers.MediaReceiver">
<intent-filter android:priority="2147483647">
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
</receiver>

Related

Android Web Intent Issue

G'day,
Disclaimer: I'm not an Android dev, I'm QAing an Android app with the issue I'm describing. The technical terms I use to describe this issue might be wrong.
I'm testing an Android app that describes in its manifest that it can handle web intents with the address of type https://www.example.com/app/(.*). The way it should handle these URLs is that it gets the first match group $1 and sends a request to https://api.example.com/$1 and if the response is a HTTP200, it renders the response within the app. If not, it should open the URL in any browser app the user has installed on their device (by sending an intent to open the URL). If the user has no browser apps installed on their device, we show an error prompt saying they don't have a browser installed which can handle this URL.
Now, this works fine except when the user marks this app as the default to handle URLs like https://www.example.com/app/(.*) when it first tries to open a URL like https://www.example.com/app/(.*). Then, even if the user has browser apps installed on their system, when they open a link that needs to be opened in a browser, the only option seems to be the our original app and we have to show the error message (as it seems like there are no other browser apps installed on the system which can handle this URL).
One way to tackle this is to show a message asking the user to clear the defaults for this app when we encounter a URL that needs to be opened in a browser app but the only option is our own app — but this is terrible UX. Is there another work-around for this issue?
Sample code to understand the issue: https://gist.github.com/GVRV/5879fcf0b1838b495e3a2151449e0da3
Edit 1: Added sample code link
To solve this problem and keep the systems default handling of intents you need 2 additional activities and 1 <activity-alias>:
Create a new invisible empty Activity. I called it IntentFilterDelegationActivity. This activity is responsible to receive URL intents from the activity-alias (defined in the next step). Manifest:
<activity
android:name=".intent_filter.IntentFilterDelegationActivity"
android:excludeFromRecents="true"
android:exported="true"
android:launchMode="singleInstance"
android:noHistory="true"
android:taskAffinity=""
android:theme="#style/Theme.Transparent"/>
Code:
public class IntentFilterDelegationActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
findAndStartMatchingActivity(getIntent());
finish();
}
}
Create an <activity-alias>. The activity alias is responsible to delegate your URL intents to your IntentFilterDelegationActivity. Only a Manifest entry is needed:
<activity-alias
android:name="${packageName}.IntentFilterDelegation"
android:enabled="true"
android:exported="true"
android:targetActivity=".intent_filter.IntentFilterDelegationActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http" android:host="www.example.com"/>
</intent-filter>
</activity-alias>
Now you are able to do the trick: You can deactivate the activity-alias before you launch your own URL intent and activate the alias after the launch. This causes android that your app won't be listed as app which can handle the URL intent. To implement the activation and deactivation you need an additional Activity. I called it ForceOpenInBrowserActivity.
Manifest:
<activity
android:name=".activity.ForceOpenInBrowserActivity"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:noHistory="true"
android:taskAffinity=""
android:theme="#style/Theme.Transparent"/>
Code:
public class ForceOpenInBrowserActivity extends Activity
{
public static final String URI = IntentUtils.getIntentExtraString(ForceOpenInBrowserActivity.class, "URI");
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Uri uri = fetchUriFromIntent();
if (uri != null)
{
startForcedBrowserActivity(uri);
}
else
{
finish();
}
}
#Nullable
private Uri fetchUriFromIntent()
{
Uri uri = null;
Intent intent = getIntent();
if (intent != null)
{
uri = intent.getParcelableExtra(URI);
}
return uri;
}
private void startForcedBrowserActivity(Uri uri)
{
disableActivityAlias(this);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// After starting another activity, this activity will be destroyed
// android:noHistory="true" android:excludeFromRecents="true"
startActivity(intent);
}
/**
* Re-enable intent filters when returning to app.
* Note: The intent filters will also be enabled when starting the app.
*/
#Override
protected void onStop()
{
super.onStop();
enableActivityAlias();
}
public void disableActivityAlias()
{
String packageName = getPackageName();
ComponentName componentName = new ComponentName(packageName, packageName + ".IntentFilterDelegation"); // Activity alias
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
public void enableActivityAlias()
{
String packageName = getPackageName();
ComponentName componentName = new ComponentName(packageName, packageName + ".IntentFilterDelegation");
getPackageManager().setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
}
Now you can send any URL intent which must be opened in an external browser to the ForceOpenInBrowserActivity:
#NonNull
public static Intent createForceBrowserIntent(Context context, #NonNull Uri uri)
{
Intent intent = new Intent(context, ForceOpenInBrowserActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(ForceOpenInBrowserActivity.URI, uri);
return intent;
}
if website https://www.example.com/ is under your supervision, you could change the logic and use an unique schema like example://app/(.) to handle your case. The website could then use redirection to for its navigation. In this way when you broadcast https://www.example.com/ for action view only browser apps could handle this and your app would be only listening to your custom schema example://app/(.) and wont launch.
Else you could check for default activity and clear it instead of showing an alert.
PackageManager pm = context.getPackageManager();
final ResolveInfo res = pm.resolveActivity(your_intent, 0);
if (res.activityInfo != null && getPackageName()
.equals(res.activityInfo.packageName)) {
pm.clearPackagePreferredActivities("you_package_name");
broadcast your intent
}
Sadly, there is no official solution for this problem (see this SO question).
A workaround is the following:
Use PackageManager.queryIntentActivities(), modify the result to not include your app and show it in a custom chooser dialog.
If you don't want your users to choose a browser every time, you can manage a custom default inside your app.
If you control the domain, there is a cleaner workaround:
Lets say your url is http://www.example.com. Your Android IntentFilter should listen for that schema. Now you create a second schema, e.g. http://web.example.com, which displays the same content as the normal url. If you want to redirect to the web from your app, use the second schema. Everywhere else, use the first one.
Note that you should not use a custom schema like example://, because this will cause problems if your app is not present.

How to get notified when an image is copied to device via MTP

I am writing a Gallery like app.
I want to be notified when an image is copied to device via MTP.
I tried to register a broadcast receiver to listen for media scanner finished action, but it never got called. I tried to both register in AndroidManifest.xml or register in Java code, neither works.
<receiver android:name="com.robin.huangwei.omnigif.content.MediaScannerReceiver"
android:enabled="true" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_SCANNER_FINISHED" />
<data android:scheme="file" />
</intent-filter>
</receiver>
public class MediaScannerReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("-----", "Scan finished, new item: " + intent);
}
}
or using JAVA code as below
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("-----", "Scan finished, new item: " + intent);
}
};
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_FINISHED);
filter.addDataScheme("file");
registerReceiver(mReceiver, filter);
None of the above works.
Whats wrong with my code? I can see every time a new image is copied, it could show in Gallery App. It might be using content observer to watch the media store database, which I don't want to.
I just want to be notified when a new image is added into the device storage via MTP. Since it is added into media store, which must be done by media scanner, why cannot my app capture the broadcast intent? If this is not even correct, I want to know the answer I post as the title.

Callback from BroadcastReceiver beetwen Apps?

I want to allow other Apps to integrate with mine and I'm writing a dummy "consumer" app but I cant achieve to return a callback to notify the "consumer" app if everything went well.
So my DUMMY_APP has a simple layout with 2 buttons a success call, and a call with a wrong EXTRA param.
To make DUMMY_APP to call MAIN_APP I use sendBroadcast
// MainActivity class
private static final String REQUIRED_ACTION = "com.basetis.afr.intent.action.INIT_TEXT_FLOW";
onCreate....
Button btnSuccess = (Button)findViewById(R.id.button_success_call);
btnSuccess.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
i.setAction(REQUIRED_ACTION);
i.putExtra(Intent.EXTRA_TEXT, textToBeRead);
sendBroadcast(i);
}
});
So MAIN_APP has the corresponding BroadcastReceiver that is receiving fine.
// BlinkingReadReceiver class
private static final String CALLBACK_CALL_AFR_ACTION = "com.basetis.afr.intent.action.CALLBACK_CALL_AFR_ACTION";
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent();
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(TAG, "SUCCESS send callback");
i.setAction(CALLBACK_CALL_AFR_ACTION);
i.putExtra(CALL_AFR_SUCCESS_EXTRA, CALL_AFR_SUCCESS_EXTRA_DESC);
i.setType("text/plain");
context.sendBroadcast(i);
}
So the DUMMY_APP BroadcastReceiver never receive nothing :(
So I configured Manifests like that:
DUMMY_APP
<receiver android:name=".MainBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="com.basetis.afr.intent.action.CALLBACK_CALL_AFR_ACTION"></action>
</intent-filter>
</receiver>
MAIN_APP
<receiver android:name=".BlinkingReadReceiver" android:enabled="true">
<intent-filter>
<action android:name="com.basetis.afr.intent.action.INIT_TEXT_FLOW"></action>
</intent-filter>
</receiver>
Sometimes I receive this error (afrsender is de DUMMY_APP) but seems sort of random...
Performing stop of activity that is not resumed: {com.basetis.afrsender.afrsender/com.basetis.afrsender.afrsender.MainActivity}
java.lang.RuntimeException: Performing stop of activity that is not resumed
Any suggestions about how to achieve this two way App communication?
Thank you very much.
As stated in the document
Starting from Android 3.1, the system's package manager keeps track of applications
that are in a stopped state and provides a means of controlling their launch from
background processes and other applications.
That means that till the app is not started manually by the user your app will be in force stop state and it won't receive any broadcast.
That's why your dummy app is not receiving and broadcast sent by main app.
Check here for more reference

Android return from browser to app

I have option in my app to start browser and load imdb website.
I'm using ActionView for this.
Intent intent1 = new Intent(android.content.Intent.ACTION_VIEW,
Uri.parse(website));
try {
activity.startActivity(intent1);
} catch (Exception e) {
Toast.makeText(activity, R.string.no_imdb, Toast.LENGTH_SHORT)
.show();
}
The problem occurs when I tap on back button.
When default browser app is launched everything is ok.
When Opera Mini app is launched, when I tap on back button, it seems like my app receives two back actions, and finish my current activity.
How to prevent this?
Try starting the intent in a new task:
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Or
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Please add this code to your android manifest for activity that you need return
<activity
android:name="YourActivityName"
android:launchMode="singleTask">
<intent-filter>
<action android:name="schemas.your_package.YourActivityName" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.ALTERNATIVE" />
</intent-filter>
</activity>
and add this to your web page
click to load app
because only one app has this action name (schemas.your_package.YourActivityName) on your phone, web page directly return to app
Also You can Use Airbnb DeepLink lib
Example
Here's an example where we register SampleActivity to pull out an ID from a deep link like example://example.com/deepLink/123. We annotated with #DeepLink and specify there will be a parameter that we'll identify with id.
#DeepLink("example://example.com/deepLink/{id}")
public class SampleActivity extends Activity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent.getBooleanExtra(DeepLink.IS_DEEP_LINK, false)) {
Bundle parameters = intent.getExtras();
String idString = parameters.getString("id");
// Do something with idString
}
}
}

What Intent Filter can I use to capture intent calls to the permissions screen on the Android Market?

I am creating an app that warns users about strange permission requests before they download an app from the Android Market (such as a wallpaper app that requests to read a user's contact information). Is there a way to capture the intent called when a user presses the install button and is shown the list of uses-permissions?
There's no way of doing this that I know of. You can show the user your dialog right after the user installs the app instead (in most cases, before they run it):
AndroidManifest.xml
<receiver android:name=".Receiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_CHANGED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
Receiver.java
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
PackageManager manager = this.getPackageManager();
PackageInfo info = manager.getPackageInfo(
intent.getData().getSchemeSpecificPart(), 0);
Toast.makeText(context, "Look at these suspicious permissions:"+
info.permissions, Toast.LENGTH_LONG).show();
} catch (Exception e) {}
}
}

Categories

Resources