Seamless replacement of an app with another app - android

I have two very similar news apps on the Play Store, let's call them app A and app B (B is similar to A, but has advanced features). Now I want all my users of app A to seamlessly migrate to app B. For that, I wish to push an app update to A, with a button titled "Upgrade to B now". When the button is tapped, I want to do two things. One is to check if B is not installed, and if it isn't I want to start downloading the app B, then proceeding to install it. Once the app is installed or if it were already installed, I wish to pass on login information to the app B and open its home page(probably using intents). The second is to delete app A once migrated to B. How can this be accomplished as seamlessly as possible so as to give the users of app A, an opportunity to start using the much better app B, with the transition happening automagically behind the scenes?
I went through a few posts with people downloading custom apks, and then installing it programmatically. So probably what I could do is download the apk of B from my server, and then install it after INSTALL_PACKAGES permission is granted by the user. Is this the right way to go about this?

once the user clicks the install button. check whether the app B installed otherwise you can direct him to the playstore of app B, use the below code to achieve this.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add respective layout
setContentView(R.layout.main_activity);
// Use package name which we want to check
boolean isAppInstalled = appInstalledOrNot("Package name of your App B");
if(isAppInstalled) {
//This intent will help you to launch if the package is already installed
Intent LaunchIntent = getPackageManager()
.getLaunchIntentForPackage("Package name of your App B");
startActivity(LaunchIntent);
Log.i("Application is already installed.");
} else {
// Redirect to play store
Intent i = new Intent(android.content.Intent.ACTION_VIEW);
i.setData(Uri.parse("https://play.google.com/store/apps/details?id=Package name of your App B"));
startActivity(i);.
Log.i("Application is not currently installed.");
}
}
private boolean appInstalledOrNot(String uri) {
PackageManager pm = getPackageManager();
try {
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
}
return false;
}}
Hope i gave you an solution :)

Related

Test that app launches another app in Android

I have a very simple activity, that redirects the user to the app's Play Store page, when the button is clicked:
public class MyActivity extends AppCompatActivity {
private static final String PLAY_STORE_URI =
"market://details?id=" + BuildConfig.APPLICATION_ID;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
findViewById(R.id.go_to_play_store).setOnClickListener(this::goToPlayStore);
}
public void goToPlayStore(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(PLAY_STORE_URI));
startActivity(intent);
}
}
Is it possible to write a test to check that the PlayStore is launched when the button is clicked? Better, is it possible to verify it shows the expected page?
I know that by using ActivityMonitors transitions between Activities can be tested. I also know that I can verify the Intents being sent using Espresso Intents. But can I actually check that a user action launches another app?
I would click the button, then use:
intended(allOf(
hasAction(Intent.ACTION_VIEW),
hasData("https://play.google.com/store/apps/...your app...")
))
I would suggest a slightly different question - is it you app's job to verify that? You'd be testing not your own app but Android OS and Google's Play Store App.
The way I've been approaching it is by being explicit about the boundaries, and aware of the possible scenarios.
What I mean by that is, extract the Intent manipulation and invocation logic into a thin service (that's the being explicit about boundaries part) and test your Activity correctly interacts with it (by constructing the Intent appropriately).
The part about possible scenarios is being aware of what can happen. For example what does your app do (if anything) if the user doesn't have Play Store on their phone.

Android send intent issue

I have added intent-filter to my activity, so I can receive send intents that contain text, I have set data to text/plain, everything works fine, I can choose my application from the picker, it is opening, showing me the text, but, when I minimalize my app, and then click again on ColorNote application (I am using it to take my notes) it is opening my app instead of ColorNote, anyone ever had similiar issue? And know how to resolve it?
#Edit
Do not know why I am getting - points, if you think it is easy question you could write how to solve it instead of giving -.
By the way, I don't think any code is neccessary in here right now.
That's the way it should behave. For e.g. if an application starts another and you leave the application without closing it's task you will return to the intent it started. I can reproduce this with apps which take me to the play store:
What I did:
First test:
Start application
Let it start the Play Store
Press Home
Start the application again
welcome back to the app store
Second test:
Start application
Let it start the Play Store
Close application (Task Manager)
Start the application again
welcome back to the application
Okay now on how to solve your problem. I would check if my application was started by another intent -> http://developer.android.com/training/basics/intents/filters.html
boolean wasStartedByOtherApp = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
if (intent.getType().equals("text/plain")) {
wasStartedByOtherApp = true;
}
}
#Override
protected void onPause() {
super.onPause();
if(wasStartedByOtherApp){
finish();
}
}
I have not tested this. This should just give you an idea on how to solve the issue you are facing. But be warned as an android user I would be angry if an application closes itself just when I want to check my calendar if I have time and would like to return to the intent the app just started.

recover an app from stack or app in background

I have a small application with two activities.
The first activity has a button, pressing this button opens a second activity (the first is paused)
This second activity launch notification.
With the notification released, leave the application by pressing the "home" button and let the application on the stack or in background.
When I click on the notification, I want the application to retrieve the activity that was on the screen.
I am recovering the application using the package name:
Intent intent = null;
PackageManager manager = getPackageManager();
try {
intent = manager.getLaunchIntentForPackage("hello.com.vierco.pruebas");
if (intent == null)
throw new PackageManager.NameNotFoundException();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.putExtra("hola", notificationData);;
} catch (PackageManager.NameNotFoundException e) {
}
I get retrieve the application, but always opens the activity number "one" because is reopening.
Someone can tell me how I can retrieve the application? retrieve it from the stack, not open it again. there is any intent?
Wear two days trying without success, I have also searched many forums but without success
A practical example would be grateful because I'm going slightly mad

