Skip to content

Commit

Permalink
added code to store temporary uri permissions and also consult them w…
Browse files Browse the repository at this point in the history
…hen deciding whether we can write to a given uri or not
  • Loading branch information
Christian Gogolin committed Jan 13, 2016
1 parent eece517 commit 1ee94f8
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 35 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Expand Up @@ -39,3 +39,15 @@ platform/android/.settings
docs/browse
include/mupdf/pdf/name-table.h
source/pdf/pdf-name-table.h
platform/android/appcompat/bin
platform/android/appcompat/gen
platform/android/cardview/bin
platform/android/cardview/gen
platform/android/design/bin
platform/android/design/gen
platform/android/recyclerview/bin
platform/android/recyclerview/gen
down vote
accepted
.gradle
local.properties
29 changes: 28 additions & 1 deletion platform/android/AndroidManifest.xml
Expand Up @@ -6,7 +6,7 @@
android:installLocation="auto">
<permission android:name="com.cgogolin.penandpdf.LAUNCH_PEN_AND_PDF_FILE_CHOOSER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<!-- <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />-->
<uses-permission android:name="com.cgogolin.penandpdf.LAUNCH_PEN_AND_PDF_FILE_CHOOSER" />
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="23" />
<supports-screens
Expand Down Expand Up @@ -83,7 +83,34 @@
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
<!-- <meta-data
android:name="android.content.DOCUMENT_PROVIDER"
android:value="true" />-->
</provider>
<!--
The drive provider:
<provider
android:name=".ExternalStorageProvider"
android:authorities="com.android.externalstorage.documents"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<meta-data
android:name="android.content.DOCUMENT_PROVIDER"
android:value="true" />
</provider>-->
<!-- <provider
android:name="com.cgogolin.penandpdf.MyContentProvider"
android:authorities="com.cgogolin.penandpdf.MyContentProvider"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS"
android:enabled="@bool/atLeastKitKat">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>-->

<!-- This activity alias is added so that GET_CONTENT intent-filter
can be disabled for builds on API level 19 and higher. -->
<!-- <activity-alias android:name="com.android.example.app.MyPicker" -->
Expand Down
64 changes: 64 additions & 0 deletions platform/android/src/com/cgogolin/penandpdf/MyContentProvider.java
@@ -0,0 +1,64 @@
package com.cgogolin.penandpdf;

import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsProvider;

import java.io.FileNotFoundException;

public class MyContentProvider extends DocumentsProvider {

private final static String[] rootColumns = new String[]{
"_id", "root_id", "title", "icon"
};
private final static String[] docColumns = new String[]{
"_id", "document_id", "_display_name", "mime_type", "icon"
};

MatrixCursor matrixCursor;
MatrixCursor matrixRootCursor;

@Override
public boolean onCreate() {

matrixRootCursor = new MatrixCursor(rootColumns);
matrixRootCursor.addRow(new Object[]{1, 1, "TEST", R.mipmap.ic_launcher});

matrixCursor = new MatrixCursor(docColumns);
matrixCursor.addRow(new Object[]{1, 1, "a.pdf", "application/pdf", R.mipmap.ic_launcher});
matrixCursor.addRow(new Object[]{2, 2, "b.pdf", "application/pdf", R.mipmap.ic_launcher});
matrixCursor.addRow(new Object[]{3, 3, "c.pdf", "application/pdf", R.mipmap.ic_launcher});

return true;
}

@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
return matrixRootCursor;
}

@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {

return matrixCursor;
}

@Override
public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
String sortOrder)
throws FileNotFoundException {

return matrixCursor;
}

@Override
public ParcelFileDescriptor openDocument(String documentId, String mode,
CancellationSignal signal)
throws FileNotFoundException {

return null;
}
}
49 changes: 31 additions & 18 deletions platform/android/src/com/cgogolin/penandpdf/PenAndPDFActivity.java
Expand Up @@ -79,7 +79,7 @@ public void execute(Runnable r) {
}
}

