How to get the mobile number of my device programmatically? - java

I have tried using 2 methods for retrieving my phone number but both of them don't work. I used:
TelephonyManager
SubscriptionManager
I do get Network name, Country iso, and IMEI but whenever I try to return Number it returns nothing.
I have also added all the required permissions for these! My manifest looks like:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
Code using TelephonyManager:
TelephonyManager phoneMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
phoneMgr.getLine1Number()
Code using SubscriptionManager:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
List<SubscriptionInfo> subscription = SubscriptionManager.from(getApplicationContext()).getActiveSubscriptionInfoList();
for (int i = 0; i < subscription.size(); i++) {
SubscriptionInfo info = subscription.get(i);
Log.e("TAG", "number " + info.getNumber());
Log.e("TAG", "network name : " + info.getCarrierName());
Log.e("TAG", "country iso " + info.getCountryIso());
}
}
In both attempts I get nothing!
Is there any other way to get phone number or I'm doing something wrong?

Nowadays the TelephonyManager does not help us. Play Services API without permission is good solution for this.
This dependency is useful for this
implementation 'com.google.android.gms:play-services-auth:16.0.1'
Now inside your Activity.java:
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Auth.CREDENTIALS_API)
.build();
if (mGoogleApiClient != null) {
mGoogleApiClient.connect();
}
After this do request for Phone Number:
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent intent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
try {
startIntentSenderForResult(intent.getIntentSender(), 1008, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
Log.e("", "Could not start hint picker Intent", e);
}
Now you need to handle response in your onActivityResult like this:
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 1008:
if (resultCode == RESULT_OK) {
Credential cred = data.getParcelableExtra(Credential.EXTRA_KEY);
// cred.getId====: ====+919*******
Log.e("cred.getId", cred.getId());
userMob = cred.getId();
} else {
// Sim Card not found!
Log.e("cred.getId", "1008 else");
return;
}
break;
}
}

I found #bhoomika's answer useful but now using GoogleApiClient is deprecated. So you can use CredentialsClient instead.
Below is the method I used to trigger the phone number hint dialog (this method is in a helper class).
public void requestPhoneNumberHint(Activity currentActivity) {
HintRequest hintRequest = new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
CredentialsClient credentialsClient = Credentials.getClient(currentActivity);
PendingIntent intent = credentialsClient.getHintPickerIntent(hintRequest);
try {
uiListener.getCurrentActivity().startIntentSenderForResult(intent.getIntentSender(),
RESOLVE_PHONE_NUMBER_HINT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
Below is my handling for the corresponding onActivityResult (my Activity code is in Kotlin)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if(requestCode == RESOLVE_PHONE_NUMBER_HINT){
if (resultCode == RESULT_OK) {
var credential : Credential? = data?.getParcelableExtra(Credential.EXTRA_KEY)
credential?.apply {
processPhoneNumber(id)
}
}
}
As mentioned in the below documentation, the result codes can be used to identify if there were no hints that were displayed or if the user did not chose any of the options.
public static final int ACTIVITY_RESULT_NO_HINTS_AVAILABLE
Activity result code indicating that there were no hints available.
Constant Value: 1002
public static final int ACTIVITY_RESULT_OTHER_ACCOUNT
Activity result code indicating that the user wishes to use a different account from what was presented in the credential or hint picker.
Constant Value: 1001
https://developers.google.com/android/reference/com/google/android/gms/auth/api/credentials/CredentialsApi#ACTIVITY_RESULT_NO_HINTS_AVAILABLE

By using below code you current device phone number & after selecting phone number onActivity result will be called.
Gradle :
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:17.0.0'
Setup Google API Client :
//set google api client for hint request
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.enableAutoManage(this, this)
.addApi(Auth.CREDENTIALS_API)
.build();
Get an available number :
public void getHintPhoneNumber() {
HintRequest hintRequest =
new HintRequest.Builder()
.setPhoneNumberIdentifierSupported(true)
.build();
PendingIntent mIntent = Auth.CredentialsApi.getHintPickerIntent(mGoogleApiClient, hintRequest);
try {
startIntentSenderForResult(mIntent.getIntentSender(), RESOLVE_HINT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
Get Selected Number in onActivityResult :
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//Result if we want hint number
if (requestCode == RESOLVE_HINT) {
if (resultCode == Activity.RESULT_OK) {
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
// credential.getId(); <-- will need to process phone number string
inputMobileNumber.setText(credential.getId());
}
}
}
Reference : https://androidwave.com/automatic-sms-verification-android/

Have you given access to READ_PHONE_STATE permission?
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

getLine1Number() should do the trick, but depending on the SIM-card this will not always be able to get the phone number
I suggest that if you do not get the number from getLine1Number(), then make the user type it in manually. (This is what iOS users have to do anyway)
EDIT:
Also, you should not use IMEI as of Android 10 you will not have permission to get that information

Related

how to know whether the app currently has the 'All Files Access' permission or not. Android 11

but how to validate if the app currently has the 'All Files Access' permission or not. i use this method to ask permission.
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s",this.getPackageName())));
startActivityForResult(intent, 2296);
Please help me to fix this :(
declare this permission in your manifest.xml:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
for asking MANAGE_EXTERNAL_STORAGE:
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse(String.format("package:%s", getApplicationContext().getPackageName())));
startActivityForResult(intent, 2296);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, 2296);
}
get your result in onActivityResult
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 2296) {
if (SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
// perform action when allow permission success
} else {
Toast.makeText(this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show();
}
}
}
}
so, basically you can check permission is granted or not in Android11 like this;
if (SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
// Permission Granted
}
}
create new dummy 0-bytes file in root of internal memory and then remove it. if both operations success then you have access. when you won't have access then your code will throw Exception (on create-file step), which you can try{ }catch(Exception e) (preventing app crash)
path for internal/external/shared can be obtained from Environment class, I would try at first getExternalStorageDirectory
I do it that way:
public static boolean hasAllFilesAccess(Context context) {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
ApplicationInfo app = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
return appOpsManager.unsafeCheckOpNoThrow("android:manage_external_storage",app.uid, context.getPackageName()) == AppOpsManager.MODE_ALLOWED;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
catch (NoSuchMethodError e)
{
return true;
}
return false;
}
Seems there is a better way now though:
Environment.isExternalStorageManager()

