Fix permission problems on API 23+

This commit is contained in:
Adam Jones 2016-06-08 23:03:17 +01:00
parent 7955cfe71b
commit 8bf9808cfc
5 changed files with 137 additions and 100 deletions

View file

@ -8,12 +8,12 @@ dependencies {
compile 'com.nostra13.universalimageloader:universal-image-loader:1.8.4' compile 'com.nostra13.universalimageloader:universal-image-loader:1.8.4'
compile 'ch.acra:acra:4.5.0' compile 'ch.acra:acra:4.5.0'
compile 'org.mediawiki:api:1.3' compile 'org.mediawiki:api:1.3'
compile 'de.keyboardsurfer.android.widget:crouton:1.8.5@aar'
compile 'commons-codec:commons-codec:1.10' compile 'commons-codec:commons-codec:1.10'
compile 'com.android.support:support-v4:23.4.0' compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:appcompat-v7:23.4.0' compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0' compile 'com.android.support:design:23.4.0'
//noinspectio GradleDependency - old version has required feature
//noinspection GradleDependency - old version has required feature
compile 'com.google.code.gson:gson:1.4' compile 'com.google.code.gson:gson:1.4'
} }

View file

@ -8,10 +8,6 @@
android:targetSdkVersion="23" /> android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/> <uses-permission android:name="android.permission.READ_SYNC_STATS"/>

View file

@ -1,26 +1,39 @@
package fr.free.nrw.commons.auth; package fr.free.nrw.commons.auth;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NavUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException; import java.io.IOException;
import android.content.*;
import android.net.Uri;
import android.text.*;
import android.view.inputmethod.EditorInfo;
import de.keyboardsurfer.android.widget.crouton.*;
import android.os.*;
import android.accounts.*;
import android.app.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import android.support.v4.app.NavUtils;
import fr.free.nrw.commons.*;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.EventLog; import fr.free.nrw.commons.EventLog;
import fr.free.nrw.commons.contributions.*; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
public class LoginActivity extends AccountAuthenticatorActivity { public class LoginActivity extends AccountAuthenticatorActivity {
@ -79,7 +92,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
} else if(result.equals("NotExists") || result.equals("Illegal") || result.equals("NotExists")) { } else if(result.equals("NotExists") || result.equals("Illegal") || result.equals("NotExists")) {
response = R.string.login_failed_username; response = R.string.login_failed_username;
passwordEdit.setText(""); passwordEdit.setText("");
} else if(result.equals("EmptyPass") || result.equals("WrongPass")) { } else if(result.equals("EmptyPass") || result.equals("WrongPass") || result.equals("WrongPluginPass")) {
response = R.string.login_failed_password; response = R.string.login_failed_password;
passwordEdit.setText(""); passwordEdit.setText("");
} else if(result.equals("Throttled")) { } else if(result.equals("Throttled")) {
@ -88,10 +101,11 @@ public class LoginActivity extends AccountAuthenticatorActivity {
response = R.string.login_failed_blocked; response = R.string.login_failed_blocked;
} else { } else {
// Should never really happen // Should never really happen
Log.d("Commons", "Login failed with reason: " + result);
response = R.string.login_failed_generic; response = R.string.login_failed_generic;
} }
Crouton.makeText(context, response, Style.ALERT, R.id.loginErrors).show(); Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
dialog.dismiss(); dialog.cancel();
} }
} }

View file

