Always retain a single instance of my MainActivity (launcher app) - java

I'm currently working on an app that is supposed to replace the devices whole UI. Therefore I'm using <category android:name="android.intent.category.HOME"/> in order to call it whenever I press the home button, replacing the devices launcher.
This app should always have just one instance of its MainActivity running and when the home button is pressed, it shouldn't start a new Activity but resume to the existing one.
I've played with the different launchModes a bit and it looks like singleInstance is by best bet. But for some reason the home button gets rendered useless and I'm unabled to return the launcher. Sporadically sometimes it works and when it works, it works just like I've intended it to be.
standard launchmode starts a new instance of the launcher whenever I press the home button, which leads to several issues, since the launcher is supposed to do some things, like opening other apps and resuming to the launcher, whenever the MainActivity is created. This leads to a loop: launcher starts apps -> resumes to launcher by creating a new Activity -> launcher starts apps...
That behaviour could be fixed somehow, but still it doesn't solve the root of its cause
singleTop does the same.
singleTask does the same.
singleInstance retains the Activity and whenever I press the home button, the launcher just resumes with its last state. BUT for some reason, the home button sometimes doesn't work. Meaning that the App won't come to the foreground. Even closing all apps doesn't return me to the launcher -> the device kind of gets bricked since you can't do anything
Edit:
Couldn't really find a solution. I'm pretty sure it's a bug on googles side. I couldn't find the cause, but I've settled with a workaround.
I've created a new activitiy called "LoadingActivity" which literally only loads the activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
}
#Override
protected void onResume() {
super.onResume();
startActivity(new Intent(LoadingActivity.this,MainActivity.class));
}
The LoadingActivitys launchMode is "standard" while the "MainActivity" remains "singleInstance". This fixes everything for now. If I find a better solution in the future, I can simply delete the Activity and make the MainActivity the main activity again.
Edit2, per request, here's the manifest prior to the workaround:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="[package]">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.READ_LOGS" tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
<application
android:allowBackup="true"
android:icon="#drawable/logo"
android:label="#string/app_name"
android:roundIcon="#drawable/logo"
android:supportsRtl="false"
android:requestLegacyExternalStorage="true"
android:theme="#style/AppTheme">
<activity
android:name="[package].MainActivity"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="true"
android:configChanges="uiMode|fontScale">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service android:name=".MediaServiceBrowser">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<service android:name=".LocationService" />
<service
android:name=".NotificationListener"
android:enabled="true"
android:exported="true" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>

Related

Android Deeplink opens application twice

i used webview to create app and implemented deep linking like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ariagp.myapplication">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<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:scheme="https" android:host="mysitename.com" />
</intent-filter>
</activity>
</application>
</manifest>
it will asking my for open with my application before open the links, but the problem is:
two applications will open in the phone task manager (the application does not open in the previous application which is running):
what is the solution?
Add android:launchMode="singleTask" in declared activity in AndroidManifest.xml.
And then, in your activity, you should override onNewIntent() method and you will get arguments there.
Ok, so you don't actually have a problem here.
This is just how deep links work. If you open one in a certain app, the deep link will open your app but in the same window the deep link was originally in.
Your app will have two instances in a way.
You could go to you web brower app and click share and any app that pops up. They all open in the web browsing app window. So there is nothing to worry about. I had the same problem myself before I realised that it is just how things work.
Some apps are just verified to open other apps if they implement the browsability.
Add android:launchMode="singleTask" in declared activity in AndroidManifest.xml.
And then, in your activity, you should override onNewIntent() method and call mNavController.handleDeepLink(intent) there.
It is worth mentioning that you are now able to get your arguments using, private val args: XFragmentArgs by navArgs()in your fragment.

"Default activity not found"

