I use a module in a another app. Before I implement the module, I set it on API Level 23 (compile and target) like my Main Project This works fine, except this Error. The Pproblem is, that Google has changed the permissions management since marshmellow. Finaly now, i dont know how and where I should set the permissions.
This error I get when starting the application:
java.lang.SecurityException: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to perform any operations location.
Could you please help me and explain briefly (what kind of code i need and where to insert) to avoid this error?
Manifest from the Modul:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cs.android.weminder">
<!--
android:versionCode="3"
android:versionName="1.2.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
-->
<!-- Grant the network access permission -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- Grant the location access permission -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen from dimming -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Permission required to use Alarm Manager -->
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-permission android:name="android.permission.GET_TASKS" />
<application
android:allowBackup="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!-- android:name="com.cs.android.weminder.MyApplication"
android:label="@string/app_name">-->
<!-- This meta-data tag is required to use Google Play Services. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<!-- Required for creating dialog of the ACRA report -->
<activity
android:name="org.acra.CrashReportDialog"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:theme="@style/AcraDialog" />
<activity
android:name="com.cs.android.weminder.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</activity>
<activity android:name="com.cs.android.weminder.BaseScreen" />
<activity android:name="com.cs.android.weminder.LocationBaseScreen" />
<activity android:name="com.cs.android.weminder.SettingsActivity" />
<activity android:name="com.cs.android.weminder.WeminderApplication" />
<!-- Required by the AdMob Ads SDK -->
<activity
android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" />
<!-- Include the AdActivity configChanges and theme. -->
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
<receiver
android:name="com.cs.android.weminder.AlarmReceiver"
android:process=":remote" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name="com.cs.android.weminder.AlarmService" >
</service>
</application>
Main Activity from the Modul:
public class MainActivity extends SettingsActivity implements ActionTypes,
ResultType {
private TextView tvLocation, tvHTemp, tvLTemp, tvCurrentTemp, tvTimestamp,
tvDate, tvWindSpeed, tvPressure;
private ImageView ivCurrentWeather, ivRefresh, ivUnit, ivSearch, ivRemind,
ivMyLocation;
private HorizontalViewGallery forecastGallery;
// Search field
private MyCustomEditText etSearch;
// holding different weather icons presenting weather condition
// alternatively.
private IconFinder mIconFinder;
private ApiCallQueue requestsQueue = new ApiCallQueue();
// Ads View
private AdView adView;
private void setupUI(View view) {
// Set up touch listener for non-text box views to hide keyboard.
if (!(view instanceof EditText)) {
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// Hide the search field if the user is touching anywhere
// except the search field
if (etSearch.isShown()) {
etSearch.startDeflation();
return true;
}
return false;
}
});
}
// If a layout container, iterate over children and seed recursion.
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View innerView = ((ViewGroup) view).getChildAt(i);
setupUI(innerView);
}
}
}
/**
* Initial the UI of current screen Initial variables;
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private void initialActivity() {
setupUI(findViewById(R.id.parent));
// display the name of location
tvLocation = (TextView) findViewById(R.id.tvLocation);
// Set the location icon
tvLocation.setCompoundDrawablesWithIntrinsicBounds(getResources()
.getDrawable(R.drawable.icon_marker), null, null, null);
// display today highest temperature
tvHTemp = (TextView) findViewById(R.id.tvHTemp);
// Set the highest temperature icon
tvHTemp.setCompoundDrawablesWithIntrinsicBounds(getResources()
.getDrawable(R.drawable.icon_highest), null, null, null);
// display today lowest temperature
tvLTemp = (TextView) findViewById(R.id.tvLTemp);
// Set the lowest temperature icon
tvLTemp.setCompoundDrawablesWithIntrinsicBounds(getResources()
.getDrawable(R.drawable.icon_lowest), null, null, null);
// display the current temperature
tvCurrentTemp = (TextView) findViewById(R.id.tvCurrentTemp);
// display the update time stamp
tvTimestamp = (TextView) findViewById(R.id.tvTimestamp);
// display the date of today
tvDate = (TextView) findViewById(R.id.tvDate);
// display the wind speed
tvWindSpeed = (TextView) findViewById(R.id.tvWindSpeed);
// Set wind speed icon
tvWindSpeed.setCompoundDrawablesWithIntrinsicBounds(getResources()
.getDrawable(R.drawable.icon_wind), null, null, null);
// display the pressure
tvPressure = (TextView) findViewById(R.id.tvPressure);
// Set wind speed icon
tvPressure.setCompoundDrawablesWithIntrinsicBounds(getResources()
.getDrawable(R.drawable.icon_pressure), null, null, null);
// visualize the current weather condition
ivCurrentWeather = (ImageView) findViewById(R.id.ivCurrentWeather);
// Scrollable forecast
forecastGallery = (HorizontalViewGallery) findViewById(R.id.gForcast);
// Search city button
ivSearch = (ImageView) findViewById(R.id.ivSearch);
// Setting button
ivRemind = (ImageView) findViewById(R.id.ivRemind);
// My location button
ivMyLocation = (ImageView) findViewById(R.id.ivMyLocation);
// Temp unit setting Button
ivUnit = (ImageView) findViewById(R.id.ivUnit);
if (getTempUnit().equals(PARAM_TEMP_UNIT_C))
ivUnit.setImageDrawable(getResources().getDrawable(
R.drawable.button_unit_f));
else if (getTempUnit().equals(PARAM_TEMP_UNIT_F))
ivUnit.setImageDrawable(getResources().getDrawable(
R.drawable.button_unit_c));
// Refresh button
ivRefresh = (ImageView) findViewById(R.id.ivRefresh);
// Search field
etSearch = (MyCustomEditText) findViewById(R.id.etSearch);
// set the animation of search field
// Animation Duration in milliseconds;
int duration = 500;
// Inflate animation
final AnimationSet inflate = new AnimationSet(true);
ScaleAnimation scaleIn = new ScaleAnimation(0f, 1.0f, 1.0f, 1.0f,
Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF,
0.5f);
scaleIn.setDuration(duration);
inflate.addAnimation(scaleIn);
inflate.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
etSearch.setVisibility(View.VISIBLE);
etSearch.requestFocus();
showSoftKeyboard(etSearch);
}
});
// Deflate animation
final AnimationSet deflate = new AnimationSet(true);
ScaleAnimation scaleDe = new ScaleAnimation(1.0f, 0f, 1.0f, 1.0f,
Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF,
0.5f);
scaleDe.setDuration(duration);
deflate.addAnimation(scaleDe);
deflate.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation animation) {
etSearch.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
hideSoftKeyboard(etSearch);
}
});
etSearch.setInflation(inflate);
etSearch.setDeflation(deflate);
// Running the change of digital clock on separate UI thread
// to avoid any delay of other action on UI.
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!Utils.androidMinimum(API_JELLY_BEAN_MR1)) {
// Using the widget class {@code DigitalClock} if the
// android api is less than 17
DigitalClock dcClock = (DigitalClock) findViewById(R.id.dcClock);
dcClock.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s,
int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
// Removed seconds
if (s.length() >= 5) {
if (s.charAt(4) == ':') {
s.delete(4, s.length());
} else if (s.length() >= 6
&& s.charAt(5) == ':') {
s.delete(5, s.length());
}
}
}
});
} else {
// Using the widget class {@code TextClock} if the android
// api is greater than or equal to 17
TextClock dcClock = (TextClock) findViewById(R.id.dcClock);
dcClock.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s,
int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
// Removed seconds
if (s.length() >= 5) {
if (s.charAt(4) == ':') {
s.delete(4, s.length());
} else if (s.length() >= 6
&& s.charAt(5) == ':') {
s.delete(5, s.length());
}
}
}
});
}
}
});
mIconFinder = new IconFinder(this);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
* Create a new location client, using the enclosing class to handle
* callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
setContentView(R.layout.activity_main);
.
.
.
Could you please explain, what is the best way to manage the new Permission-System. Please explain to me in a simple way
The coarse and fine location are considered dangerous permissions. These permissions have to be explicitly requested on Android 6 and up. Here's one way to go about it:
public void checkPermission(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
){//Can add more as per requirement
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},
123);
}
}
And for calling:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
checkPermission();
}
You can implement it anywhere. You can request it on the first launch, or whenever they're needed.
Nonetheless, always remember to check whether you have the permission before using it. The permission can be revoked at runtime as well, which would bypass any checks you have on startup.
Here's one way to do it:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
This is called before initializing the map because the map needs permissions(the cause of the crash)
*/
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M ) {
checkPermission();
}
// Re-check before enabling. You can add an else statement to warn the user about the lack of functionality if it's disabled.
// "or" is used instead of "and" as per the error. If it requires both, flip it over to &&. (I'm not sure, I haven't used GPS stuff before)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED){
/*
* Create a new location client, using the enclosing class to handle
* callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
}
setContentView(R.layout.activity_main);
.
.
.
This is the way I recommend doing it. There are other ways to deal with permissions, but no matter what you pick, do make sure you check whether you have the permission before accessing an API dependent on dangerous permissions. The permissions can be withdrawn at runtime without triggering the destroy lifecycle events, and therefore bypass any checks you have on startup to make sure you have the permissions. Because of that, checking before you use APIs is extremely important.
There's also other ways to implement permission requesting. If you're looking for alternatives to this super basic startup check, here's some ideas:
There's also more ways to go about this than I know if. I haven't touched Android in a couple years, and I'm far from familiar with all the ways to request permissions. See also the documentation for potentially more up to date advice.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With