Make a call using action_call intent on Android 10 doesn't work

This works on the previous version of android but on android 10 it no longer works . any ideas how to solve this problem. any help would be greatly appreciated . I have tried with intent action_call and placeCall from telecomManager.
/**
* Call a given number
*
* #param context
* #param number
*/
public static void call(#NotNull Context context, #NotNull String number) {
try {
// Create call intent
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + Uri.encode(number)));
// Handle sim card selection
// int simCard = getSimSelection(context);
// Timber.d("simcard "+simCard);
// if (simCard != -1) callIntent.putExtra("com.android.phone.extra.slot", simCard);
callIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
// Start the call
context.startActivity(callIntent);
} catch (SecurityException e) {
Toast.makeText(context, "Couldn't make a call due to security reasons", Toast.LENGTH_LONG).show();
} catch (NullPointerException e) {
Toast.makeText(context, "Couldnt make a call, no phone number", Toast.LENGTH_LONG).show();
}
}
/**
* Places a new outgoing call to the provided address using the system telecom service with
* the specified intent.
*
* #param activity {#link Activity} used to start another activity for the given intent
* #param telecomManager the {#link TelecomManager} used to place a call, if possible
* #param intent the intent for the call
*/
public static boolean placeCall(#Nullable FragmentActivity activity,
#Nullable TelecomManager telecomManager, #Nullable Intent intent) {
if (activity == null || telecomManager == null || intent == null) {
return false;
}
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return false;
}
telecomManager.placeCall(intent.getData(), intent.getExtras());
return true;
// activity.startActivityForResult(intent, 1291);
// return true;
}
try with adding callIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
Are you trying to start a call from a background service?
Android 10 restricted to starting activity from background. There are some exclusions for this. In my view asking for "SYSTEM_ALERT_WINDOW" permission is the easiest one.
https://developer.android.com/guide/components/activities/background-starts
https://stackoverflow.com/a/59421118/11982611
private void RequestPermission() {
// Check if Android M or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getActivity().getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(getContext())) {
PermissionDenied();
}
else
{
//Permission Granted-System will work
}
}
}
If the account is not selected before placing a call in setting it requires you to send a account handle as an extra.
try {
List<PhoneAccountHandle> phoneAccountHandleList = TelecomUtil.getTelecomManager(context).getCallCapablePhoneAccounts();
int simCard = getSimSelection(context);
// Create call intent
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + Uri.encode(number)));
if (phoneAccountHandleList != null && !phoneAccountHandleList.isEmpty()) {
callIntent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandleList.get(simCard));
}
// // Handle sim card selection
Timber.d("simcard %s", simCard);
if (simCard != -1) callIntent.putExtra("com.android.phone.extra.slot", simCard);
// // Start the call
context.startActivity(callIntent);
} catch (SecurityException e) {
Toast.makeText(context, "Couldn't make a call due to security reasons", Toast.LENGTH_LONG).show();
} catch (NullPointerException e) {
Toast.makeText(context, "Couldnt make a call, no phone number", Toast.LENGTH_LONG).show();
}

