I'm trying to develop an android app for the SDK version 30 that (when a button is clicked) starts listening to what apps are opened on the phone. If it detects the user opening Whatsapp, it is supposed to show a LockScreen activity over Whatsapp that makes you answer a math question before being able to use Whatsapp.
I know this can be done as their are apps like QualityTime or Forest that have similar features to restrict you from using certain apps, but I am a newbie when it comes to programming (probably obvious from my code) and feel totally stuck.
I have already figured out how to detect what app the user opened in the last second with code from stack overflow:
public String getCurrentApp() {
String topPackageName = "None";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService("usagestats");
long time = System.currentTimeMillis();
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (!mySortedMap.isEmpty())
{
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
}
return topPackageName;
}
I have another function that is started when the user clicks the button in my app to "activate" the listening process. This function keeps checking if the user opens Whatsapp and is then supposed to display the Lockscreen activity on top:
public void startListening(View view)
{
System.out.println("Lock activated.");
while (activated) {
String currentlyRunningApp;
currentlyRunningApp = getCurrentApp();
if (currentlyRunningApp.equals("com.whatsapp"))
{
System.out.println("Whatsapp detected. Showing Lockscreen...");
Intent i = new Intent(this,LockScreen.class);
startActivity(i);
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(All of the code I have shown is in my MainActivity btw.)
I have the following permissions granted to my app:
android.permission.PACKAGE_USAGE_STATS (for the getCurrentApp() function)
android.permission.SYSTEM_ALERT_WINDOW (as suggested here)
android.permission.ACTION_MANAGE_OVERLAY_PERMISSION (although I am not sure I even need this one)
My problem is, that instead of showing the Lockscreen activity I created, it only shows a blackscreen for the user. The Lockscreen activity itself works fine if I let the user open it through a button on the mainActivity, so the issue seems to really be that I can not properly show an activity if my app is running in the background and I want to display it on top of Whatsapp.
I have tried to look through similar questions, but all of the posts on here with similar use cases seem to be very old and outdated (i.e. this or this), as the newer versions seem to have way tighter security restrictions.
I also tried to do it with a screen overlay instead of an activity (using this source), but this doesn't even give me a blackscreen - just does nothing...
I am also aware that there are probably better ways to program the whole "listening and checking for whatsapp" part - i.e. with a service instead of a while-loop or something, but I only found out about services while researching this problem and I'd like to fix the blackscreen issue first.
After lots of trial and error I figured out that the issues was indeed caused by a missing permission, but one that I could not find on any stack overflow answer related to black screen problems. On top of that, I believe it's an issue that only occurred because I used a Xiaomi device for testing.
There are currently two separate permissions for displaying screens over other apps that you will need to grant:
Display over other apps, also called Display pop-up window. This is the android.permission.ACTION_MANAGE_OVERLAY_PERMISSION that I wasn't sure was even needed. So to emphasize, I definetly do need this permission.
Display pop-up windows while running in the background. This is the permission I was missing.
After I allowed them both (which you can do under Settings > Apps > Manage Apps > Your App > Other Permissions) everything worked fine.
To direct the user directly to the settings menu where they can allow these permissions, I used the code from this stack overflow answer. This is also where I got the info that it's a xiaomi-specific "issue".
Related
I am stuck with a functionality of the Firebase SDK (Auth package) regarding the Scenes and it's integration. Here's how they work:
1st: Loading Scene
Here, I just added the FirebaseInit code EXACTLY as suggested by Patrick, which it's only function is to call the next scene (Login/Register) once everything loads correctly.
2nd: Login/Register Scene
Here I do all the Login AND ALSO the register logic. I set up a button that alternates between the two (Activating the respective parent gameObject within the Canvas). Once the user log's in, the 3rd scene comes into play.
3rd: App's Main Screen Scene
Main Screen of the app, where the user can LOGOUT and return to the Login Scene.
Problem
I added the 'LoadSceneWhenUserAuthenticated.cs' in the 2nd Scene, and it works (kind of).
It actually does what it is supposed to. If I log in, quit the game without loging out, and open it again, it does come back directly to the 3rd scene. BUT some things are happening and they aren't supposed to.
First
When I Sign Up a user, I call the method 'CreateUserWithEmailAndPasswordAsync()'. Once it completes, it should activate the login screen and stay there, waiting for the user to fill in the password, but the 'FirebaseAuth.DefaultInstance.StateChanged' comes into play and forces the 3rd screen to be loaded, skipping several other steps that should be taken (email registration for example).
Second
As I mentioned in the end of number 1 above, if I try to log in to an account that does not have it's email verified, it works! (due to the 'LoadSceneWhenUserAuthenticated.cs' which is added in the scene). Code:
var LoginTask = auth.SignInWithEmailAndPasswordAsync(_email, _password);
LoginTask.ContinueWithOnMainThread(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
Firebase.FirebaseException e =
task.Exception.Flatten().InnerExceptions[0] as Firebase.FirebaseException;
GetErrorMessage((AuthError)e.ErrorCode, warningLoginText);
return;
}
if (task.IsCompleted)
{
User = LoginTask.Result;
if (User.IsEmailVerified == true)
{
UIControllerLogin.instance.MainScreenScene();
}
else
{
warningLoginText.text = Lean.Localization.LeanLocalization.GetTranslationText($"Login/VerifyEmail");
}
I know that it's possible to fix this issue by adding an extra scene just before the login scene (as Patrick does in the youtube video) but it doesn't make any sense in my app. It would actually only harm the UX of it.
Patrick's Video:
https://www.youtube.com/watch?v=52yUcKLMKX0&t=264s
I'm glad my video helped!
My architecture won't work for every game, and I tried to boil it down to the bare minimum to get folks started. You may be able to get the functionality you want by adding an additional check in HandleAuthStateChanged:
private void HandleAuthStateChanged(object sender, EventArgs e)
{
if (_auth.CurrentUser != null && !_auth.CurrentUser.IsAnonymous && _auth.CurrentUser.IsEmailVerified)
{
SceneManager.LoadScene(_sceneToLoad);
}
}
but it does sound like, at this point, you'll want to build out a more robust registration/sign in flow that fits your use case.
If you need more help, I might suggest re-posting on the community mailing list or the subreddit. Those resources may be more better suited to discussing various pros/cons of different architectures or spitballing ideas (and feel free to link to any new posts in a comment so myself or others interested can follow along).
I'm looking for a way to open the battery settings screen from an android app.
So far I found the two intents :
Intent.ACTION_POWER_USAGE_SUMMARY
Settings.ACTION_BATTERY_SAVER_SETTINGS
but none of them open this screen.
I was wondering if anyone knows of such a way. It sounds strange that an intent for something so simple doesn't exist
Settings.ACTION_BATTERY_SAVER_SETTINGS on "plain" Android versions will show the settings page you want to show.
Intent.ACTION_POWER_USAGE_SUMMARY will lead to the overview page showing the battery consumption.
Some manufactures such as Samsung build their own implementation over the system one, e.g. in this the "Battery" page. On Samsung devices, you can call this by calling the SmartManager interface directly. An code example:
if (Build.MANUFACTURER == "samsung") {
val intent = Intent()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
intent.component = ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
intent.component = ComponentName("com.samsung.android.sm", "com.samsung.android.sm.ui.battery.BatteryActivity")
}
try {
activity?.startActivity(intent);
} catch (ex: ActivityNotFoundException) {
// Fallback to global settings
startActivity(Intent(Settings.ACTION_SETTINGS))
}
} else {
startActivity(Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS))
}
It can be the case that you need additional cases for Huawei or Xiaomi as well.
Huawei can be "com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"...
...and the MIU based ones "com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity"
I know this is quite old. But a trick I use is going to the appropriate settings screen in the device settings and then while connected to the phone run:
adb shell
dumpsys window windows | grep -E 'mCurrentFocus'
This returns the package name and Activity name currently in focus.
Using that I can check in code if the intent is callable. If it is, I launch it. If it isnt, I might have better luck with a different screen that is near by or explain to the user he needs to do something manually etc... Obviously the more devices you have, the more Intents you can create and check at run time. Im sure there is a list of Intents for different devices online.
I want to check if my app is running on a background mode.
The problem is that i have many activities(list activities, map activities etc.). Initially I have tried in the life cycle's resume and pause(or the onUserLeaveHint) methods to set a static boolean as true or false and work with this way. But this obviously can't work because when I move from one activity to another, the previous one get paused.
Also, I've read here on stackoverflow that the getRunningTasks() should be used only for debugging purposes. I did a huge research but I can't find a solution. All I want to do is to be able to detect if a the app is running on a background. Can anyone propose me a way, or express any thought on how can I do that?
You can try the same mechanism (a boolean attribute) but on application side rather than activity side. Create a class which extends Application, declare it in the manifest file under <application android:name=YourClassApp>.
EDIT: I assume you know that activities aren't intended for background processing, if not you should take a look at the Services.
I don't know if this will help but you can use
getApplicaton().registerActivityLifecycleCallbacks(yourClass);
To get a birds eye view of how your activities are displayed in the FG. (For older s/w you can use this)
If your Application has a Service you could have a static get/set which accesses a static variable. Do not do this in Activities though, it causes mem leaks.
But realistically speaking there is no tidy way of tracking if your application is running or not.
I had the same problemen when overwriting the Firebase push messaging default behavior (show notifications only when in the background) I checked how Firebase did this by looking in the .class file com.google.firebase.messaging.zzb:53 (firebase-messaging:19.0.1) which appears to us getRunningAppProcesses. Mind you FireBase is created by Google them self. So I'm assuming it's pretty save to use. Cleaned up version:
List<ActivityManager.RunningAppProcessInfo> runningApps;
boolean isInForeground =false;
if ((runningApps = ((ActivityManager)this.getApplication().getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses()) != null) {
Iterator runningApp = runningApps.iterator();
int myPid = Process.myPid();
while(runningApp.hasNext()) {
ActivityManager.RunningAppProcessInfo processInfo;
if ((processInfo = (ActivityManager.RunningAppProcessInfo)runningApp.next()).pid == myPid) {
isInForeground = processInfo.importance == 100;
break;
}
}
}
there a lot of q&a about how users can rate my app within the app,
but i need just a direct link to review\rate my app to send the user by mail and not to my app page in the market because there he need to cilck review then login and then write the review and this is exhausting and not user friendly.
tnx
In order not to disturb the user with annoying forms you can add a menu item that let the user rate the application through your application site in google play. After the user click in this option, this should not been showed again (even if the user did not rate the app at the end). This solution is quite user friendly, in my opinion.
Add a menu item like this (in res\menu[menu].xml):
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
(other options...)
<item android:id="#+id/MenuRateApp" android:title="#string/menu_Rate_app"
android:icon="#drawable/ic_menu_star"></item>
</menu>
In your main activity add the following in order to hide the option once the user has already rated your app:
#Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem register = menu.findItem(R.id.MenuRateApp);
if(fApp.isRated()) {
register.setVisible(false);
}
return true;
}
Change the fApp.isRated() for a method or variable that keep a boolean saying if the user already rated the app (write and read this value using the sharedPreferences mechanism).
The code to redirect the user to your app site in Google Play could be like the following:
private boolean MyStartActivity(Intent aIntent) {
try {
startActivity(aIntent);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
(other options code...)
if (item.getItemId() == R.id.MenuRateApp) {
//Try Google play
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id="+getPackageName()));
if (MyStartActivity(intent) == false) {
//Market (Google play) app seems not installed, let's try to open a webbrowser
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id="+getPackageName()));
if (MyStartActivity(intent) == false) {
//Well if this also fails, we have run out of options, inform the user.
Toast.makeText(this, this.getString(R.string.error_no_google_play), Toast.LENGTH_LONG).show();
}
}
//Do not disturb again (even if the user did not rated the app in the end)
fApp.setRated(true);
}
return super.onOptionsItemSelected(item);
}
Hope this solution feets your requirements.
Note: part of the code has been borrowed from this site:
http://martin.cubeactive.com/android-how-to-create-a-rank-this-app-button/
Example:
The premise from where you start, saying that rating an app is exhausting and not user friendly is not applicable because the user should only rate your app when he is willing to "donate" 30 seconds of his life to rate your app. There is a minimal responsibility involved when rating other people work.
The farthest I'd go, since there are also ethics involved, is providing a button in the About section of my app with a link to the Market app screen containing my app, using an Intent to the market (search StackOverflow). Other apps constantly ask a user to rate... I find it bothersome, but at least they are not pushing me right into the Edit and star Views of the Market.
The question you need to ask yourself: do you need to disrupt the user experience of your app by automatically stopping the activity and displaying this "oh-my-gosh-rate-my-app" view in the Market app?
You don't need to push the user into that situation... chances are you will end up with more low ratings than good ratings. I'd take one star just because of that. :-)
Personally, I wouldn't do it and leave the way it is. My 2 cents, of course.
Based on a similar question I posted, the desired answer I was looking for was
https://play.google.com/store/apps/details?id= + your.package.name
This should be what you're looking for if a link is what you have in mind. The first part is the default starter, and the second part will be your package name.
Hi I want to get a list of all of the installed applications on the users device I have been googling for the longest time but can't find what i want this link was the closest though and works fine except me being new don't understand how to use the method getPackages(); and create a list with it
http://www.androidsnippets.com/get-installed-applications-with-name-package-name-version-and-icon
Any help on how to create the actual list would be a major help i have all that code already in just can't get the list to actually show thanks for any help
I was working on something like this recently. One thing I'll say up front is to be sure and perform this in a separate thread -- querying the application information is SLOW. The following will get you a list of ALL the installed applications. This will include a lot of system apps that you probably aren't interested in.
PackageManager pm = getPackageManager();
List<ApplicationInfo> apps = pm.getInstalledApplications(0);
To limit it to just the user-installed or updated system apps (e.g. Maps, GMail, etc), I used the following logic:
List<ApplicationInfo> installedApps = new ArrayList<ApplicationInfo>();
for(ApplicationInfo app : apps) {
//checks for flags; if flagged, check if updated system app
if((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
installedApps.add(app);
//it's a system app, not interested
} else if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//Discard this one
//in this case, it should be a user-installed app
} else {
installedApps.add(app);
}
}
EDIT: Also, to get the name and icon for the app (which is probably what takes the longest -- I haven't done any real deep inspection on it -- use this:
String label = (String)pm.getApplicationLabel(app);
Drawable icon = pm.getApplicationIcon(app);
installedApps should have a full list of the apps you need, now. Hope this helps, but you may have to modify the logic a bit depending on what apps you need to have returned. Again, it is SLOW, but it's just something you have to work around. You might want to build a data cache in a database if it's something you'll be accessing frequently.