public class PenAndPDFActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener, android.support.v7.widget.SearchView.OnQueryTextListener, android.support.v7.widget.SearchView.OnCloseListener, FilePicker.FilePickerSupport
public class PenAndPDFActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener, android.support.v7.widget.SearchView.OnQueryTextListener, android.support.v7.widget.SearchView.OnCloseListener, FilePicker.FilePickerSupport, TemporaryUriPermission.TemporaryUriPermissionProvider
{
enum ActionBarMode {Main, Annot, Edit, Search, Selection, Hidden, AddingTextAnnot, Empty};

Expand Down Expand Up @@ -117,6 +117,8 @@ enum ActionBarMode {Main, Annot, Edit, Search, Selection, Hidden, AddingTextAnno
private AlertDialog mAlertDialog;
private FilePicker mFilePicker;

private ArrayList<TemporaryUriPermission> temporaryUriPermissions = new ArrayList<TemporaryUriPermission>();

//Code from http://stackoverflow.com/questions/13209494/how-to-get-the-full-file-path-from-uri
/**
* Get a file path from a Uri. This will get the the path for Storage Access
Expand Down Expand Up @@ -421,7 +423,7 @@ public void onCreate(Bundle savedInstanceState)
protected void onResume()
{
super.onResume();
Log.i(getString(R.string.app_name), "onResume()");
// Log.i(getString(R.string.app_name), "onResume()");

Intent intent = getIntent();
if (Intent.ACTION_MAIN.equals(intent.getAction()))
Expand All @@ -436,7 +438,8 @@ else if (Intent.ACTION_VIEW.equals(intent.getAction()))
if (core != null) //OK, so apparently we have a valid pdf open
{
// Try to take permissions
tryToTakePermissions(intent.getData());
tryToTakePersistablePermissions(intent);
rememberTemporaryUriPermission(intent);

//Setup the mDocView
setupDocView();
Expand Down Expand Up @@ -861,13 +864,14 @@ public void onClick(DialogInterface dialog, int which) {

}

private void tryToTakePermissions(Uri uri) {
private void tryToTakePersistablePermissions(Intent intent) {
Uri uri = intent.getData();
if (android.os.Build.VERSION.SDK_INT >= 19)
{
try
{
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.i(getString(R.string.app_name), "Succesfully took persistable read uri permissions for "+uri);
Log.i(getString(R.string.app_name), "Succesfully took persistable read uri permissions for "+uri.getPath());
}
catch(Exception e)
{
Expand Down Expand Up @@ -910,12 +914,12 @@ public void setupCore() {//Called during onResume()
alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
// finish();
}
});
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
finish();
// finish();
}
});
alert.show();
Expand Down Expand Up @@ -1254,10 +1258,12 @@ public void openNewDocument(String filename) throws java.io.IOException {

public void openDocument() {
if (core!=null && core.hasChanges()) {
final PenAndPDFActivity activity = this;//Not sure if this is good style...

DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_POSITIVE) {
if(core.canSaveToCurrentUri(getApplicationContext()))
if(core.canSaveToCurrentUri(activity))
{
if(!save())
showInfo(getString(R.string.error_saveing));
Expand Down Expand Up @@ -1299,16 +1305,19 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
if(resultCode == AppCompatActivity.RESULT_OK)
{
if (intent != null) {
Uri uri = intent.getData();

getIntent().setAction(Intent.ACTION_VIEW);
getIntent().setData(uri);
getIntent().setData(intent.getData());
getIntent().setFlags((getIntent().getFlags() & ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION & ~Intent.FLAG_GRANT_READ_URI_PERMISSION) | (intent.getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) | (intent.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION));//Set the read and writ flags to what they are in the received intent

Log.i(getString(R.string.app_name), "onActivityResult() flags="+intent.getFlags()+" and write flag is set to "+((intent.getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == Intent.FLAG_GRANT_WRITE_URI_PERMISSION)+" and read flag is set to "+((intent.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION) == Intent.FLAG_GRANT_READ_URI_PERMISSION));

if (core != null) {
core.onDestroy();
core = null;
}
onResume();//New core and new docview are setup here
// tryToTakePersistablePermissions(intent);//No need to do this, is done during onResume()
// rememberTemporaryUriPermission(intent);//No need to do this, is done during onResume()
// onResume();//New core and new docview are setup during onResume(), which is automatically called after onActivityResult()
}
}
break;
Expand Down Expand Up @@ -1403,7 +1412,8 @@ private boolean saveAs(Uri uri) {
//Save the viewport under the new name
saveViewportAndRecentFiles(core.getUri());
//Try to take permissions
tryToTakePermissions(getIntent().getData());
tryToTakePersistablePermissions(getIntent());
rememberTemporaryUriPermission(getIntent());
//Resetup the ShareActionProvider
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
Expand Down Expand Up @@ -1444,12 +1454,7 @@ private void saveViewport(SharedPreferences.Editor edit, String path) {

private void restoreVieport() {
if (core != null && mDocView != null) {
// String path = core.getPath(); //Can be null
SharedPreferences prefs = getSharedPreferences(SettingsActivity.SHARED_PREFERENCES_STRING, Context.MODE_MULTI_PROCESS);
// if(path != null)
// setViewport(prefs, path);
// else
// setViewport(prefs, core.getFileName());
setViewport(prefs, core.getUri());
}
}
Expand Down Expand Up @@ -1839,4 +1844,12 @@ public static File getNotesDir(Context contex) {
return contex.getDir("notes", Context.MODE_WORLD_READABLE);
}

public ArrayList<TemporaryUriPermission> getTemporaryUriPermissions() {
return temporaryUriPermissions;
}

public void rememberTemporaryUriPermission(Intent intent) {
Log.i(getString(R.string.app_name), "remembering temporary permission for "+intent.getData()+" with flags="+intent.getFlags());
temporaryUriPermissions.add(new TemporaryUriPermission(intent));
}
}
Expand Up @@ -112,7 +112,7 @@ private static String[] resolveDocumentProjection(String[] projection) {

private String getDocIdForFile(File file) {
// return file.getName();
return file.getAbsolutePath();
return file.getAbsolutePath().substring(1);//remove the leading '/'
}

private File getFileForDocId(String documentId) {
Expand All @@ -121,7 +121,7 @@ private File getFileForDocId(String documentId) {
return mNotesDir;
else
// return new File(mNotesDir.getPath(), documentId);
return new File(documentId);
return new File("/"+documentId);
}

private String getChildMimeTypes(File file) {
Expand Down
42 changes: 28 additions & 14 deletions platform/android/src/com/cgogolin/penandpdf/PenAndPDFCore.java
@@ -1,6 +1,7 @@
package com.cgogolin.penandpdf;
import java.util.ArrayList;

import android.support.v7.app.AppCompatActivity;
import android.util.Base64;
import android.content.pm.PackageManager;
import android.content.UriPermission;
Expand Down Expand Up @@ -40,18 +41,18 @@ public PenAndPDFCore(Context context, Uri uri) throws Exception

public void init(Context context, Uri uri) throws Exception
{
// Log.e("Core", "creating with uri="+uri);
// Log.i("Core", "creating with uri="+uri);

this.uri = uri;

if(new File(Uri.decode(uri.getEncodedPath())).isFile()) //Uri points to a file
{
Log.e("Core", "uri points to file");
Log.i("Core", "uri points to file");
super.init(context, Uri.decode(uri.getEncodedPath()));
}
else if (uri.toString().startsWith("content://")) //Uri points to a content provider
{
Log.e("Core", "uri points to content");
Log.i("Core", "uri points to content");
String displayName = null;
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.MediaColumns.DISPLAY_NAME}, null, null, null); //This should be done asynchonously!

Expand Down Expand Up @@ -105,7 +106,7 @@ else if (uri.toString().startsWith("content://")) //Uri points to a content prov
is.read(buffer, 0, len);
is.close();
if(pfd != null) pfd.close();
Log.e("Core", "read "+len+" bytes into buffer "+buffer);
Log.i("Core", "read "+len+" bytes into buffer "+buffer);
super.init(context, buffer, displayName);
}
else
Expand Down Expand Up @@ -216,24 +217,37 @@ private static void copyStream(InputStream input, OutputStream output)
}
}

public boolean canSaveToUriViaContentResolver(Context context, Uri uri) {
public <T extends Context & TemporaryUriPermission.TemporaryUriPermissionProvider> boolean canSaveToUriViaContentResolver(T context, Uri uri) {
try
{
boolean haveWritePermissionToUri = false;
if (android.os.Build.VERSION.SDK_INT >= 19)
for(TemporaryUriPermission permission : (context).getTemporaryUriPermissions()) {
if(permission.isWritePermission() && permission.getUri().equals(uri))
{
haveWritePermissionToUri = true;
break;
}
}
if(haveWritePermissionToUri == false)
{
for( UriPermission permission : context.getContentResolver().getPersistedUriPermissions()) {
if(permission.isWritePermission() && permission.getUri().equals(uri))
if (android.os.Build.VERSION.SDK_INT >= 19)
{
for(UriPermission permission : (context).getContentResolver().getPersistedUriPermissions()) {
if(permission.isWritePermission() && permission.getUri().equals(uri))
{
haveWritePermissionToUri = true;
break;
}
}
}
else
{
if(context.checkCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED)
{
haveWritePermissionToUri = true;
}
}
}
else
{
if(context.checkCallingUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PackageManager.PERMISSION_GRANTED)
haveWritePermissionToUri = true;
}

if(haveWritePermissionToUri)
{
Expand Down Expand Up @@ -272,7 +286,7 @@ public boolean canSaveToUriAsFile(Context context, Uri uri) {
}
}

public boolean canSaveToCurrentUri(Context context) {
public <T extends Context & TemporaryUriPermission.TemporaryUriPermissionProvider> boolean canSaveToCurrentUri(T context) {
return canSaveToUriViaContentResolver(context, getUri()) || canSaveToUriAsFile(context, getUri());
}

Expand Down

0 comments on commit 1ee94f8

Please sign in to comment.