How to get Google-Sign in to work after getting error 10?

I have trying to implement Google Sign In on my app by following the guide.
https://developers.google.com/identity/sign-in/android/start-integrating#add_google_play_services
However, I keep getting error 10 everytime I go to try to log in and I know it means its a developer error but, I cant figure out what I am doing wrong. I implemented all the code, made sure I have the correct packages and updated Android Studio.
I tried different client ids from SHA1 hashes that came from multiple generated signed bundles and apks for my app. I tried the pre generated one that Google gives you for sign-in. Any ideas?
Intent for google sign in
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestIdToken(getString(R.string.server_client_id))
.build();
googleSignInClient = GoogleSignIn.getClient(getActivity(),gso);
Intent signInIntent = googleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, 21);
OnActivityResult function
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
callbackManager.onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 21) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
handleSignInResult(task);
}
else if (resultCode == RESULT_CANCELED)
{
Log.d("frag", "intent fired and something went wrong");
}
}
handleSignInResult function
private void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
try {
GoogleSignInAccount account = completedTask.getResult(ApiException.class);
// Signed in successfully, show authenticated UI.
Log.d("frag", "Email of account is " + account.getEmail());
} catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason.
// Please refer to the GoogleSignInStatusCodes class reference for more information.
Log.w("ytsignin", "signInResult:failed code=" + e.getStatusCode());
}
}
Check the SHA-1 code and package name in developer console. Most of the time it is what causes the error 10 which is 'DEVELOPER_ERROR'.
Check the SHA-1 in console against the one you get from Android Studio after running signingReport.

onActivityResult Not Being Called For UnityPlayerActivity

