I am building and Android app and trying to learn all I can in the process. As a test I wanted to add a "chat heads" style overlay to the system. So, I looked around and found some good tutorials, and some really good answers on here, but it is still not working. In my android manifest I have the uses-permission declaration..
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Then in my Preference page, when the user clicks on a SwitchPreference to activate the popup chat window I am checking for version and permissions like so...
private Preference.OnPreferenceChangeListener chatHeadsChangeListener = new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object o) {
if((boolean) o) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
!Settings.canDrawOverlays(getApplicationContext())) {
Intent intent = new Intent(
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName())
);
startActivityForResult(intent, CODE_DRAW_OVER_OTHER_APP_PERMISSION);
} else {
initializeChatHeadsView();
}
}
return true;
}
};
Then in my onActivityResult I am checking again to make sure, like one answer I found on here...
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == CODE_DRAW_OVER_OTHER_APP_PERMISSION) {
if(Settings.canDrawOverlays(this)) {
initializeChatHeadsView();
} else {
Toast.makeText(
this,
"Draw over other app permission was not available. Cannot activate Popup Chat",
Toast.LENGTH_LONG
).show();
}
}
}
The first time I load the app up on the emulator, which is running Android 8.0, I go into my settings page and click the SwitchPreference turning it on. The settings page opens up asking me if I want to enable the permission, I activate the permission, but it doesn't go back to my app after activating, so I hit the back button which goes back into my app, then I get the "[Application] has stopped responding." If I check the log in AndroidStudio I get the error...
Process: test.notreal.justatext, PID: 21429
java.lang.RuntimeException: Unable to create service test.notreal.justatext.service.FloatingViewService: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W#bc855eb -- permission denied for window type 2002
So it is still saying the permission is denied, even though I am enabling the permission, and in my onActivityResult I am checking if(Settings.canDrawOverlays())
Can anyone see why this is failing? I can't figure it out. Thank you for any help, I really appreciate it.
for apps targeting Android 8.0
apps must use a new window type called TYPE_APPLICATION_OVERLAY.
so for Android 8.0 you must ask this permission, for lower verison
SYSTEM_ALERT_WINDOW permission
will work
For more Refer this link
https://developer.android.com/about/versions/oreo/android-8.0-changes.html#cwt
Related
My current Android Application needs to call
ActivityManager.clearApplicationUserData()
to simulate the user clearing App storage
Which works fine.
A side effect of calling clearApplicationUserData() is that the App is (understandably) closed.
Which gives a poor user experience.
I am having difficulty in restarting my Application once I have called clearApplicationUserData().
I have tried using startActivity, Alarm Manager with Pending Intent, Foreground/Background service.
Nothing works.
Is it impossible to restart an Android App having called clearApplicationUserData()?
(1st answer: this answer only works on limited situations. it's not a complete answer)
public boolean clearApplicationUserData ()
Description
Return: true if the application successfully requested that the application's data be erased; false otherwise.
As the reference website stated, we have a returnee before the application is being closed. so, we are going to use this returnee in order to restart the app.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
}
when Activity onDestroy() and onStop() are called restart app.
#Override
protected void onDestroy() {
super.onDestroy();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
#Override
protected void onStop() {
super.onStop();
if(doRestart){
Intent intent = new Intent(this, Activity.class);
this.startActivity(intent);
}
}
We put restart action in both onDestroy() and onStop() in order to make sure the app will be restarted again.
And also, I think it's a good idea to force stop activity before OS stops it.
if(ActivityManager.clearApplicationUserData)
{
doRestart = true;
finish(); <= i mean this
}
it's because it makes sure that onDestroy() and onStop() will be invoked.
My suggestion might sound trivial but, have you consider not calling ActivityManager.clearApplicationUserData()?
Here what the docs says about this method:
Permits an application to erase its own data from disk. This is
equivalent to the user choosing to clear the app's data from within
the device settings UI. It erases all dynamic data associated with the
app -- its private data and data in its private area on external
storage -- but does not remove the installed application itself, nor
any OBB files.
So in order to mimic this behavior you just need to clear you internal and external storage directories. No permissions are needed to access any of those.
(2nd answer: I need much more contribution on it)
After 8 hours of researching in Android OS and Android Developers Website in order to find a solution to restart activity when clearApplicationUserData is invoked. Finally, I would be able to find a nice/hacking solution.
This solution looks like Zidane dribble :)
Let's introduce the solution. at first, clearApplicationUserData clears all the clues of the application when is invoked like tasks, notifications, alarms and etc. therefore, explicit Activity calling is impossible.
implicit way is the only possible way of calling activity.
After a couple of tests I found that application manifest registered intent-filters wouldn't be removed and they are able to listen for incoming system broadcasts.
Approximately, 98% of system broadcasts wouldn't be received by cleared application and that 2% remained might not be broadcasted very soon.
so what to do? hmmm? come on man I must find a solution ...
bingo, ** I must trigger something in order to system broadcast it** <= looks hacking :)
so I decide to choose WIFI_STATE_CHANGED because
Easy Access permission
System Broadcasts it on delay <= this makes sure that the app is closed
before broadcast
manifest.xml
<receiver
android:name=".PackageDataClearedReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
MainActivity.java
public class MainActivity extends AppCompatActivity {
ActivityManager am;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new Thread(new Runnable() {
#Override
public void run() {
am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (am != null) {
ExecutorService pool = Executors.newFixedThreadPool(2);
final Collection<Future> futures = new HashSet<Future>();
futures.add(pool.submit(new Runnable() {
#Override
public void run() {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(true);
wifiManager.setWifiEnabled(false);
am.clearApplicationUserData();
}
}));
for (Future future : futures) {
future.isDone();
}
}
}
}).start();
}
});
}
}
demo
keep in mind, it's just A minimum viable product that needs to be developed more in order to make it work perfectly.
Clearing the app data on the device through the API clearApplicationUserData() resets the app as if it were just installed. As you have found, any alarms and broadcasts registered with your app are also cleared. The most efficient way to keep your app in the foreground would be to clear the data yourself, as others have pointed out, rather than using the API. Here is an example: https://stackoverflow.com/a/9073473/949224
However, if you are determined to use the API (which does guarantee all data is cleared) and the app is force-stopped, I have a suggestion:
Create a small companion app that can be launched just prior to you clearing your app data. The companion app simply re-launches your app, possibly after a short time-out.
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.testrelaunchapp");
if (launchIntent != null) {
startActivity(launchIntent);//null pointer check in case package name was not found
} else {
Log.w( TAG, "Unable to resolve launch activity of relauncher companion app");
}
((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
.clearApplicationUserData();
The companion app itself needs to close afterwards, and ideally should be hidden from the Activity Stack etc..
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent launchIntent = getPackageManager().getLaunchIntentForPackage("example.com.yourmainapp");
if (launchIntent != null) {
Handler handler = new Handler(getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.i( TAG, "About to act on launchIntent");
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(launchIntent);
finish();
System.exit(0);
}
}, 1000);
}
}
I have seen this work with Android 6.0, but no guarantees it would be versatile and work across the board. There would be more to do to make the companion app UI-less if desired and to be hidden from the phone's launcher. You would also probably want to bundle the APK as a file within your own app and install it upon first run, which would need the user to enable installation from "Unknown sources" (non-Play store). It can be done through intents to the right System settings, if needed, but users would need a good explanation why this is needed.
So, as I was saying, the simpler approach is to clear the data and app permissions yourself.
As you all know 5th May is the deadline for using Scoped Storage for Android 11. And here's the case for:
I have an app in which user uploads an image during the Sign Up process and I'm using this method to select an image from Gallery
Intent chooseImageIntent = new Intent();
chooseImageIntent.setType("image/*");
chooseImageIntent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(chooseImageIntent, "Select image"), requestCode);
then
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case PICK_IMAGE_ID:
// If image is selected successfully, set the image URI and bitmap.
Uri imageUri = data.getData();
mBitmap1 = ImageHelper.loadSizeLimitedBitmapFromUri(
imageUri, getContentResolver());
if (mBitmap1 != null) {
ivItem1.setImageBitmap(mBitmap1);
uploadFile(mBitmap1, PICK_IMAGE_ID);
} else {
Toast.makeText(this, "image not loaded", Toast.LENGTH_SHORT).show();
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
What changes should I make, in order to comply with the Android 11 update or storage policy update?
It's mentioned in the Google's support website and I quote
Google Play restricts the use of high risk or sensitive permissions,
including a special app access called All files access. This is only
applicable to apps that target Android 11 (API level 30) and declare
the MANAGE_EXTERNAL_STORAGE permission, which is added in Android 11.
Also, this policy does not impact the usage of the
READ_EXTERNAL_STORAGE permission.
So if you are requesting MANAGE_EXTERNAL_STORAGE permission on Android 11 then you need to remove it if it not required by your app. If you need that permission then you need to explain why you are using it.
Since you are asking only READ_EXTERNAL_STORAGE permission. You don't have to do anything. and the policy will not affect your app unless you are requesting MANAGE_EXTERNAL_STORAGE permission
More details can be found here :
Preview: Use of All files access (MANAGE_EXTERNAL_STORAGE) permission
Recently I've added In App Update feature in my App with IMMEDIATE update flow. Here is I'm checking and requesting for the update in onCreate() of MainActivity
initialized the variable in onCreate() like this
appUpdateManager = AppUpdateManagerFactory.create(this);
appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
and in onCreate(), I've added this piece of code
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
Toast.makeText(app, "update available", Toast.LENGTH_SHORT).show();
//update is available
try {
appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.IMMEDIATE, this, Constant.APP_UPDATE_RQ_CODE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
Log.e(TAG, "Update app error: " + e.getMessage());
Toast.makeText(app, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
And this code in onActivityResult()
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == Constant.APP_UPDATE_RQ_CODE) {
if (requestCode == RESULT_OK){
//update is failed, request update again
Toast.makeText(app, "this is called again and again!", Toast.LENGTH_SHORT).show();
requestUpdateApp();
}
}
}
The problem is, after updating the app, always appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE gets true and that's why in app update dialog shows again and again. Please help me to resolve this issue.
for anyone cant install update apk from playstore.
the wrong is not your code, but way your test this library.
you test your function with upload news release (after add in app update libs) to playstore with increment version code and version name.
then you decrement or -1 your version and version code, then build news release apk and try install in your device.
and update is show, but install is failed. cause after succes install you find your version code apk keep with old version. and pop up update keep show and again and again.
the right way for test is like this :
you test your function with upload news release (after add in app update libs) to playstore with increment version code and version name.
install your news version from playstore
then you increment or +1 your version and version code, then build news release apk or bundle and upload again to playstore with news release.
then wait for apk or bundle live in playstore (until you see “Update” in your playstore), if app already live in playstore, but you not found “update” in your app. you can clear all data in playstore and open search again your app in playstore and you will found “Update”
open your apk and you will find show pop up update, and update must be succes.
I am currently making an Android game and I am using google sign in (for access to firebase, google leaderboard and achievement). I already integrated all of that and this is working fine. However I was always using my main google account but when I tried to change it to another account to have other people test it, it doesn't connect anymore.
I added the other account as a tester on the google play game service for my app. I believe I must have missed to add it somewhere but I cannot find where and I have been searching for hours.
Here's the code that should connect but doesn't:
private void startSignInIntent() {
Intent intent = GoogleSignIn.getClient(this,
GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).getSignInIntent();
startActivityForResult(intent, RC_SIGN_IN);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RC_SIGN_IN:
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
GoogleSignInAccount x = result.getSignInAccount();
//x.getIdToken() is always null with the other account
if (x.getIdToken() != null)
firebaseConnect(x);
else
//I tried repeating the operation but won't work
(...)
I also tried with different accounts but only my original one works. So do you have any idea of what I could have done that made this original account work and not any other? I did most of that Google Sign in parametrization long time ago so I don't remember in every details. And I am very desperate to debug that, after spending so much time trying to solve it in vain.
Thank you for any hint or help about that!
Found the solution, I used "GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN" but we need to use "GoogleSignInOptions.DEFAULT_SIGN_IN" to use google connection. For more details about the difference between these 2, refer to the GoogleSignInOptions API documentation.
I am developing native android app using Eclipse, I add the following permission on Android Manifest
<uses-permission android:name="android.permission.CALL_PHONE" />
Then, I get the following message when I am trying to install the android app on my device:
Directly call phone numbers this may cost you money
could I do something to hide this message because I think this is not friendly for users? maybe i need to use another permission, which is the closer one with no permission is required?
I used the following code on my adapter:
public void onClick(View v) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse(PhoneCall));
v.getContext().startActivity(callIntent); }
});
You are welcome to remove the CALL_PHONE permission. Then the message will not appear when the app is installed. All permissions trigger installation warnings, not just CALL_PHONE.
Note that your app only needs the CALL_PHONE permission if you use stuff like the ACTION_CALL Intent. ACTION_DIAL can also be used to place phone calls, but since it allows the user to be involved, no permission is required.
EDIT
Use this format to specify the number and user will have only to click green button "Call".
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:0123456789"));
startActivity(intent);