I keep getting the "Default activity is not found" error whenever I want to try and run my app. I have tried 'clean project', 'invalidate caches/restart' and restarted my computer.
UPDATE: I tried running different projects, but they all give the same error too. This makes me think there must be something wrong with settings.
It works perfectly for my friend, but I get the error that the "Default activity is not found". If I try to launch a specific activity it also says the activity is not found. This is the code in the androidmanifest.xml file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="ek97.fhict.theapp">
<!-- To auto-complete the email text field in the login form with the user's emails -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission. ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#drawable/loqate_logo"
android:label="#string/app_name"
android:roundIcon="#drawable/loqate_logo"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".bottomnavigationfinal"
android:label="#string/title_activity_bottomnavigationfinal"/>
<activity android:name="Settings" />
<activity
android:name=".Login"
android:label="#string/title_activity_login" />
<activity
android:name=".Registration"
android:exported="true"
android:label="#string/title_activity_registration" />
<activity android:name=".Navigation" />
<activity android:name=".SplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
The API key for Google Maps-based APIs is defined as a string resource.
(See the file "res/values/google_maps_api.xml").
Note that the API key is linked to the encryption key used to sign the APK.
You need a different API key for each encryption key, including the release key that is used to
sign the APK for publishing.
You can define the keys for the debug and release targets in src/debug/ and src/release/.
-->
<activity
android:name=".GoogleMaps"
android:label="#string/title_activity_google_maps" >
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
</activity>
<activity android:name=".Profile" />
</application>
</manifest>
If it works for your friend and it is the same code it could be that all you need to do is to refresh the cache of your IDE:
File -> Invalidate Caches / Restart
Try this::
Your manifest is missing default activity
Add to any activity as you consider a default activity
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
I finally solved it by making sure all implementation versions were the same, there were some in a 28.0.0 version and others in a 26.1.0. version. Now the app works fine on the emulator. Thanks for your suggestions though

Parse sending notifications in dashboard but Android device not receiving

I've successfully created a Parse app and I'm using the application keys in my Android app implementation. I've followed all the steps here https://www.parse.com/apps/quickstart?onboard=#parse_push/android/existing (registration required unfortunately, but it's an instant process) and when I send a test push notification it is dispatched from the dashboard and is even received by a virtual device I'd set up as a control but nothing happens on a physical device. I thought it might be a socket issue so I alternated between wifi and 3G connections with no success
Here's the code I'm using:
Main:
package com.wetu.chronicle;
import android.os.Bundle;
import org.apache.cordova.*;
import com.parse.Parse;
import com.parse.ParseAnalytics;
import com.parse.ParseInstallation;
import com.parse.PushService;
public class chronicle extends DroidGap
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.loadUrl(Config.getStartUrl());
Parse.initialize(this, "xxxxxxxxx", "yyyyyyyyyyyy");
PushService.setDefaultPushCallback(this, chronicle.class);
PushService.subscribe(this, "Newsfeed", chronicle.class);
ParseInstallation.getCurrentInstallation().saveInBackground();
}
}
Android Manifest:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!--
IMPORTANT: Change "com.parse.starter.permission.C2D_MESSAGE" in the lines below
to match your app's package name + ".permission.C2D_MESSAGE".
-->
<permission android:protectionLevel="signature"
android:name="com.wetu.chronicle.permission.C2D_MESSAGE" />
<uses-permission android:name="com.wetu.chronicle.permission.C2D_MESSAGE" />
<application
android:debuggable="false"
android:hardwareAccelerated="true"
android:icon="#drawable/icon"
android:label="#string/app_name" tools:ignore="HardcodedDebugMode">
<activity android:name="chronicle" android:label="#string/app_name"
android:theme="#android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParseBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver android:name="com.parse.GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<!--
IMPORTANT: Change "com.parse.starter" to match your app's package name.
-->
<category android:name="com.wetu.chronicle" />
</intent-filter>
</receiver>
</application>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
I'm available to clarify any questions you might have and/or post additional code.
Its most likely because your app isn't active in the background. A lot of my android apps have been struggling from this. A decent solution might be to make sure there is an alarm clock that wakes the app up. I've never dealt with parse though so this may not work, its worked for me in other scenarios though