I don't have a lot of experience creating Java (.aar) plugins for Unity3d, but I am attempting to setup google authentication with firebase from such a plugin. To give an example of my problem, I begin by opening a unity android application, then I run the c# code below, and get a popup on my display to sign-in with google. I then choose the correct google account, then the google intent/activity disappears, then I receive no indication that "onActivityResult" has been called. No errors occur and I am unable to to do anything with the google account information that I chose.
In the image below, I click submit -> it opens the google sign-in activity in the next picture -> then it returns back to the submit screen (closing the google sign-in activity).
I think my issue is in this line:
activity.startActivityForResult(signInIntent, RC_SIGN_IN);
The "activity" in this case is a UnityPlayerActivity sent from the c# unity code below. I think this is making it so my code is looking for an "onActivityResult" method in the C# unity code rather than the java code. Any help would be greatly appreciated. Let me know if you need any other info or screenshots. Please call me out if I am being a moron.
Here is my code for calling the Google Signin Plugin From C# & Unity3d:
AndroidJNIHelper.debug = true;
using (AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
activity_context = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
}
using (AndroidJavaClass pluginClass = new AndroidJavaClass("com.package.class")) {
if (pluginClass != null) {
GoogleSignInActivity = pluginClass.CallStatic<AndroidJavaObject>("instance");
GoogleSignInActivity.Call("SetContext", activity_context);
GoogleSignInActivity.Call("StartGoogleLogin", activity_context);
activity_context.Call("runOnUiThread", new AndroidJavaRunnable(() => {
GoogleSignInActivity.Call("ShowMessage", "You signed in as " + display_name);
}));
}
}
Here is the code for creating the Google SignIn Activity:
public void StartGoogleLogin(UnityPlayerActivity activity) {
gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("some url")
.requestEmail()
.build();
mGoogleSignInClient = GoogleSignIn.getClient(activity, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
activity.startActivityForResult(signInIntent, RC_SIGN_IN);
Log.d(TAG, "Activity Started; Waiting For Result");
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "Result Received!");
if (requestCode == RC_SIGN_IN) {
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
// Google Sign In was successful
GoogleSignInAccount account = task.getResult(ApiException.class);
someMethod(account);
} catch (ApiException e) {
// Google Sign In failed, update UI appropriately
Log.d(TAG, "Google sign in failed", e);
}
setResult(RESULT_OK);
}
}
Thank you for your time.
Well, I solved my own problem of getting Google Authentication to work between the Android .aar plugin and Unity c#. Been working tirelessly and found some kickbutt resources.
First and foremost, I referenced the code written by a guy named cwgtech HERE.
I also went through all of his videos.
Instead of using UnitySendMessage, I was able to use a callback method similar to what CWGTech does to send a googleIdToken back to Unity and sign-in with Google into Firebase. I was also correct in thinking that my mistake was with the statement below:
activity.startActivityForResult(signInIntent, RC_SIGN_IN);
Instead of doing this, I followed CWGTech's advice and removed "activity." portion. I ran the startActivityForResult in a ResultCallback class that extends Activity. If you are still confused, dm me or comment on this post. Thanks!
Here is some of the code I used to send a callback string to Unity via a Java Proxy in written in C#. Information about writing a Java proxy can be found in the cwgtech information above. Writing the java proxy is extremely important if you want to get information to flow from Android activities to Unity C#. CWGTech explains the intricacies of java proxies way better than I could do justice.
public static final String LOGTAG = GoogleSignInActivity.TAG + "_OnResult";
public static GoogleSignInActivity.ShareStringCallback shareStringCallback;
private static final int RC_SIGN_IN = 9001;
private GoogleSignInClient mGoogleSignInClient;
private GoogleSignInOptions gso;
private CallbackManager mCallbackManager;
public void myFinish(String myresult) {
if (shareStringCallback != null) {
shareStringCallback.onLoginComplete(myresult);
}
shareStringCallback = null;
finish();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(LOGTAG, "onCreateBundle");
Intent intent = getIntent();
setContentView(R.layout.login_activity);
findViewById(R.id.buttonFacebookLogin).setOnClickListener(this);
findViewById(R.id.signInButton).setOnClickListener(this);
findViewById(R.id.buttonAnonymousSignIn).setOnClickListener(this);
}
/* GOOGLE SIGN-IN CODE */
public Intent StartGoogleLogin() {
/*
Google Sign In Client Init Code Goes Here
*/
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
return signInIntent;
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//Detects some type of result from an activity, in this case Google
String id_result = "Send This To Unity";
myFinish(id_result);
}
Here is a bit more code from a different java class file. The 'Login' method is called from Unity c#.
/* INTERFACES FOR CALLBACK FUNCTIONAILITY */
public interface ShareStringCallback {
public void onLoginComplete(String result);
}
public void Login(final ShareStringCallback callback)
{
mainActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
try {
Log.i(TAG,"Starting Authentication");
try {
try {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setClass(mainActivity,OnResultCallback.class);
OnResultCallback.shareStringCallback = callback;
mainActivity.startActivity(shareIntent);
}
catch (Exception e)
{
e.printStackTrace();
Log.i(TAG,"error sharing intent: " + e);
}
}
catch (Exception e)
{
e.printStackTrace();
Log.i(TAG,"Error getting Uri: " + e);
}
}
catch (Exception e)
{
e.printStackTrace();
Log.i(TAG,"Error writing file: " + e);
}
}
});
}
When you start a activity from UnityPlayerActivity, the onActivityResult will be called when finished, but will have its default return value. What you can do is create a new Activity in Android, and extends UnityPlayerActivity.
In Android, create a new Activity
import com.unity3d.player.UnityPlayer;
public class OverrideUnityPlayerActivity extends UnityPlayerActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public Activity getCurrentActivity(){
return mUnityPlayer.currentActivity;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 0) {
switch (resultCode) {
case Activity.RESULT_OK:
//do something
break;
case Activity.RESULT_CANCELED:
//do something
break;
}
}
}
}
You need to set this override activity as launch endpoint in AndroidManifest.XML
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
<activity
android:name="com.example.unitylibrary.manager.OverrideUnityPlayerActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="app_name" android:value="app_value"/>
</activity>
</application>
</manifest>
3.Then in Unity, you can get this activity and context, also get the onActivityResult
AndroidJavaObject overridePlayerActivity;
AndroidJavaObject overrideActivity;
public void init(){
overridePlayerActivity = new AndroidJavaObject("com.example.unitylibrary.manager.OverrideUnityPlayerActivity");
overrideActivity= overridePlayerActivity.Call<AndroidJavaObject>("getCurrentActivity");
}
public void startAct(){
anonymousFunction.Call("StartActForRes", overrideActivity);
}
Hope this will solve your problem.

