Статьи

Добавление Dropbox в приложение для Android

Эта статья была рецензирована Верной Анчетой . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!

Хранение документов и файлов имеет важное значение для вычислительных устройств с момента их создания. Традиционно хранилище было локальным, но в последние годы наблюдается все больший переход к хранению в облаке. Облачное хранилище имеет свои проблемы, но предлагает почти неограниченную емкость и низкую стоимость.

В этом руководстве я объясню, как хранить файлы из приложения Android в Dropbox, используя Dropbox Core SDK для Java 6+ в Android. Согласно описанию SDK, этот Core SDK является библиотекой Java для доступа к основанному на HTTP API-интерфейсу Dropbox v2 . Этот SDK также поддерживает более старый Core API v1 , но эта поддержка будет удалена в какой-то момент.

Вы можете найти окончательный код этого проекта на GitHub .

Чтобы подключиться к Dropbox, вам необходимо иметь учетную запись Dropbox и использовать API Dropbox. Создайте новое приложение в консоли приложения .

Создать приложение

После создания нового приложения оно будет переведено в состояние разработки. С этим статусом приложение может использоваться только тестовыми пользователями, и если вы включите дополнительных пользователей, их может быть до 100. Когда приложение будет готово, вы должны подать заявку на производственный статус.

Консоль приложения содержит информацию о приложении, и вам понадобится ключ приложения для проекта Android.

Консоль приложения

Добавить разрешения

Создайте проект в Android Studio (с активностью входа в систему ) и в AndroidManifest.xml добавьте следующее для доступа к Интернету и хранилищу. Вы можете удалить автоматически сгенерированные разрешения, добавленные в AndroidManifest.xml с помощью LoginActivity . Они не требуются в этом проекте.

 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 

Добавить зависимости

