blob: 98e58c0db365ae59020bd69f0c2b7a1e5fc95324 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.providers.media.tools.photopicker;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.google.android.material.snackbar.Snackbar;
public class PhotoPickerToolActivity extends Activity {
private static final String TAG = "PhotoPickerToolActivity";
private static final String EXTRA_PICK_IMAGES_MAX = "android.provider.extra.PICK_IMAGES_MAX";
private static final String ACTION_PICK_IMAGES = "android.provider.action.PICK_IMAGES";
private static final String EXTRA_PICK_IMAGES_LAUNCH_TAB =
"android.provider.extra.PICK_IMAGES_LAUNCH_TAB";
private static final int PICK_IMAGES_MAX_LIMIT = 100;
private static final int REQUEST_CODE = 42;
private int mMaxCount = 10;
private boolean mIsShowImageOnly;
private boolean mIsShowVideoOnly;
private boolean mSetMimeType;
private ScrollView mScrollView;
private CheckBox mSetImageOnlyCheckBox;
private CheckBox mSetVideoOnlyCheckBox;
private CheckBox mSetMimeTypeCheckBox;
private CheckBox mSetSelectionCountCheckBox;
private CheckBox mAllowMultipleCheckBox;
private CheckBox mGetContentCheckBox;
private CheckBox mOrderedSelectionCheckBox;
private CheckBox mPickerLaunchTabCheckBox;
private EditText mMaxCountText;
private EditText mMimeTypeText;
private RadioButton mAlbumsRadioButton;
private RadioButton mPhotosRadioButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAllowMultipleCheckBox = findViewById(R.id.cbx_allow_multiple);
mGetContentCheckBox = findViewById(R.id.cbx_get_content);
mSetImageOnlyCheckBox = findViewById(R.id.cbx_set_image_only);
mSetMimeTypeCheckBox = findViewById(R.id.cbx_set_mime_type);
mSetSelectionCountCheckBox = findViewById(R.id.cbx_set_selection_count);
mSetVideoOnlyCheckBox = findViewById(R.id.cbx_set_video_only);
mOrderedSelectionCheckBox = findViewById(R.id.cbx_ordered_selection);
mMaxCountText = findViewById(R.id.edittext_max_count);
mMimeTypeText = findViewById(R.id.edittext_mime_type);
mScrollView = findViewById(R.id.scrollview);
mPickerLaunchTabCheckBox = findViewById(R.id.cbx_set_picker_launch_tab);
mAlbumsRadioButton = findViewById(R.id.rb_albums);
mPhotosRadioButton = findViewById(R.id.rb_photos);
mSetImageOnlyCheckBox.setOnCheckedChangeListener(this::onShowImageOnlyCheckedChanged);
mSetVideoOnlyCheckBox.setOnCheckedChangeListener(this::onShowVideoOnlyCheckedChanged);
mSetMimeTypeCheckBox.setOnCheckedChangeListener(this::onSetMimeTypeCheckedChanged);
mSetSelectionCountCheckBox.setOnCheckedChangeListener(
this::onSetSelectionCountCheckedChanged);
mPickerLaunchTabCheckBox.setOnCheckedChangeListener(
this::onSetPickerLaunchTabCheckedChanged);
mMaxCountText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
try {
mMaxCount = Integer.parseInt(mMaxCountText.getText().toString().trim());
} catch (NumberFormatException ex) {
// The input is not an integer type, set the mMaxCount to -1.
mMaxCount = -1;
final String wrongFormatWarning =
"The count format is wrong! Please input correct number!";
Snackbar.make(mMaxCountText, wrongFormatWarning, Snackbar.LENGTH_LONG).show();
}
}
});
final Button launchButton = findViewById(R.id.launch_button);
launchButton.setOnClickListener(this::onLaunchButtonClicked);
}
private void onShowImageOnlyCheckedChanged(View view, boolean isChecked) {
if (mIsShowImageOnly == isChecked) {
return;
}
mIsShowImageOnly = isChecked;
if (isChecked) {
mSetVideoOnlyCheckBox.setChecked(false);
mSetMimeTypeCheckBox.setChecked(false);
}
}
private void onShowVideoOnlyCheckedChanged(View view, boolean isChecked) {
if (mIsShowVideoOnly == isChecked) {
return;
}
mIsShowVideoOnly = isChecked;
if (isChecked) {
mSetImageOnlyCheckBox.setChecked(false);
mSetMimeTypeCheckBox.setChecked(false);
}
}
private void onSetMimeTypeCheckedChanged(View view, boolean isChecked) {
if (mSetMimeType == isChecked) {
return;
}
mSetMimeType = isChecked;
if (isChecked) {
mSetImageOnlyCheckBox.setChecked(false);
mSetVideoOnlyCheckBox.setChecked(false);
}
mMimeTypeText.setEnabled(isChecked);
}
private void onSetSelectionCountCheckedChanged(View view, boolean isChecked) {
mMaxCountText.setEnabled(isChecked);
}
private void onSetPickerLaunchTabCheckedChanged(View view, boolean isChecked) {
mAlbumsRadioButton.setEnabled(isChecked);
mPhotosRadioButton.setEnabled(isChecked);
}
private void onLaunchButtonClicked(View view) {
final Intent intent;
if (mGetContentCheckBox.isChecked()) {
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
} else {
intent = new Intent(ACTION_PICK_IMAGES);
// This extra is not permitted in GET_CONTENT
if (mPickerLaunchTabCheckBox.isChecked()) {
int launchTab;
if (mAlbumsRadioButton.isChecked()) {
launchTab = 0;
} else {
launchTab = 1;
}
intent.putExtra(EXTRA_PICK_IMAGES_LAUNCH_TAB, launchTab);
}
}
if (mAllowMultipleCheckBox.isChecked()) {
if (mGetContentCheckBox.isChecked()) {
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
} else {
intent.putExtra(EXTRA_PICK_IMAGES_MAX, PICK_IMAGES_MAX_LIMIT);
// ordered selection is not allowed in get content.
if (mOrderedSelectionCheckBox.isChecked()) {
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_IN_ORDER, true);
}
}
}
if (mSetImageOnlyCheckBox.isChecked()) {
intent.setType("image/*");
} else if (mSetVideoOnlyCheckBox.isChecked()) {
intent.setType("video/*");
} else if (mSetMimeTypeCheckBox.isChecked()) {
final String mimeType = mMimeTypeText.getText().toString().trim();
intent.setType(mimeType);
}
if (mSetSelectionCountCheckBox.isChecked()) {
intent.putExtra(EXTRA_PICK_IMAGES_MAX, mMaxCount);
}
try {
startActivityForResult(intent, REQUEST_CODE);
} catch (ActivityNotFoundException ex){
final String errorMessage =
"No Activity found to handle Intent with type \"" + intent.getType() + "\"";
logErrorAndShowToast(errorMessage);
}
}
private void logErrorAndShowToast(String errorMessage) {
Log.e(TAG, errorMessage);
Snackbar.make(mScrollView, errorMessage, Snackbar.LENGTH_LONG).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_CANCELED) {
Log.e(TAG, "The result code is canceled");
return;
};
if (requestCode != REQUEST_CODE) {
logErrorAndShowToast("The request code is not as we expected");
return;
}
if (data == null) {
logErrorAndShowToast("The result intent is null");
return;
}
final Uri uri = data.getData();
if (uri == null && data.getClipData() == null) {
logErrorAndShowToast("The uri and clipData of result intent is null");
return;
}
final LinearLayout itemContainer = findViewById(R.id.item_container);
final int itemSize = (int) (300 * getResources().getDisplayMetrics().density);
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(itemSize, itemSize);
params.gravity = Gravity.CENTER;
itemContainer.removeAllViews();
if (uri != null) {
itemContainer.addView(generateText(uri.toString()));
itemContainer.addView(generateItems(uri, params));
} else {
final ClipData clipData = data.getClipData();
final int count = clipData.getItemCount();
for (int i = 0; i < count; i++) {
Uri item = (Uri) clipData.getItemAt(i).getUri();
itemContainer.addView(generateText("" + i + ". " + item.toString()));
itemContainer.addView(generateItems(item, params));
}
// scroll to first item
mScrollView.smoothScrollTo(0, 0);
}
}
private TextView generateText(String text) {
final TextView textView = new TextView(this);
textView.setTextAppearance(R.style.HeaderTitle);
textView.setText(text);
return textView;
}
private ImageView generateImageView(Uri uri, LinearLayout.LayoutParams params) {
final ImageView image = new ImageView(this);
image.setLayoutParams(params);
image.setScaleType(ImageView.ScaleType.FIT_CENTER);
Glide.with(this)
.load(uri)
.thumbnail()
.into(image);
return image;
}
private VideoView generateVideoView(Uri uri, LinearLayout.LayoutParams params) {
final VideoView video = new VideoView(this);
video.setLayoutParams(params);
video.setVideoURI(uri);
video.setOnPreparedListener(mp -> {
mp.setLooping(true);
mp.seekTo(0);
mp.start();
});
return video;
}
private View generateItems(Uri uri, LinearLayout.LayoutParams params) {
String mimeType = null;
// TODO: after getType issue is fixed, change to use getType
try (Cursor cursor = getContentResolver().query(uri,
new String[]{MediaStore.Files.FileColumns.MIME_TYPE}, null, null, null, null)) {
cursor.moveToFirst();
mimeType = cursor.getString(0);
}
if (isVideoMimeType(mimeType)) {
return generateVideoView(uri, params);
} else {
return generateImageView(uri, params);
}
}
private static boolean isVideoMimeType(@Nullable String mimeType) {
if (mimeType == null) {
return false;
}
return startsWithIgnoreCase(mimeType, "video/");
}
/**
* Variant of {@link String#startsWith(String)} but which tests with case-insensitivity.
*/
private static boolean startsWithIgnoreCase(@Nullable String target, @Nullable String other) {
if (target == null || other == null) {
return false;
}
if (other.length() >= target.length()) {
return false;
}
return target.regionMatches(true, 0, other, 0, other.length());
}
}