blob: d35e686ad635a9eb93a737bfd5b838406082693d [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.preferences.download;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.TextViewCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.widget.TintedImageView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Custom adapter that populates the list of which directories the user can choose as their default
* download location.
*/
public class DownloadDirectoryAdapter extends ArrayAdapter<Object> {
public static int NO_SELECTED_ITEM_ID = -1;
/**
* Denotes a given option for directory selection; includes name, location, and space.
*/
public static class DirectoryOption {
private final String mName;
private final File mLocation;
private final long mAvailableSpace;
DirectoryOption(String directoryName, File directoryLocation, long availableSpace) {
mName = directoryName;
mLocation = directoryLocation;
mAvailableSpace = availableSpace;
}
/**
* @return File location associated with this directory option.
*/
public File getLocation() {
return mLocation;
}
/**
* @return Name associated with this directory option.
*/
public String getName() {
return mName;
}
/**
* @return Amount of available space in this directory option.
*/
long getAvailableSpace() {
return mAvailableSpace;
}
}
private Context mContext;
private LayoutInflater mLayoutInflater;
private List<DirectoryOption> mCanonicalOptions = new ArrayList<>();
private List<DirectoryOption> mAdditionalOptions = new ArrayList<>();
public DownloadDirectoryAdapter(@NonNull Context context) {
super(context, android.R.layout.simple_spinner_item);
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
refreshData();
}
@Override
public int getCount() {
return mCanonicalOptions.size() + mAdditionalOptions.size();
}
@Nullable
@Override
public Object getItem(int position) {
if (position < mCanonicalOptions.size()) {
return mCanonicalOptions.get(position);
} else {
return mAdditionalOptions.get(position - mCanonicalOptions.size());
}
}
@Override
public long getItemId(int position) {
return position;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = convertView;
if (view == null) {
view = mLayoutInflater.inflate(R.layout.download_location_spinner_item, null);
}
view.setTag(position);
DirectoryOption directoryOption = (DirectoryOption) getItem(position);
if (directoryOption == null) return view;
TextView titleText = (TextView) view.findViewById(R.id.text);
titleText.setText(directoryOption.getName());
return view;
}
@Override
public View getDropDownView(
int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View view = convertView;
if (view == null) {
view = mLayoutInflater.inflate(R.layout.download_location_spinner_dropdown_item, null);
}
view.setTag(position);
DirectoryOption directoryOption = (DirectoryOption) getItem(position);
if (directoryOption == null) return view;
TextView titleText = (TextView) view.findViewById(R.id.title);
titleText.setText(directoryOption.getName());
TextView summaryText = (TextView) view.findViewById(R.id.description);
if (isEnabled(position)) {
TextViewCompat.setTextAppearance(titleText, R.style.BlackTitle1);
TextViewCompat.setTextAppearance(summaryText, R.style.BlackBody);
summaryText.setText(DownloadUtils.getStringForAvailableBytes(
mContext, directoryOption.getAvailableSpace()));
} else {
TextViewCompat.setTextAppearance(titleText, R.style.BlackDisabledText1);
TextViewCompat.setTextAppearance(summaryText, R.style.BlackDisabledText3);
summaryText.setText(mContext.getText(R.string.download_location_not_enough_space));
}
TintedImageView imageView = (TintedImageView) view.findViewById(R.id.icon_view);
imageView.setVisibility(View.GONE);
return view;
}
@Override
public boolean isEnabled(int position) {
DirectoryOption directoryOption = (DirectoryOption) getItem(position);
return directoryOption != null && directoryOption.getAvailableSpace() != 0;
}
/**
* @return ID of the directory option that matches the default download location or
* NO_SELECTED_ITEM_ID if no item matches the default path.
*/
public int getSelectedItemId() {
String defaultLocation = PrefServiceBridge.getInstance().getDownloadDefaultDirectory();
for (int i = 0; i < getCount(); i++) {
DirectoryOption option = (DirectoryOption) getItem(i);
if (option == null) continue;
if (defaultLocation.equals(option.getLocation().getAbsolutePath())) return i;
}
return NO_SELECTED_ITEM_ID;
}
/**
* In the case that there is no selected item ID/the selected item ID is invalid (ie. there is
* not enough space), select either the default or the next valid item ID. Set the default to be
* this item and return the ID.
*
* @return ID of the first valid, selectable item and the new default location.
*/
public int getFirstSelectableItemId() {
for (int i = 0; i < getCount(); i++) {
DirectoryOption option = (DirectoryOption) getItem(i);
if (option == null) continue;
if (option.getAvailableSpace() > 0) {
PrefServiceBridge.getInstance().setDownloadAndSaveFileDefaultDirectory(
option.getLocation().getAbsolutePath());
return i;
}
}
// TODO(jming): Update behavior with UX suggestions.
throw new AssertionError("No selected item ID.");
}
private void refreshData() {
setCanonicalDirectoryOptions();
setAdditionalDirectoryOptions();
}
private void setCanonicalDirectoryOptions() {
mCanonicalOptions.clear();
File directoryLocation =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
mCanonicalOptions.add(new DirectoryOption(mContext.getString(R.string.menu_downloads),
directoryLocation, directoryLocation.getUsableSpace()));
}
private void setAdditionalDirectoryOptions() {
mAdditionalOptions.clear();
// TODO(jming): Is there any way to do this for API < 19?
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
File[] externalDirs = mContext.getExternalFilesDirs(Environment.DIRECTORY_DOWNLOADS);
// If there are no more additional directories, it is only the primary storage available.
if (externalDirs.length <= 1) return;
int numOtherAdditionalDirectories = 0;
for (File dir : externalDirs) {
if (dir == null) continue;
// Skip the directory that is in primary storage.
if (dir.getAbsolutePath().contains(
Environment.getExternalStorageDirectory().getAbsolutePath())) {
continue;
}
// Add index (ie. SD Card 2) if there is more than one secondary storage option.
String directoryName = (numOtherAdditionalDirectories > 0)
? mContext.getString(
org.chromium.chrome.R.string.downloads_location_sd_card_number,
numOtherAdditionalDirectories + 1)
: mContext.getString(org.chromium.chrome.R.string.downloads_location_sd_card);
mAdditionalOptions.add(new DirectoryOption(directoryName, dir, dir.getUsableSpace()));
numOtherAdditionalDirectories++;
}
}
}