В build.gradle (Модуль: приложение) добавьте следующее:

 dependencies { //... compile 'com.dropbox.core:dropbox-core-sdk:2.0.1' compile 'com.squareup.picasso:picasso:2.5.2' } 

Примечание . Первая зависимость предназначена для API Dropbox Core. Библиотека Пикассо не является обязательной, но полезна для загрузки изображений.

Добавьте активность Dropbox

Откройте AndroidManifest.xml и в разделе <application></application> добавьте AuthActivity .

 <application> ... <!-- Dropbox AuthActivity --> <activity android:name="com.dropbox.core.android.AuthActivity" android:configChanges="orientation|keyboard" android:launchMode="singleTask"> <intent-filter> <!-- Insert your app key after “db- ...” --> <data android:scheme="db-APP_KEY" /> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> </application> 

Ошибка дублирующегося файла

Во время тестирования приложения я обнаружил ошибку, которая возникла из-за конфликта во время сборки нескольких файлов License.txt или Notice.txt после прочтения этого ответа на Stackoverflow . Чтобы избежать этого конфликта, в build.gradle (Module: app) в разделе android {…} исключите эти файлы.

 android { ... packagingOptions { exclude 'META-INF/LICENSE' exclude 'META-INF/LICENSE.txt' exclude 'META-INF/NOTICE' exclude 'META-INF/NOTICE.txt' } } 

Ключ приложения

Добавьте ключ вашего приложения в strings.xml :

 <!-- Change this to your app key --> <string name="APP_KEY">APP_KEY_HERE</string> 

Теперь проект Android настроен на использование API Dropbox, добавление новой активности в проект с помощью пункта меню Файл-> Новая-> Деятельность-> Основная активность . Назовите это MainActivity.java .

Описание Проекта

Образец приложения будет состоять из входа в учетную запись пользователя Dropbox, получения подробной информации и загрузки изображения в папку Dropbox приложения. Проект будет иметь два LoginActivity деятельности: LoginActivity и MainActivity .

Войти с Dropbox

Задача LoginActivity проста. При нажатии кнопки он выполняет задачу входа в систему, чтобы сгенерировать токен доступа для приложения Dropbox, идентифицированного APP_KEY в строковых ресурсах.

Метод startOAuth2Authentication() открывает AuthActivity Dropbox, это действие, добавляемое в файл манифеста. В AuthActivity пользователь должен подтвердить свою учетную запись Dropbox. После того, как пользователь подтвердил свою учетную запись Dropbox, перенаправьте его в LoginActivity .

Метод startOAuth2Authentication() не возвращает startOAuth2Authentication() доступа, он только открывает AuthActivity который внутренне используется для аутентификации. Теперь пользователь прошел аутентификацию, ему нужен токен доступа.

AuthActivity

AuthActivity

В LoginActivity замените весь автоматически сгенерированный код, кроме имени пакета проекта, следующим:

 public class LoginActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); Button SignInButton = (Button) findViewById(R.id.sign_in_button); SignInButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { Auth.startOAuth2Authentication(getApplicationContext(), getString(R.string.APP_KEY)); } }); } @Override protected void onResume() { super.onResume(); getAccessToken(); } public void getAccessToken() { String accessToken = Auth.getOAuth2Token(); //generate Access Token if (accessToken != null) { //Store accessToken in SharedPreferences SharedPreferences prefs = getSharedPreferences("com.example.valdio.dropboxintegration", Context.MODE_PRIVATE); prefs.edit().putString("access-token", accessToken).apply(); //Proceed to MainActivity Intent intent = new Intent(LoginActivity.this, MainActivity.class); startActivity(intent); } } } 

Для макета этого действия обновите Activity_login.xml :

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.valdio.dropboxintegration.LoginActivity"> <!-- Update package name --> <Button android:id="@+id/sign_in_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/sign_in" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout> 

Добавьте следующее в strings.xml для текста кнопки входа:

 ... <string name="sign_in">Sign in</string> 

Метод жизненного цикла onResume() запрашивает токен, вызывая метод getOAuth2Token() . Токен сохраняется в данных SharedPreferences приложения для последующего использования, затем открывается MainActivity .

onResume() жизненного цикла onResume() вызывался ранее, когда приложение открывалось впервые. Почему ты не получил токен тогда?

Это потому, что пользователь не был аутентифицирован раньше. При перенаправлении из AuthActivity с успешной аутентификацией метод onResume() будет вызван еще раз и сгенерирует токен доступа.

Клиент Dropbox теперь вошел в систему

В MainActivity объявляют следующие переменные:

 public class MainActivity extends AppCompatActivity { private static final int IMAGE_REQUEST_CODE = 101; private String ACCESS_TOKEN; ... } 

В MainActivity макета content_main.xml MainActivity замените содержимое следующим:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="valdioveliu.valdio.com.dropboxintegration.MainActivity" tools:showIn="@layout/activity_main"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/accountData"> <ImageView android:id="@+id/imageView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <TextView android:id="@+id/textView3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/imageView" android:layout_marginLeft="15dp" android:layout_marginTop="50dp" android:text="Name" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/name_textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:gravity="center_horizontal" android:text="" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/textView3" android:layout_marginLeft="15dp" android:layout_marginTop="58dp" android:text="Email" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/email_textView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_below="@+id/textView" android:gravity="center_horizontal" android:text="" android:textAppearance="?android:attr/textAppearanceLarge" /> </LinearLayout> </RelativeLayout> 

Информация об учетной записи

Получение информации об учетной записи пользователя или доступ к Dropbox любым способом — это сетевой запрос, а в Android сетевые запросы обрабатываются асинхронно. Все эти сетевые запросы требуют клиента Dropbox.

Создайте DropboxClient.java и добавьте следующий код:

 public class DropboxClient { public static DbxClientV2 getClient(String ACCESS_TOKEN) { // Create Dropbox client DbxRequestConfig config = new DbxRequestConfig("dropbox/sample-app", "en_US"); DbxClientV2 client = new DbxClientV2(config, ACCESS_TOKEN); return client; } } 

Строка ACCESS_TOKEN в getClient() — это токен, полученный при входе пользователя в систему.

Следующая задача — получить данные учетной записи пользователя. Класс UserAccountTask представляет запрос сведений об учетных записях. Конструктор этого асинхронного класса имеет интерфейсный параметр TaskDelegate используемый для возврата информации об учетных записях обратно в действие, в котором была выполнена задача.

Создайте UserAccountTask.java и добавьте следующий код:

 public class UserAccountTask extends AsyncTask<Void, Void, FullAccount> { private DbxClientV2 dbxClient; private TaskDelegate delegate; private Exception error; public interface TaskDelegate { void onAccountReceived(FullAccount account); void onError(Exception error); } UserAccountTask(DbxClientV2 dbxClient, TaskDelegate delegate){ this.dbxClient =dbxClient; this.delegate = delegate; } @Override protected FullAccount doInBackground(Void... params) { try { //get the users FullAccount return dbxClient.users().getCurrentAccount(); } catch (DbxException e) { e.printStackTrace(); error = e; } return null; } @Override protected void onPostExecute(FullAccount account) { super.onPostExecute(account); if (account != null && error == null){ //User Account received successfully delegate.onAccountReceived(account); } else { // Something went wrong delegate.onError(error); } } } 

Метод getUserAccount является примером того, как класс UserAcountTask выполняется в классе MainActivity .

Добавьте следующий метод в класс MainActivity , вы будете использовать его позже:

 protected void getUserAccount() { if (ACCESS_TOKEN == null)return; new UserAccountTask(DropboxClient.getClient(ACCESS_TOKEN), new UserAccountTask.TaskDelegate() { @Override public void onAccountReceived(FullAccount account) { //Print account's info Log.d("User", account.getEmail()); Log.d("User", account.getName().getDisplayName()); Log.d("User", account.getAccountType().name()); updateUI(account); } @Override public void onError(Exception error) { Log.d("User", "Error receiving account details."); } }).execute(); } 

Чтобы отобразить данные учетной записи пользователя в пользовательском интерфейсе, добавьте следующую функцию в класс MainActivity :

 private void updateUI(FullAccount account) { ImageView profile = (ImageView) findViewById(R.id.imageView); TextView name = (TextView) findViewById(R.id.name_textView); TextView email = (TextView) findViewById(R.id.email_textView); name.setText(account.getName().getDisplayName()); email.setText(account.getEmail()); Picasso.with(this) .load(account.getProfilePhotoUrl()) .resize(200, 200) .into(profile); } 

Загрузка в Dropbox

Загрузка задач происходит с помощью InputStream . Указанный файл преобразуется в InputStream и с помощью Core SDK поток загружается в папку приложений в Dropbox.

Создайте UploadTask.java и добавьте в него следующий код:

 public class UploadTask extends AsyncTask { private DbxClientV2 dbxClient; private File file; private Context context; UploadTask(DbxClientV2 dbxClient, File file, Context context) { this.dbxClient = dbxClient; this.file = file; this.context = context; } @Override protected Object doInBackground(Object[] params) { try { // Upload to Dropbox InputStream inputStream = new FileInputStream(file); dbxClient.files().uploadBuilder("/" + file.getName()) //Path in the user's Dropbox to save the file. .withMode(WriteMode.OVERWRITE) //always overwrite existing file .uploadAndFinish(inputStream); Log.d("Upload Status", "Success"); } catch (DbxException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Object o) { super.onPostExecute(o); Toast.makeText(context, "Image uploaded successfully", Toast.LENGTH_SHORT).show(); } } 

Примечание . Android Studio сообщит вам о конфликте импорта, вам нужно импортировать File java.io.File для переменной File .

Чтобы выполнить загрузку, сначала нужно загрузить что-то, например, изображение. Вы можете получить изображение, запустив другое действие для получения результата.

Добавьте следующий метод в MainActivity :

 private void upload() { if (ACCESS_TOKEN == null)return; //Select image to upload Intent intent = new Intent(); intent.setType("image/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); startActivityForResult(Intent.createChooser(intent, "Upload to Dropbox"), IMAGE_REQUEST_CODE); } 

При вызове startActivityForResult() необходимо реализовать метод onActivityResult() для обработки результатов в MainActivity . Когда будет получен действительный URI изображения, UploadTask файл и выполните UploadTask .

Добавьте следующий метод в MainActivity :

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK || data == null) return; // Check which request we're responding to if (requestCode == IMAGE_REQUEST_CODE) { // Make sure the request was successful if (resultCode == RESULT_OK) { //Image URI received File file = new File(URI_to_Path.getPath(getApplication(), data.getData())); if (file != null) { //Initialize UploadTask new UploadTask(DropboxClient.getClient(ACCESS_TOKEN), file, getApplicationContext()).execute(); } } } } 

В onActivityResult() есть параметр URI_to_Path.getPath(getApplication(), data.getData()) . Java-класс URI_to_Path является вспомогательным классом для преобразования файловых URI в абсолютные пути. Это основано на этом ответе StackOverflow .

Это большой класс, который не имеет прямого отношения к этому руководству, поэтому создайте URI_to_Path.java и добавьте сюда код. Обязательно измените имя пакета на ваше.

Наконец прибывает метод onCreate() . Добавьте следующий код в MainActivity , заменив все, что уже существует:

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); if (!tokenExists()) { //No token //Back to LoginActivity Intent intent = new Intent(MainActivity.this, LoginActivity.class); startActivity(intent); } ACCESS_TOKEN = retrieveAccessToken(); getUserAccount(); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { upload(); } }); } 

Каждый раз, когда приложение открывается, вам нужно проверить, есть ли токен доступа. Если токен существует, SharedPreferences его из SharedPreferences . Добавьте эти методы в MainActivity чтобы помочь с этим:

 private boolean tokenExists() { SharedPreferences prefs = getSharedPreferences("com.example.valdio.dropboxintegration", Context.MODE_PRIVATE); String accessToken = prefs.getString("access-token", null); return accessToken != null; } private String retrieveAccessToken() { //check if ACCESS_TOKEN is stored on previous app launches SharedPreferences prefs = getSharedPreferences("com.example.valdio.dropboxintegration", Context.MODE_PRIVATE); String accessToken = prefs.getString("access-token", null); if (accessToken == null) { Log.d("AccessToken Status", "No token found"); return null; } else { //accessToken already exists Log.d("AccessToken Status", "Token exists"); return accessToken; } } 

Вывод

Это все, что касается основ интеграции Dropbox с v2 API в Android. Если вы хотите глубже взглянуть на эту тему, прочитайте документацию Dropbox для Java, исходный код SDK и примеры .