i'm creating a launcher app and i would like to show the default launcher chooser manually.
I already found and tried this but this is not perfect, since i cannot select ALWAYS immediately. After choosing the launcher i need to press the home button to be able to select ALWAYS.
This is my code:
public static void resetPreferredLauncherAndOpenChooser(Context context) {
PackageManager packageManager = context.getPackageManager();
ComponentName componentName = new ComponentName(context, DefaultLauncherHelperActivity.class);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Intent selector = new Intent(Intent.ACTION_MAIN);
selector.addCategory(Intent.CATEGORY_HOME);
selector.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(selector);
packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP);
}
and here the manifest:
<activity
android:name=".activities.MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".activities.DefaultLauncherHelperActivity"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
This is what i get (left) vs. what i'm trying to achieve (right)
There has to be a way since Microsoft's launcher and the poco launcher are able to do this but i couldn't find a solution and unfortunately there is no open source launcher with the same functionality. Can anyone help me with this?
There is no straight forward API available for this. However, calling below code before resetting preferred launcher will result in chooser dialog as per right side image.
Use it cautiously as calling this will actually resets user home app preference and effect can be seen outside of your home app as well.
try {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MAIN);
intentFilter.addCategory(CATEGORY_HOME);
intentFilter.addCategory(CATEGORY_DEFAULT);
Intent intent1 = new Intent(Intent.ACTION_MAIN);
intent1.addCategory(CATEGORY_HOME);
intent1.setComponent(null);
Class<?> clazz = Class.forName("android.app.AppGlobals");
Class.forName("android.content.pm.IPackageManager")
.getMethod("setLastChosenActivity", new Class[] { Intent.class, String.class, int.class, IntentFilter.class, int.class, ComponentName.class })
.invoke(clazz.getMethod("getPackageManager", new Class[0]).invoke(clazz, null),
new Object[] { intent1, intent1.resolveTypeIfNeeded(context.getContentResolver()), Integer.valueOf(65536), intentFilter, Integer.valueOf(1081344), intent1.getComponent() });
} catch (Exception ignore) {
}
Related
My question is how to start app A within app B that appears app A is within app B through deep linking?
In the first picture below, the Debug app appears as a separate app from Slack (new code, Firebase deep linking). In the 2nd picture, the Debug app appears to be within the Slack app (old code, Android deep linking). I want to use Firebase deep linking and show the Debug app within other apps (Slack, Gmail etc).
Can anyone please go through my code below and let me know how I can achieve this?
Old code, Android deep linking:
AndroidManifest
<activity
android:name=".activity.SplashScreenActivity"
android:screenOrientation="portrait"
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with "abc://flights” -->
<data
android:host="sales"
android:scheme="abc" />
<data
android:host="deals"
android:scheme="abc" />
</intent-filter>
</activity>
Activity:
Intent intent = new Intent(SplashScreenActivity.this, BottomNavBarActivity.class);
//Deep Linking Content
Uri deepLinkData = getIntent().getData();
if (deepLinkData != null) {
intent.putExtra(EXTRA_DEEP_LINK, deepLinkData.getHost());
}
startActivity(intent);
overridePendingTransition(R.anim.splash_fade_in, R.anim.splash_fade_out);
finish();
New Code, Firebase deep linking:
AndroidManifest:
<activity
android:name=".activity.SplashScreenActivity"
android:screenOrientation="portrait"
android:theme="#style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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:host="abc.app.goo.gl"
android:scheme="http"/>
<data
android:host="abc.app.goo.gl"
android:scheme="https"/>
</intent-filter>
</activity>
Activity:
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// Get deep link from result (may be null if no link is found)
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
// Start the activity through intent, same as before.
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.v(TAG, "Firebase deep link failure");
}
});
Its dependent on the app which sends the intent, for example whether they pass FLAG_ACTIVITY_NEW_TASK or not. I suspect the difference here is how Slack is handling the links - they may treat web URLs differently than other format ones (your old links have non-standard schemes).
Create a method openApp(), and call it accorrding to your need.
public void openAnApp()
{
Boolean flag=false;
try{
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
final PackageManager packageManager = getActivity().getPackageManager();
Intent intent1 = new Intent(Intent.ACTION_MAIN, null);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resInfos = packageManager.queryIntentActivities(intent1, 0);
ActivityInfo activity = null;
//getting package names and adding them to the hashset
for(ResolveInfo resolveInfo : resInfos) {
System.out.println("apap="+resolveInfo.activityInfo.packageName);
if(resolveInfo.activityInfo.packageName.equals("your.app.packagename"))
{
flag = true;
activity = resolveInfo.activityInfo;
break;
}
}
if (flag) {
// final ActivityInfo activity = app.activityInfo;
final ComponentName name = new ComponentName(activity.applicationInfo.packageName,activity.name);
intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(name);
getActivity().startActivity(intent);
//startActivity(Intent.createChooser(intent , "Send image using.."));
} else {
Uri uri=Uri.parse("market://details?id=your.app.packagename");
Intent goToMarket=new Intent(Intent.ACTION_VIEW,uri);
try{
startActivity(goToMarket);
}catch(ActivityNotFoundException e){
Toast.makeText(getActivity(),"Couldn't launch the market",Toast.LENGTH_SHORT).show();
}
}
} catch (Exception e) {
Toast.makeText(getActivity(), "Something went wrong", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
I have to show prompt when a new Application is installed or uninstalled in the device,so far its working fine. The only problem is prompt is coming even when Application is updated.
How to stop BroadCastReceiver from triggering on Application update.
<receiver android:name=".WeepingReceiver">
<intent-filter android:priority="100">
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
BroadCast
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_PACKAGE_INSTALL)
|| intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
context.startActivity(new Intent(context, NewAppActivity.class).
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK).putExtra(Utility.NEW_PACKAGE_NAME, packageName));
}
Try this
Bundle extras = intent.getExtras();
if (extras.containsKey(Intent.EXTRA_REPLACING) && extras.getBoolean(Intent.EXTRA_REPLACING))
{
//do nothing here it is condition of updating your existing app
}else
{
//do your code here
}
Application Update is re-installing a new app so it is correct that your receiver receives the event as PACKAGE_ADDED. Thus, you cannot stop your broadcast from receiving the event.
However, you can validate if the intent is being updated by checking whether the package name was there before. You can have a list of installed apps' package names and store. Then check as you are doing:
if (intent.getAction().equals(Intent.ACTION_PACKAGE_INSTALL)
|| intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
|| !packageList.contains(packageName))
You could get your package list by:
final PackageManager pm = getPackageManager();
//get a list of installed apps.
List<ApplicationInfo> packages = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo packageInfo : packages) {
packageList.add(packageInfo.packageName);
}
Just try to change your if condition as
if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED) &&
(intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED))) {
context.startActivity(new Intent(context, NewAppActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Utility.NEW_PACKAGE_NAME, packageName));
}
And manifest
<receiver android:name=".WeepingReceiver">
<intent-filter android:priority="100">
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
I want to launch app using my own app but not by giving the package name, I want to open a custom URL.
I do this to start an application.
Intent intent = getPackageManager().getLaunchIntentForPackage(packageInfo.packageName);
startActivity(intent);
Instead of package name is it possible to give a deep-link for example:
"mobiledeeplinkingprojectdemo://product/123"
Reference
You need to define a activity that will subscribe to required intent filters:
<activity
android:name="DeepLinkListener"
android:exported="true" >
<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:host="host"
android:pathPattern="some regex"
android:scheme="scheme" />
</intent-filter>
</activity>
Then in onCreate of your DeepLinkListener activity you can access the host, scheme etc.:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent deepLinkingIntent= getIntent();
deepLinkingIntent.getScheme();
deepLinkingIntent.getData().getPath();
}
Perform check on path and again fire a Intent to take the user to corresponding activity. Refer data documentation for more help.
Now fire a Intent:
Intent intent = new Intent (Intent.ACTION_VIEW);
intent.setData(Uri.parse(DEEP_LINK_URL));
Don't forget to handle the exception. If there is no activity that can handle the deep link, startActivity will return an exception.
try {
context.startActivity(
Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(deepLink)
}
)
} catch (exception: Exception) {
Toast.makeText(context, exception.localizedMessage, Toast.LENGTH_LONG).show()
}
I am new to NFC. I tried to connect to NFC and share the text data from a device to another device.
I install my application on both devices and in one device I open my application and start tapping the device to the another device in order to transmit data over Beam.
On the other device, my application opens due to the Beam interaction. However, the activity is started with the default MAIN intent action and not with TAG_DISCOVERED (or similar NFC intent). Every time it does the same thing.
Also, it's not calling the onNewIntent() method. I tried to call onNewIntent from onCreate, but the intent action is still MAIN in that case. I expected to receive an NFC intent for the Beam interaction. So can you please tell me where did I go wrong?
In this code I am not sharing the data. I just need the tag first.
Manifest:
<activity android:name="com.framentos.hellonfc.MainActivity"
android:clearTaskOnLaunch="true"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfc_tech_filter" />
</activity>
Java code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ctx = this;
// startHandler();
_handler = new Handler();
Button btnWrite = (Button) findViewById(R.id.button);
message = (TextView) findViewById(R.id.nfcwriteTag);
btnWrite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
adapter = NfcAdapter.getDefaultAdapter(this);
if (adapter == null) {
message.setText("NFC is not supported on this device.");
}
if (adapter.isEnabled()) {
message.setText("NFC is Enabled on this device.");
} else {
message.setText("Please enable NFC to communicate.");
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(
NfcAdapter.ACTION_NDEF_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter tagTech = new IntentFilter(
NfcAdapter.ACTION_TECH_DISCOVERED);
tagTech.addCategory(Intent.CATEGORY_DEFAULT);
IntentFilter tagDetect = new IntentFilter(
NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetect.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[] { tagDetected, tagTech ,tagDetect};
// handleIntent(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.i(getPackageName(), "on New Intent is called...!");
handleIntent(getIntent());
}
public void onPause() {
super.onPause();
WriteModeOff();
}
#Override
public void onResume() {
super.onResume();
WriteModeOn();
}
private void WriteModeOn() {
writeMode = true;
adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters,
null);
}
private void WriteModeOff() {
writeMode = false;
adapter.disableForegroundDispatch(this);
}
From the code that you posted in your question, I assume that you did not register your application to send a specific NDEF message. In that case, if your app is open on one device, Android will automatically Beam an NDEF message containing a URI record with the Play Store link of your app and an Android Application Record (AAR) to the other device.
So your second device will receive the following NDEF message:
+---------------------------------------------------------------------------------------+
| WKT:URI | http://play.google.com/store/apps/details?id=your.package.name&feature=beam |
+---------------------------------------------------------------------------------------+
| EXT:android:com:pkg | your.package.name |
+---------------------------------------------------------------------------------------+
What happens now if your app is not already open on the second device is, that the Android Application Record (second record) will force your app to be started. However, looking at your manifest, you do not have an intent filter that matches the first record of this NDEF message (the Play Store URL). Consequently, Android thinks that you do not expect an NDEF message and uses the standard android.intent.action.MAIN (with category android.intent.category.LAUNCHER) to start your app (or rather the first activity of your app that has an intent filter for action MAIN with category LAUNCHER).
In order to receive an NFC intent together with the whole NDEF message in your app, you would need to define a proper intent filter that matches the first record in the above NDEF message:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http"
android:host="play.google.com"
android:pathPrefix="/store/apps/details?id=your.package.name" />
</intent-filter>
Now, Android will recognize that your app/activity is ready to receive an NFC intent and pass the NDEF_DISCOVEREDintent to your activity. Note that you still won't receive that intent through onNewIntent() if your app is not already running. Instead you can get the intent that started your activity with the activity's getIntent() method. E.g. in onCreate/onStart/onResume, you could use
Intent intent = getIntent();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage ndefMessage = null;
Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if ((rawMessages != null) && (rawMessages.length > 0)) {
ndefMessage = (NdefMessage)rawMessages[0];
}
// TODO: do something with the received NDEF message
}
to get the intent and the NDEF message.
Regarding the intent filters you already have in your manifest:
NDEF_DISCOVERED:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
An NDEF_DISCOVERED intent filter without a <data ... /> tag will never trigger on many NFC devices. You should always define what specific data you expect to be present in the launching NDEF record.
TECH_DISCOVERED:
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfc_tech_filter" />
You should only use this intent filter if you wan't you application to be started upon detection of a certain NFC tag technology (you define the specific technologies in the nfc_tech_filter.xml file. For your Beam scenario, you would not use such an intent filter. Btw. the <category ...> tag is not used for this intent filter.
TAG_DISCOVERED:
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
This intent filter should normally not be used in the manifest. It is meant as a fallback that triggers if no other app could possibly handle a detected NFC tag. Using this can lead to bad user-experience (e.g. your app being started for tags it can't or does not actually want to handle). It is primarily available for backward compatibility with API level 9 (?).
In order to also catch the NFC intents if your activity is already started, you could register for the foreground dispatch like this (in the onResume method):
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
That way, you will receive any NFC discovery events that occur while your activity is in the foreground in your activity's onNewIntent method. They will be dispatched through an ACTION_TAG_DISCOVERED intent.
I'm trying to use another project I already have in my main project but when I call the activity from it, I get the ActivityNotFoundException: have you declared this activity in your androidManifest?. This is what i've done :
final Intent intent = new Intent();
ComponentName cName = new ComponentName("com.GCM.pushnotifications","com.GCM.pushnotifications.RegisterActivity");
intent.setComponent(cName);
startActivity(intent);
and in the manifest :
<activity android:name=".RegisterActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
What am i doing wrong? this is what I found on the net.
You just have to provide the activity class name as the second parameter of ComponentName
Change this
ComponentName cName = new ComponentName("com.GCM.pushnotifications","com.GCM.pushnotifications.RegisterActivity");
to
ComponentName cName = new ComponentName("com.GCM.pushnotifications","RegisterActivity");