Я экспериментирую с Storage Access Framework, потому что я хотел бы улучшить свое приложение, которое уже перешло на SAF (хотя мне пришлось отказаться от некоторых функций из-за некоторых недостатков дизайна SAF или, возможно, ошибок).
Теперь я хочу, чтобы мое приложение уведомлялось, когда пользователь что-то делает в папке SAF в облаке.
В настоящее время оно является чисто экспериментальным, поскольку взломать, что можно получить URIпапка cloud от GDrive.
(другие облачные провайдеры, похоже, не готовы к SAF, исправьте меня, если я ошибаюсь)
Я просто работаю заранее, прежде чем они выпустят полную функцию (об этом есть запрос исправления ошибки), чтобы можно было выбрать папку в корневом каталоге облачного хранилища в системном средстве выбора.
Я создал простое приложение для создания папки SAF в облаке (выбор невозможен принастоящее время в облачном хранилище) и прослушивание событий.
package com.example.safevents;
import android.app.Activity;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
Activity activity;
Handler handler;
Observer observer;
class Observer extends ContentObserver {
public Observer(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
Log.d("SAF","event fired; selfchange="+selfChange);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.d("SAF","event fired on uri="+uri.toString()+"; selfchange="+selfChange);
}
}
public void openPickerForFolderCreation(Activity activity, int requestCode) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.setType("vnd.android.document/directory");
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
activity.startActivityForResult(intent, requestCode);
}
public Uri takePermanentReadWritePermissions(Activity activity, Uri uri, int flags) {
int takeFlags = flags
&
(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
);
;
ContentResolver resolver = activity.getContentResolver();
resolver.takePersistableUriPermission(uri, takeFlags);
return uri;
}
public boolean arePermissionsGranted(Activity activity, String uriString) {
Uri uri = Uri.parse(uriString);
ContentResolver resolver = activity.getContentResolver();
List<UriPermission> list = resolver.getPersistedUriPermissions();
for (int i = 0; i < list.size(); i++) {
if (((Uri.decode(list.get(i).getUri().toString())).equals(Uri.decode(uriString))) && list.get(i).isWritePermission() && list.get(i).isReadPermission()) {
return true;
}
}
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
activity=this;
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
SAFUtils.openPickerForFolderCreation(activity,0);
}
});
}
@ Переопределить защищенный void onDestroy () {super.onDestroy();// важный метод getContentResolver (). unregisterContentObserver (наблюдатель);}
@Override
protected void onActivityResult(int requestCode, int resultCode,Intent returnedIntent)
{
Log.d("SAF","picker returned "+resultCode+" "+requestCode);
if (returnedIntent!=null) Log.d("SAF","intent="+returnedIntent.toString());
if (returnedIntent.getData()!=null) Log.d("SAF","uri="+returnedIntent.getData().toString());
//resultCode is -1
if (resultCode==-1)
if (requestCode==0)
{
Uri uri=SAFUtils.takePermanentReadWritePermissions(activity,returnedIntent.getData(),returnedIntent.getFlags());
Log.d("SAF","read/write permissions="+SAFUtils.arePermissionsGranted(activity,uri.toString()));
handler=new Handler(){};
observer=new Observer(handler);
getContentResolver().
registerContentObserver(
uri,
true,
observer);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Я получаю следующие строки журнала:
com.example.safevents D/SAF: picker returned -1 0
com.example.safevents D/SAF: intent=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=3;doc=encoded=randomalphanumericsequence= flg=0x43 }
com.example.safevents D/SAF: uri=content://com.google.android.apps.docs.storage/document/acc%3D3%3Bdoc%3Dencoded%3Dthesamealphanumericsequence%3D
com.example.safevents D/SAF: read/write permissions=true
, но я не получаю никаких уведомлений о событиях после некоторых простых операций с файлами внутри созданной папки.
Обратите внимание, что resultCode = -1.
Также обратите внимание, что пропуск вызова метода takePersistentPermissions приводит к отсутствию разрешений, что связано с функцией полезности, которая проверяет их, не находя uri в списке.
Что-то не так в кодевыше?
Является ли создание облачной папки SAF таким взломом, чтобы она не работала?
Возможно ли дальнейшее взломать, чтобы заставить это работать, чтобы быть предварительно протестированным примерно?
Эта функция SAF недоступна в настоящее время?