Android multiple instances of Activity

I'm having a problem where multiple instances of an activity are present in the backstack.
e.g.
Stack: A > B > C
Press home and use some other apps then relaunch my app
Stack: A > B > C > A
I would expect my app to resume with activity C, which in most cases it does, but occasionally it resumes with activity A and then when i press back it goes to activity C.
I know this problem is not caused by android killing my app as if this was the case it would resume with activity A as the root.
I have tried altering the launchMode attribute to singleTask and singleInstance but this results in activity A being resumed as the root, which is not the behaviour that i want, i want it to go back to the previously viewed activity with the backstack intact (I understand this is not always possible if the OS kills the app, which is fine).
I dont believe setting the launchMode attribute to singleTop would be useful either as this would prevent A > A situations but not A > B > A or A > B > C > A etc
Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package=""
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto"
>
<application android:label="#string/app_name" android:debuggable="false"
android:name=""
android:icon="#drawable/icon" android:theme="#style/CustomTheme"
android:targetSdkVersion="8" android:minSdkVersion="4">
<activity android:name=".activity.A"
android:windowSoftInputMode="stateUnchanged|adjustPan"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.B"
android:screenOrientation="portrait"/>
<activity android:name=".activity.C"
android:screenOrientation="portrait" />
<activity android:name=".activity.D"
android:screenOrientation="landscape"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen" />
<activity android:name=".activity.E"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen" />
<service android:name=".service.S" />
<uses-library android:name="com.google.android.maps" />
</application>
<uses-sdk android:minSdkVersion="4"
android:targetSdkVersion="8"
/>
<supports-screens
android:largeScreens="false"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"
/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Any advice would be greatly appreciated
David
The behavior you're looking for is what singleTask was meant for.
The problem, then, is why resuming your app pops back to the root. If it's just due to the usual way the system works (e.g., 30-minute timeout), and you really want to prevent that, you could use android:alwaysRetainTaskState="true".

Intent resolved to different process when running Unit Test in Android

I have a small application that uses two activities. Both the activities inherit from MapActivity and display a map (com.google.android.maps).
Since the Android Google Map documentation says
Only one MapActivity is supported per
process. Multiple MapActivities
running simultaneously are likely to
interfere in unexpected and undesired
ways.
I modified my manifest to run the two activities in two different processes (I have removed some lines to make it short):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="#drawable/icon"
android:label="#string/app_name"
android:theme="#android:style/Theme.Light">
<uses-library android:name="com.google.android.maps" />
<activity
android:name=".Activity1"
android:process=".Activity1">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>Unit
</activity>
<activity
android:name=".Activity2"
android:process=".Activity2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>
Now the application runs fine but I have problems when I what to run Unit Tests on both the Activities.
For example:
package com.example.myapp;
public class Activity1Test extends ActivityInstrumentationTestCase2<Activity1> {
Activity1 mActivity;
public Activity1Test() {
super("com.example.myapp.Activity1", Activity1.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
setActivityIntent(new Intent());
mActivity = getActivity(); //An exception is thrown at this line
}
}
When I call the getActivity() method an exception is thrown:
java.lang.RuntimeException: Intent in process com.example.myapp resolved to different process .Activity1: Intent { flg=0x10000000 cmp=com.example.myapp/.Activity1 }
at android.app.Instrumentation.startActivitySync(Instrumentation.java:377)
at android.test.InstrumentationTestCase.launchActivityWithIntent(InstrumentationTestCase.java:119)
at android.test.ActivityInstrumentationTestCase2.getActivity(ActivityInstrumentationTestCase2.java:100)
at com.example.myapp.Activity1Test.setUp(Activity1Test.java:28)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:520)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
Is there a way to make the Unit Test to "resolve" the correct process?
Instrumentation runs all of your application components in the same process.

Categories

Resources