@ -5,16 +5,18 @@ import android.content.ContentResolver;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NavUtils; import android.support.v4.app.NavUtils;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.util.Log; import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
@ -28,6 +30,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.auth.WikiAccountAuthenticator; import fr.free.nrw.commons.auth.WikiAccountAuthenticator;
import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.category.CategorizationFragment;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.modifications.CategoryModifier; import fr.free.nrw.commons.modifications.CategoryModifier;
import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.modifications.ModifierSequence;
@ -39,12 +42,12 @@ import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
*/ */
public class ShareActivity public class ShareActivity
extends AuthenticatedActivity extends AuthenticatedActivity
implements fr.free.nrw.commons.upload.SingleUploadFragment.OnUploadActionInitiated, implements SingleUploadFragment.OnUploadActionInitiated,
CategorizationFragment.OnCategoriesSaveHandler { CategorizationFragment.OnCategoriesSaveHandler {
private static final String TAG = ShareActivity.class.getName(); private static final String TAG = ShareActivity.class.getName();
private fr.free.nrw.commons.upload.SingleUploadFragment shareView; private SingleUploadFragment shareView;
private CategorizationFragment categorizationFragment; private CategorizationFragment categorizationFragment;
private CommonsApplication app; private CommonsApplication app;
@ -54,14 +57,14 @@ public class ShareActivity
private String mediaUriString; private String mediaUriString;
private Uri mediaUri; private Uri mediaUri;
private fr.free.nrw.commons.contributions.Contribution contribution; private Contribution contribution;
private ImageView backgroundImageView; private ImageView backgroundImageView;
private fr.free.nrw.commons.upload.UploadController uploadController; private UploadController uploadController;
private CommonsApplication cacheObj; private CommonsApplication cacheObj;
private boolean cacheFound; private boolean cacheFound;
private fr.free.nrw.commons.upload.GPSExtractor imageObj; private GPSExtractor imageObj;
public ShareActivity() { public ShareActivity() {
super(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE); super(WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE);
@ -77,8 +80,8 @@ public class ShareActivity
Log.d(TAG, "Cache the categories found"); Log.d(TAG, "Cache the categories found");
} }
uploadController.startUpload(title, mediaUri, description, mimeType, source, new fr.free.nrw.commons.upload.UploadController.ContributionUploadProgress() { uploadController.startUpload(title, mediaUri, description, mimeType, source, new UploadController.ContributionUploadProgress() {
public void onUploadStarted(fr.free.nrw.commons.contributions.Contribution contribution) { public void onUploadStarted(Contribution contribution) {
ShareActivity.this.contribution = contribution; ShareActivity.this.contribution = contribution;
showPostUpload(); showPostUpload();
} }
@ -140,7 +143,7 @@ public class ShareActivity
} else { } else {
EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT) EventLog.schema(CommonsApplication.EVENT_UPLOAD_ATTEMPT)
.param("username", app.getCurrentAccount().name) .param("username", app.getCurrentAccount().name)
.param("source", getIntent().getStringExtra(fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE)) .param("source", getIntent().getStringExtra(UploadService.EXTRA_SOURCE))
.param("multiple", true) .param("multiple", true)
.param("result", "cancelled") .param("result", "cancelled")
.log(); .log();
@ -153,10 +156,10 @@ public class ShareActivity
app.getApi().setAuthCookie(authCookie); app.getApi().setAuthCookie(authCookie);
shareView = (fr.free.nrw.commons.upload.SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
if(shareView == null && categorizationFragment == null) { if(shareView == null && categorizationFragment == null) {
shareView = new fr.free.nrw.commons.upload.SingleUploadFragment(); shareView = new SingleUploadFragment();
this.getSupportFragmentManager() this.getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.add(R.id.single_upload_fragment_container, shareView, "shareView") .add(R.id.single_upload_fragment_container, shareView, "shareView")
@ -173,10 +176,15 @@ public class ShareActivity
finish(); finish();
} }
/**
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
* Then initiates the calls to MediaWiki API through an instance of MwVolleyApi.
*/
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
uploadController = new fr.free.nrw.commons.upload.UploadController(this); uploadController = new UploadController(this);
setContentView(R.layout.activity_share); setContentView(R.layout.activity_share);
app = (CommonsApplication)this.getApplicationContext(); app = (CommonsApplication)this.getApplicationContext();
@ -186,10 +194,10 @@ public class ShareActivity
if(intent.getAction().equals(Intent.ACTION_SEND)) { if(intent.getAction().equals(Intent.ACTION_SEND)) {
mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); mediaUri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
if(intent.hasExtra(fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE)) { if(intent.hasExtra(UploadService.EXTRA_SOURCE)) {
source = intent.getStringExtra(fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE); source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
} else { } else {
source = fr.free.nrw.commons.contributions.Contribution.SOURCE_EXTERNAL; source = Contribution.SOURCE_EXTERNAL;
} }
mimeType = intent.getType(); mimeType = intent.getType();
} }
@ -203,80 +211,94 @@ public class ShareActivity
contribution = savedInstanceState.getParcelable("contribution"); contribution = savedInstanceState.getParcelable("contribution");
} }
requestAuthToken(); requestAuthToken();
}
/**
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
* Then initiates the calls to MediaWiki API through an instance of MwVolleyApi.
*/
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "Uri: " + mediaUriString); Log.d(TAG, "Uri: " + mediaUriString);
Log.d(TAG, "Ext storage dir: " + Environment.getExternalStorageDirectory()); Log.d(TAG, "Ext storage dir: " + Environment.getExternalStorageDirectory());
// Check storage permissions if marshmallow or newer
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
// Check permissions (ContextCompat.checkSelfPermission(this,
if (ContextCompat.checkSelfPermission(this.getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ||
Manifest.permission.READ_EXTERNAL_STORAGE) ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
!= PackageManager.PERMISSION_GRANTED) { if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE) && (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)))) {
String permissionRationales = getResources().getString(R.string.storage_permission_rationale) + "\n" + getResources().getString(R.string.location_permission_rationale);
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), permissionRationales,
Manifest.permission.READ_EXTERNAL_STORAGE)) { Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
Log.i(TAG,
"Displaying camera permission rationale to provide additional context.");
Snackbar.make(this.findViewById(android.R.id.content), R.string.storage_permission_rationale, Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok, new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
ActivityCompat.requestPermissions(ShareActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); ActivityCompat.requestPermissions(ShareActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
});
snackbar.show();
View snackbarView = snackbar.getView();
TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
textView.setMaxLines(3);
} else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
Snackbar.make(findViewById(android.R.id.content), R.string.storage_permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(ShareActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}).show();
} else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
Snackbar.make(findViewById(android.R.id.content), R.string.location_permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(ShareActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
} }
}).show(); }).show();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
} }
} } else {
// Convert image Uri to file path
String filePath = FileUtils.getPath(this, mediaUri);
Log.d(TAG, "Filepath: " + filePath);
//convert image Uri to file path // Check location permissions if marshmallow or newer
String filePath = fr.free.nrw.commons.upload.FileUtils.getPath(this, mediaUri); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this,
Log.d(TAG, "Filepath: " + filePath); Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Calling GPSExtractor");
imageObj = new GPSExtractor(filePath, this);
imageObj.registerLocationManager();
Log.d(TAG, "Calling GPSExtractor"); if (filePath != null && !filePath.equals("")) {
imageObj = new fr.free.nrw.commons.upload.GPSExtractor(filePath, this); // Gets image coords if exist, otherwise gets last known coords
imageObj.registerLocationManager(); String decimalCoords = imageObj.getCoords();
if (filePath != null && !filePath.equals("")) { if (decimalCoords != null) {
//Gets image coords if exist, otherwise gets last known coords Log.d(TAG, "Decimal coords of image: " + decimalCoords);
String decimalCoords = imageObj.getCoords();
if (decimalCoords != null) { // Only set cache for this point if image has coords
Log.d(TAG, "Decimal coords of image: " + decimalCoords); if (imageObj.imageCoordsExists) {
double decLongitude = imageObj.getDecLongitude();
double decLatitude = imageObj.getDecLatitude();
app.cacheData.setQtPoint(decLongitude, decLatitude);
}
//Only set cache for this point if image has coords MwVolleyApi apiCall = new MwVolleyApi(this);
if (imageObj.imageCoordsExists) {
double decLongitude = imageObj.getDecLongitude();
double decLatitude = imageObj.getDecLatitude();
app.cacheData.setQtPoint(decLongitude, decLatitude);
}
fr.free.nrw.commons.upload.MwVolleyApi apiCall = new fr.free.nrw.commons.upload.MwVolleyApi(this); List displayCatList = app.cacheData.findCategory();
boolean catListEmpty = displayCatList.isEmpty();
List displayCatList = app.cacheData.findCategory(); // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
boolean catListEmpty = displayCatList.isEmpty(); if (catListEmpty) {
cacheFound = false;
//if no categories found in cache, call MW API to match image coords with nearby Commons categories apiCall.request(decimalCoords);
if (catListEmpty) { Log.d(TAG, "displayCatList size 0, calling MWAPI" + displayCatList.toString());
cacheFound = false; } else {
apiCall.request(decimalCoords); cacheFound = true;
Log.d(TAG, "displayCatList size 0, calling MWAPI" + displayCatList.toString()); Log.d(TAG, "Cache found, setting categoryList in MwVolleyApi to " + displayCatList.toString());
MwVolleyApi.setGpsCat(displayCatList);
} else { }
cacheFound = true; }
Log.d(TAG, "Cache found, setting categoryList in MwVolleyApi to " + displayCatList.toString());
fr.free.nrw.commons.upload.MwVolleyApi.setGpsCat(displayCatList);
} }
} }
} }
@ -285,7 +307,10 @@ public class ShareActivity
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
imageObj.unregisterLocationManager(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
imageObj.unregisterLocationManager();
}
} }
@Override @Override

View file

@ -149,6 +149,8 @@
<string name="provider_campaigns">Campaigns</string> <string name="provider_campaigns">Campaigns</string>
<string name="menu_refresh">Refresh</string> <string name="menu_refresh">Refresh</string>
<string name="storage_permission_rationale">Storage permission is needed to access photos</string> <string name="storage_permission_rationale">Recommended: Storage for photo metadata</string>
<string name="location_permission_rationale">Optional: Location for geo-tag</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="back">Back</string>
</resources> </resources>