dont allow to Open own application from another app

I have seen some of example where other application can start my application by package name. Due to security reason I want to prevent this kind of access for other application.
I want to prevent this (Open another application from your own (intent)) kind of acess
Edit
For Example, If thirdparty application knows my application's package name they can launch my app from their application like this way,
Intent i;
PackageManager manager = getPackageManager();
try {
i = manager.getLaunchIntentForPackage("app package name");
if (i == null)
throw new PackageManager.NameNotFoundException();
i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i);
} catch (PackageManager.NameNotFoundException e) {
}
Now to prevent this, i have added export = "false" in my launching activity as well as added permission to lauching activity. Now due to this, it is preventing thirdparty app to launch my application but android OS launcher is also not able to launch application.
I imagine if you don't provide the launch intent in your Android manifest, other apps (including your homescreen) won't be able to launch your app.
You can try these attributes in manifest:
http://developer.android.com/guide/topics/manifest/activity-element.html#exported
http://developer.android.com/guide/topics/manifest/service-element.html#exported
http://developer.android.com/guide/topics/manifest/receiver-element.html#exported
http://developer.android.com/guide/topics/manifest/provider-element.html#exported
As also mentioned in those links, you can try another approach by using permission with the protectionLevel = signature
http://developer.android.com/guide/topics/manifest/permission-element.html#plevel
I hope below solution by comparing package name will help you protecting your activity to be launched by other app.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ComponentName componentName = this.getCallingActivity();
if(componentName == null) {
finish();
} else if("<intended package name>".equals(componentName.getPackageName())) {
finish();
} else {
String data = getIntent().getDataString();
...
}
}
}
Above solution is base on below assumption:
Only one app with a particular package name can exist on Google Play.
If a user tries to install an app whose package name already exists on the device, the installation either will fail or will overwrite the previously installed app.

Android - Open an external app and then call an intent URI

I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
String intentURI = "B://this/123";
try {
intent = Intent.parseUri(intentURI, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
Logger.appendInfoLog("Something went wrong with B", TAG);
e.printStackTrace();
Logger.appendErrorLog(e.getMessage(), TAG);
finish();
}
startActivity(intent);
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Is there a way to open that app 'B' first or check its running status and then make the intent URI call. I will not get control back from that app and once I go to the other app I dont have any control on it until the user presses the back button to return to my app.
UPDATE:
I want to start app 'B' first and then call the intent programmatically. Is this possible
UPDATE
I Ran the Catlog app to check what message comes up in app B. It just shows file 123.file (The one i am trying to play in the app B's video player) not found, but when the app is running in the background it goes through fine. It also shows a warning
java.lang.NullPointerException: PrintLn needs a message
and then it says
Activity displayed, but mediaplayer.is not playingVideo
Also the other app is written in flash and packaged as a native app on adobe air
I have an app 'A'. I am opening another app 'B''s video player and playing a video using an intent URI call like so
No, you are not. You can tell that by reading the code -- there is no startActivity() call in that code block.
Now the necessary condition is for that app 'B' to be open in the background for this to work.If the app is closed(killed by Android or manually) or it has crashed,this throws an error.
Then apparently app B has a bug. Please contact the author of app B for assistance.
You can get a list of running apps easily.
ActivityManager manager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
And you can launch apps just as easily.
Intent LaunchIntent = getPackageManager().getLaunchIntentForPackage("com.package....");
startActivity(LaunchIntent);
You can't, however, launch an app into background, so this might not solve your issue.
You don't need to open that app 'B' first. Just check if that app is running with:
// Get running processes
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningProcesses = manager.getRunningAppProcesses();
So now you have all the running processes in runningProcesses. Just iterate over the values to find out if your app 'B' is running. An example of this iteration can be found here:
ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);
List l = am.getRunningAppProcesses();
Iterator i = l.iterator();
PackageManager pm = this.getPackageManager();
while(i.hasNext()) {
ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo)(i.next());
try {
CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName, PackageManager.GET_META_DATA));
Log.w("LABEL", c.toString());
}catch(Exception e) {
//Name Not FOund Exception
}
}
Define the intent listener inside of your manifest file.
See the docs for details.
Basically, what you would do is in your AndroidManifest.xml of App B (the app you want to start with the Intent), add a section like:
<receiver android:name=".MyIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Then inside of your MyIntentReceiver class, you would define the code to handle the intent.
public class MyIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
// handle intent here
}
}
I think your problem is that Intent.parseUri() returns an Intent with the action ACTION_VIEW (see http://developer.android.com/reference/android/content/Intent.html#parseUri%28java.lang.String,%20int%29). And from your code, I gather that android does not recognize that it actually has to start activity B for this. ACTION_VIEW is generic, so id does what seems to be most appropriate. If activity B does not fall into that category (probably not the standard video viewing app), it' will not get launched. I would suggest the following:
Find out how to launch activity B (e.g. Intent i = new Intent.("com.packageB.ActivityB"); or similar. Developer should be able to tell you.
Use i.setData() to set the data of your intent to your file.
Use startActivity()
For step 2, there may be other ways Activity B needs the Uri passed. You can get this information from the developer as well.
Using startActivity rids you of having to check whether App B is running. Something that Android does anyway.
I think it is possible to start another apk from your own, just check this answer. The problem is that if you dont know their uri, it will be hard to do what you propose.
Now you can either ask the developers for the proper names or take a look at your own risk (i don't know about its legality).

Categories

Resources