Programmatically Turn On Gps in Android Lollipop [duplicate]

I know that the question about turning on/off GPS programatically on android has been discussed many times, and the answer is always the same:
"You can't for security/privacy reasons, you have to forward to location preferences screen and let the user enable/disable it."
I understand that, however I recently bought Tasker from the market and, among many other things that you can accomplish with it, you can set rules to auto-enable GPS on entering pre-determined applications and disable it on exit (see here for the tutorial on how to do it, and it just works!) and this app can't be signed with the firmware signing key as it works on many android versions and different devices and you don't even need to be rooted.
I would like to do this in my app. Of course, I don't want to blow up the users privacy, so I would first ask the user if he wants to turn it on automatically with the typical "remember my decision" checkbox and if he answers yes, enable it.
Does anybody have any idea or clue on how Tasker achieves this?
the GPS can be toggled by exploiting a bug in the power manager widget. see this xda thread for discussion.
here's some example code i use
private void turnGPSOn(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(!provider.contains("gps")){ //if gps is disabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
private void turnGPSOff(){
String provider = Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if(provider.contains("gps")){ //if gps is enabled
final Intent poke = new Intent();
poke.setClassName("com.android.settings", "com.android.settings.widget.SettingsAppWidgetProvider");
poke.addCategory(Intent.CATEGORY_ALTERNATIVE);
poke.setData(Uri.parse("3"));
sendBroadcast(poke);
}
}
use the following to test if the existing version of the power control widget is one which will allow you to toggle the gps.
private boolean canToggleGPS() {
PackageManager pacman = getPackageManager();
PackageInfo pacInfo = null;
try {
pacInfo = pacman.getPackageInfo("com.android.settings", PackageManager.GET_RECEIVERS);
} catch (NameNotFoundException e) {
return false; //package not found
}
if(pacInfo != null){
for(ActivityInfo actInfo : pacInfo.receivers){
//test if recevier is exported. if so, we can toggle GPS.
if(actInfo.name.equals("com.android.settings.widget.SettingsAppWidgetProvider") && actInfo.exported){
return true;
}
}
}
return false; //default
}
All these answers are not allowed now. Here is the correct one:
For all those still looking for the Answer:
Here is how OLA Cabs and other such apps are doing it.
Add this in your onCreate
if (googleApiClient == null) {
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(Login.this).build();
googleApiClient.connect();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
// **************************
builder.setAlwaysShow(true); // this is the key ingredient
// **************************
PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi
.checkLocationSettings(googleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
final LocationSettingsStates state = result
.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can
// initialize location
// requests here.
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be
// fixed by showing the user
// a dialog.
try {
// Show the dialog by calling
// startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(Login.this, 1000);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have
// no way to fix the
// settings so we won't show the dialog.
break;
}
}
});
}
These are the implmented methods:
#Override
public void onConnected(Bundle arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
}
Here is the Android Documentation for the same.
This is to help other guys if they are still struggling:
Edit: Adding Irfan Raza's comment for more help.
#Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1000) {
if(resultCode == Activity.RESULT_OK){
String result=data.getStringExtra("result");
} if (resultCode == Activity.RESULT_CANCELED) {
//Write your code if there's no result
}
}
}
ENABLE GPS:
Intent intent=new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", true);
sendBroadcast(intent);
DISABLE GPS:
Intent intent = new Intent("android.location.GPS_ENABLED_CHANGE");
intent.putExtra("enabled", false);
sendBroadcast(intent);
This code works on ROOTED phones if the app is moved to /system/aps, and they have the following permissions in the manifest:
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
Code
private void turnGpsOn (Context context) {
beforeEnable = Settings.Secure.getString (context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
String newSet = String.format ("%s,%s",
beforeEnable,
LocationManager.GPS_PROVIDER);
try {
Settings.Secure.putString (context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
newSet);
} catch(Exception e) {}
}
private void turnGpsOff (Context context) {
if (null == beforeEnable) {
String str = Settings.Secure.getString (context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
if (null == str) {
str = "";
} else {
String[] list = str.split (",");
str = "";
int j = 0;
for (int i = 0; i < list.length; i++) {
if (!list[i].equals (LocationManager.GPS_PROVIDER)) {
if (j > 0) {
str += ",";
}
str += list[i];
j++;
}
}
beforeEnable = str;
}
}
try {
Settings.Secure.putString (context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
beforeEnable);
} catch(Exception e) {}
}
Instead of using intent Settings.ACTION_LOCATION_SOURCE_SETTINGS you can directly able to show pop up in your app like Google Map & on Gps on click of ok button their is no need to redirect to setting simply you need to use my code as
Note : This line of code automatic open the dialog box if Location is not on. This piece of line is used in Google Map also
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
PendingResult<LocationSettingsResult> result;
final static int REQUEST_LOCATION = 199;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(30 * 1000);
mLocationRequest.setFastestInterval(5 * 1000);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest);
builder.setAlwaysShow(true);
result = LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient, builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(LocationSettingsResult result) {
final Status status = result.getStatus();
//final LocationSettingsStates state = result.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can initialize location
// requests here.
//...
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied. But could be fixed by showing the user
// a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(
MainActivity.this,
REQUEST_LOCATION);
} catch (SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way to fix the
// settings so we won't show the dialog.
//...
break;
}
}
});
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.d("onActivityResult()", Integer.toString(resultCode));
//final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
switch (requestCode)
{
case REQUEST_LOCATION:
switch (resultCode)
{
case Activity.RESULT_OK:
{
// All required changes were successfully made
Toast.makeText(MainActivity.this, "Location enabled by user!", Toast.LENGTH_LONG).show();
break;
}
case Activity.RESULT_CANCELED:
{
// The user was asked to change settings, but chose not to
Toast.makeText(MainActivity.this, "Location not enabled, user cancelled.", Toast.LENGTH_LONG).show();
break;
}
default:
{
break;
}
}
break;
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
}
Note : This line of code automatic open the dialog box if Location is not on. This piece of line is used in Google Map also
Since Android version 4.4, you can't enable/disable gps programatically. If you try the code proposed on this answer, an exception will be fired.
java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.location.GPS_ENABLED_CHANGE
Above correct answer is very old it needs something new so Here is answer
As in last update we have androidx support so first include dependency in your app level build.gradle file
implementation 'com.google.android.gms:play-services-location:17.0.0'
then add in your manifest file:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
don't forget to take user consent for these permissions if you are releasing
now here is code just use it
protected void createLocationRequest() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest);
SettingsClient client = LocationServices.getSettingsClient(this);
Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
#Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
Toast.makeText(MainActivity.this, "Gps already open",
Toast.LENGTH_LONG).show();
Log.d("location settings",locationSettingsResponse.toString());
}
});
task.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
if (e instanceof ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
ResolvableApiException resolvable = (ResolvableApiException) e;
resolvable.startResolutionForResult(MainActivity.this,
REQUEST_CHECK_SETTINGS);
} catch (IntentSender.SendIntentException sendEx) {
// Ignore the error.
}
}
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==REQUEST_CHECK_SETTINGS){
if(resultCode==RESULT_OK){
Toast.makeText(this, "Gps opened", Toast.LENGTH_SHORT).show();
//if user allows to open gps
Log.d("result ok",data.toString());
}else if(resultCode==RESULT_CANCELED){
Toast.makeText(this, "refused to open gps",
Toast.LENGTH_SHORT).show();
// in case user back press or refuses to open gps
Log.d("result cancelled",data.toString());
}
}
}
if something goes wrong please ping me
Short and easy solution with newest API, from https://developer.android.com/training/location/change-location-settings.html.
You will get nice Google AlertDialog with ok button without any need of going to settings.
Straight to the point. My code in Fragment:
override fun onResume() {
super.onResume()
checkGPSEnabled()
}
private fun checkGPSEnabled() {
val manager = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER).not()) {
turnOnGPS()
}
}
private fun turnOnGPS() {
val request = LocationRequest.create().apply {
interval = 2000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
val client: SettingsClient = LocationServices.getSettingsClient(requireActivity())
val task: Task<LocationSettingsResponse> = client.checkLocationSettings(builder.build())
task.addOnFailureListener {
if (it is ResolvableApiException) {
try {
it.startResolutionForResult(requireActivity(), 12345)
} catch (sendEx: IntentSender.SendIntentException) {
}
}
}.addOnSuccessListener {
//here GPS is On
}
}
That's it. Just copy and paste. You will need also:
implementation 'com.google.android.gms:play-services-location:18.0.0' and in Manifest <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
An answer was developed in another question, but it was closed, and I'd like the community to try it out as well.
boolean gpsStatus = locmanager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!gpsStatus) {
Settings.Secure.putString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "network,gps");
}
See this comment
This solution would require the WRITE_SETTINGS and WRITE_SECURE_SETTINGS permissions.
Maybe with reflection tricks around the class android.server.LocationManagerService.
Also, there is a method (since API 8) android.provider.Settings.Secure.setLocationProviderEnabled
This is the best solution provided by Google Developers. Simply call this method in onResume of onCreate after initializing GoogleApiClient.
private void updateMarkers() {
if (mMap == null) {
return;
}
if (mLocationPermissionGranted) {
// Get the businesses and other points of interest located
// nearest to the device's current location.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).build();
mGoogleApiClient.connect();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(10000 / 2);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
builder.setAlwaysShow(true);
LocationSettingsRequest.Builder builder = new LocationSettingsRequest
.Builder()
.addLocationRequest(mLocationRequest);
PendingResult<LocationSettingsResult> resultPendingResult = LocationServices
.SettingsApi
.checkLocationSettings(mGoogleApiClient, builder.build());
resultPendingResult.setResultCallback(new ResultCallback<LocationSettingsResult>() {
#Override
public void onResult(#NonNull LocationSettingsResult locationSettingsResult) {
final Status status = locationSettingsResult.getStatus();
final LocationSettingsStates locationSettingsStates = locationSettingsResult.getLocationSettingsStates();
switch (status.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can
// initialize location requests here.
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
status.startResolutionForResult(
MainActivity.this,
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
} catch (IntentSender.SendIntentException e) {
// Ignore the error.
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way
// to fix the settings so we won't show the dialog.
break;
}
}
});
#SuppressWarnings("MissingPermission")
PendingResult<PlaceLikelihoodBuffer> result = Places.PlaceDetectionApi
.getCurrentPlace(mGoogleApiClient, null);
result.setResultCallback(new ResultCallback<PlaceLikelihoodBuffer>() {
#Override
public void onResult(#NonNull PlaceLikelihoodBuffer likelyPlaces) {
for (PlaceLikelihood placeLikelihood : likelyPlaces) {
// Add a marker for each place near the device's current location, with an
// info window showing place information.
String attributions = (String) placeLikelihood.getPlace().getAttributions();
String snippet = (String) placeLikelihood.getPlace().getAddress();
if (attributions != null) {
snippet = snippet + "\n" + attributions;
}
mMap.addMarker(new MarkerOptions()
.position(placeLikelihood.getPlace().getLatLng())
.title((String) placeLikelihood.getPlace().getName())
.snippet(snippet));
}
// Release the place likelihood buffer.
likelyPlaces.release();
}
});
} else {
mMap.addMarker(new MarkerOptions()
.position(mDefaultLocation)
.title(getString(R.string.default_info_title))
.snippet(getString(R.string.default_info_snippet)));
}
}
Note : This line of code automatic open the dialog box if Location is not on. This piece of line is used in Google Map also
status.startResolutionForResult(
MainActivity.this,
PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
This code works on ROOTED phones:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] cmds = {"cd /system/bin" ,"settings put secure location_providers_allowed +gps"};
try {
Process p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
for (String tmpCmd : cmds) {
os.writeBytes(tmpCmd + "\n");
}
os.writeBytes("exit\n");
os.flush();
}
catch (IOException e){
e.printStackTrace();
}
}
}
For turning off GPS you can use this command instead
settings put secure location_providers_allowed -gps
You can also toggle network accuracy using the following commands:
for turning on use:
settings put secure location_providers_allowed +network
and for turning off you can use:
settings put secure location_providers_allowed -network
This one works for me.
It is a simpler solution than Rj0078's answer under this question, but that one is worked as well.
It shows a dialog like this:
(Written in Kotlin)
googleApiClient = GoogleApiClient.Builder(context!!)
.addApi(LocationServices.API).build()
googleApiClient!!.connect()
locationRequest = LocationRequest.create()
locationRequest!!.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
locationRequest!!.interval = 30 * 1000.toLong()
locationRequest!!.fastestInterval = 5 * 1000.toLong()
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest!!)
builder.setAlwaysShow(true)
result =
LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
result!!.setResultCallback { result ->
val status: Status = result.status
when (status.statusCode) {
LocationSettingsStatusCodes.SUCCESS -> {
// Do something
}
LocationSettingsStatusCodes.RESOLUTION_REQUIRED ->
try {
startResolutionForResult(),
status.startResolutionForResult(
activity,
REQUEST_LOCATION
)
} catch (e: SendIntentException) {
}
LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> {
// Do something
}
}
}
Things have changed since this question was posted, now with new Google Services API, you can prompt users to enable GPS:
https://developers.google.com/places/android-api/current-place
You will need to request ACCESS_FINE_LOCATION permission in your manifest:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Also watch this video:
https://www.youtube.com/watch?v=F0Kh_RnSM0w
This is a more statble code for all Android versions and possibly for new ones
void checkGPS() {
LocationRequest locationRequest = LocationRequest.create();
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
Task<LocationSettingsResponse> task = settingsClient.checkLocationSettings(builder.build());
task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
#Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
Log.d("GPS_main", "OnSuccess");
// GPS is ON
}
});
task.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull final Exception e) {
Log.d("GPS_main", "GPS off");
// GPS off
if (e instanceof ResolvableApiException) {
ResolvableApiException resolvable = (ResolvableApiException) e;
try {
resolvable.startResolutionForResult(ActivityMain.this, REQUESTCODE_TURNON_GPS);
} catch (IntentSender.SendIntentException e1) {
e1.printStackTrace();
}
}
}
});
}
And you can handle the GPS state changes here
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == Static_AppVariables.REQUESTCODE_TURNON_GPS) {
switch (resultCode) {
case Activity.RESULT_OK:
// GPS was turned on;
break;
case Activity.RESULT_CANCELED:
// User rejected turning on the GPS
break;
default:
break;
}
}
}
You just need to remove the LocationListener from LocationManager
manager.removeUpdates(listener);
Use This code Simple and Easy to Access:
Permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Follow this Code to access the GPS programmatically:
LocationManager locationManager ;
boolean GpsStatus ;
GPSStatus();
if(GpsStatus == true)
{
textview.setText("Your Location Services Is Enabled");
}else
{textview.setText("Your Location Services Is Disabled");}
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
public void GPSStatus(){
locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
GpsStatus = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}

Categories

Resources