/* * This is the source code of Telegram for Android v. 5.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013-2018. */ package org.telegram.ui; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Canvas; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.text.InputFilter; import android.text.TextPaint; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; import android.view.animation.OvershootInterpolator; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.jetbrains.annotations.NotNull; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BackDrawable; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.SharedDocumentCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.EditTextEmoji; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SizeNotifierFrameLayout; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicBoolean; import cn.hutool.core.collection.CollectionUtil; import kotlin.Unit; import tw.nekomimi.nekogram.NekoConfig; import tw.nekomimi.nekogram.transtale.TranslateDb; import tw.nekomimi.nekogram.transtale.Translator; import tw.nekomimi.nekogram.transtale.TranslatorKt; import tw.nekomimi.nekogram.utils.AlertUtil; public class DocumentSelectActivity extends BaseFragment { public interface DocumentSelectActivityDelegate { void didSelectFiles(DocumentSelectActivity activity, ArrayList files, String caption, boolean notify, int scheduleDate); void didSelectPhotos(ArrayList photos, boolean notify, int scheduleDate); void startDocumentSelectActivity(); default void startMusicSelectActivity(BaseFragment parentFragment) { } } private RecyclerListView listView; private ListAdapter listAdapter; private SearchAdapter searchAdapter; private ActionBarMenuItem searchItem; private ActionBarMenuItem sortItem; private LinearLayoutManager layoutManager; private ChatActivity chatActivity; private LinearLayout emptyView; private ImageView emptyImageView; private TextView emptyTitleTextView; private TextView emptySubtitleTextView; private FrameLayout frameLayout2; private FrameLayout writeButtonContainer; private View selectedCountView; private View shadow; private EditTextEmoji commentTextView; private ImageView writeButton; private SizeNotifierFrameLayout sizeNotifierFrameLayout; private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private RectF rect = new RectF(); private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private AnimatorSet animatorSet; private boolean sendPressed; private ActionBarPopupWindow sendPopupWindow; private ActionBarPopupWindow.ActionBarPopupWindowLayout sendPopupLayout; private ActionBarMenuSubItem[] itemCells; private boolean hasFiles; private File currentDir; private ArrayList items = new ArrayList<>(); private boolean receiverRegistered = false; private ArrayList history = new ArrayList<>(); private static final long sizeLimit = 1024 * 1024 * 1536; private DocumentSelectActivityDelegate delegate; private HashMap selectedFiles = new HashMap<>(); private boolean scrolling; private ArrayList recentItems = new ArrayList<>(); private int maxSelectedFiles = -1; private boolean canSelectOnlyImageFiles; private boolean allowMusic; public void setAllowPhoto(boolean allowPhoto) { this.allowPhoto = allowPhoto; } private boolean allowPhoto = true; private boolean searching; private boolean searchWas; private boolean sortByName; private final static int search_button = 0; private final static int sort_button = 1; private class ListItem { public int icon; public String title; public String subtitle = ""; public String ext = ""; public String thumb; public File file; } private class HistoryEntry { int scrollItem, scrollOffset; File dir; String title; } private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context arg0, Intent intent) { Runnable r = () -> { try { if (currentDir == null) { listRoots(); } else { listFiles(currentDir); } updateSearchButton(); } catch (Exception e) { FileLog.e(e); } }; if (Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())) { listView.postDelayed(r, 1000); } else { r.run(); } } }; public DocumentSelectActivity(boolean music) { super(); allowMusic = music; } @Override public boolean onFragmentCreate() { sortByName = SharedConfig.sortFilesByName; loadRecentFiles(); return super.onFragmentCreate(); } @Override public void onFragmentDestroy() { if (commentTextView != null) { commentTextView.onDestroy(); } try { if (receiverRegistered) { ApplicationLoader.applicationContext.unregisterReceiver(receiver); } } catch (Exception e) { FileLog.e(e); } super.onFragmentDestroy(); } @Override public View createView(Context context) { searching = false; searchWas = false; if (!receiverRegistered) { receiverRegistered = true; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL); filter.addAction(Intent.ACTION_MEDIA_CHECKING); filter.addAction(Intent.ACTION_MEDIA_EJECT); filter.addAction(Intent.ACTION_MEDIA_MOUNTED); filter.addAction(Intent.ACTION_MEDIA_NOFS); filter.addAction(Intent.ACTION_MEDIA_REMOVED); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); filter.addDataScheme("file"); ApplicationLoader.applicationContext.registerReceiver(receiver, filter); } actionBar.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); actionBar.setTitleColor(Theme.getColor(Theme.key_dialogTextBlack)); actionBar.setItemsColor(Theme.getColor(Theme.key_dialogTextBlack), false); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_dialogButtonSelector), false); actionBar.setBackButtonDrawable(new BackDrawable(false)); actionBar.setAllowOverlayTitle(true); actionBar.setTitle(LocaleController.getString("SelectFile", R.string.SelectFile)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { if (canClosePicker()) { finishFragment(); } } else if (id == sort_button) { SharedConfig.toggleSortFilesByName(); sortByName = SharedConfig.sortFilesByName; sortRecentItems(); sortFileItems(); listAdapter.notifyDataSetChanged(); sortItem.setIcon(sortByName ? R.drawable.contacts_sort_time : R.drawable.contacts_sort_name); } } }); final ActionBarMenu menu = actionBar.createMenu(); searchItem = menu.addItem(search_button, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { @Override public void onSearchExpand() { searching = true; sortItem.setVisibility(View.GONE); } @Override public void onSearchCollapse() { searching = false; searchWas = false; sortItem.setVisibility(View.VISIBLE); if (listView.getAdapter() != listAdapter) { listView.setAdapter(listAdapter); } updateEmptyView(); searchAdapter.search(null); } @Override public void onTextChanged(EditText editText) { searchAdapter.search(editText.getText().toString()); } }); searchItem.setSearchFieldHint(LocaleController.getString("Search", R.string.Search)); searchItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); EditTextBoldCursor editText = searchItem.getSearchField(); editText.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); editText.setCursorColor(Theme.getColor(Theme.key_dialogTextBlack)); editText.setHintTextColor(Theme.getColor(Theme.key_chat_messagePanelHint)); sortItem = menu.addItem(sort_button, sortByName ? R.drawable.contacts_sort_time : R.drawable.contacts_sort_name); sortItem.setContentDescription(LocaleController.getString("AccDescrContactSorting", R.string.AccDescrContactSorting)); selectedFiles.clear(); sizeNotifierFrameLayout = new SizeNotifierFrameLayout(context, SharedConfig.smoothKeyboard) { private int lastNotifyWidth; private boolean ignoreLayout; private int lastItemSize; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); int kbHeight = getKeyboardHeight(); int keyboardSize = SharedConfig.smoothKeyboard ? 0 : kbHeight; if (keyboardSize <= AndroidUtilities.dp(20)) { if (!AndroidUtilities.isInMultiwindow && commentTextView != null && frameLayout2.getParent() == this) { heightSize -= commentTextView.getEmojiPadding(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY); } } if (kbHeight > AndroidUtilities.dp(20) && commentTextView != null) { ignoreLayout = true; commentTextView.hideEmojiView(); ignoreLayout = false; } if (SharedConfig.smoothKeyboard && commentTextView != null && commentTextView.isPopupShowing()) { fragmentView.setTranslationY(getCurrentPanTranslationY()); listView.setTranslationY(0); emptyView.setTranslationY(0); } int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child == null || child.getVisibility() == GONE) { continue; } if (commentTextView != null && commentTextView.isPopupView(child)) { if (AndroidUtilities.isInMultiwindow || AndroidUtilities.isTablet()) { if (AndroidUtilities.isTablet()) { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Math.min(AndroidUtilities.dp(AndroidUtilities.isTablet() ? 200 : 320), heightSize - AndroidUtilities.statusBarHeight + getPaddingTop()), MeasureSpec.EXACTLY)); } else { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize - AndroidUtilities.statusBarHeight + getPaddingTop(), MeasureSpec.EXACTLY)); } } else { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(child.getLayoutParams().height, MeasureSpec.EXACTLY)); } } else { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); } } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (lastNotifyWidth != r - l) { lastNotifyWidth = r - l; if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } } final int count = getChildCount(); int keyboardSize = SharedConfig.smoothKeyboard ? 0 : getKeyboardHeight(); int paddingBottom = commentTextView != null && frameLayout2.getParent() == this && keyboardSize <= AndroidUtilities.dp(20) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet() ? commentTextView.getEmojiPadding() : 0; setBottomClip(paddingBottom); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() == GONE) { continue; } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int width = child.getMeasuredWidth(); final int height = child.getMeasuredHeight(); int childLeft; int childTop; int gravity = lp.gravity; if (gravity == -1) { gravity = Gravity.TOP | Gravity.LEFT; } final int absoluteGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = (r - l - width) / 2 + lp.leftMargin - lp.rightMargin; break; case Gravity.RIGHT: childLeft = (r - l) - width - lp.rightMargin - getPaddingRight(); break; case Gravity.LEFT: default: childLeft = lp.leftMargin + getPaddingLeft(); } switch (verticalGravity) { case Gravity.TOP: childTop = lp.topMargin + getPaddingTop(); break; case Gravity.CENTER_VERTICAL: childTop = ((b - paddingBottom) - t - height) / 2 + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = ((b - paddingBottom) - t) - height - lp.bottomMargin; break; default: childTop = lp.topMargin; } if (commentTextView != null && commentTextView.isPopupView(child)) { if (AndroidUtilities.isTablet()) { childTop = getMeasuredHeight() - child.getMeasuredHeight(); } else { childTop = getMeasuredHeight() + keyboardSize - child.getMeasuredHeight(); } } child.layout(childLeft, childTop, childLeft + width, childTop + height); } notifyHeightChanged(); } @Override public void requestLayout() { if (ignoreLayout) { return; } super.requestLayout(); } }; sizeNotifierFrameLayout.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); fragmentView = sizeNotifierFrameLayout; emptyView = new LinearLayout(context); emptyView.setOrientation(LinearLayout.VERTICAL); emptyView.setGravity(Gravity.CENTER); emptyView.setVisibility(View.GONE); sizeNotifierFrameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); emptyView.setOnTouchListener((v, event) -> true); emptyImageView = new ImageView(context); emptyImageView.setImageResource(R.drawable.files_empty); emptyImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogEmptyImage), PorterDuff.Mode.SRC_IN)); emptyView.addView(emptyImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); emptyTitleTextView = new TextView(context); emptyTitleTextView.setTextColor(Theme.getColor(Theme.key_dialogEmptyText)); emptyTitleTextView.setGravity(Gravity.CENTER); emptyTitleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); emptyTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17); emptyTitleTextView.setPadding(AndroidUtilities.dp(40), 0, AndroidUtilities.dp(40), 0); emptyView.addView(emptyTitleTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 11, 0, 0)); emptySubtitleTextView = new TextView(context); emptySubtitleTextView.setTextColor(Theme.getColor(Theme.key_dialogEmptyText)); emptySubtitleTextView.setGravity(Gravity.CENTER); emptySubtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); emptyView.addView(emptySubtitleTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 6, 0, 0)); listView = new RecyclerListView(context); listView.setVerticalScrollBarEnabled(false); listView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); listView.setEmptyView(emptyView); listView.setClipToPadding(false); listView.setAdapter(listAdapter = new ListAdapter(context)); listView.setPadding(0, 0, 0, AndroidUtilities.dp(48)); sizeNotifierFrameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); searchAdapter = new SearchAdapter(context); listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { scrolling = newState != RecyclerView.SCROLL_STATE_IDLE; if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); } } }); listView.setOnItemClickListener((view, position) -> { ListItem item; if (listView.getAdapter() == listAdapter) { item = listAdapter.getItem(position); } else { item = searchAdapter.getItem(position); } if (item == null) { return; } File file = item.file; if (file == null) { if (item.icon == R.drawable.files_gallery) { HashMap selectedPhotos = new HashMap<>(); ArrayList selectedPhotosOrder = new ArrayList<>(); PhotoPickerActivity fragment = new PhotoPickerActivity(0, MediaController.allMediaAlbumEntry, selectedPhotos, selectedPhotosOrder, 0, chatActivity != null, chatActivity); fragment.setDocumentsPicker(true); fragment.setDelegate(new PhotoPickerActivity.PhotoPickerActivityDelegate() { @Override public void selectedPhotosChanged() { } @Override public void actionButtonPressed(boolean canceled, boolean notify, int scheduleDate) { removeSelfFromStack(); if (!canceled) { sendSelectedPhotos(selectedPhotos, selectedPhotosOrder, notify, scheduleDate); } } @Override public void onCaptionChanged(CharSequence text) { } @Override public void onOpenInPressed() { removeSelfFromStack(); delegate.startDocumentSelectActivity(); } }); fragment.setMaxSelectedPhotos(maxSelectedFiles, false); presentFragment(fragment); } else if (item.icon == R.drawable.files_music) { if (delegate != null) { delegate.startMusicSelectActivity(this); } } else { HistoryEntry he = history.remove(history.size() - 1); actionBar.setTitle(he.title); if (he.dir != null) { listFiles(he.dir); } else { listRoots(); } updateSearchButton(); layoutManager.scrollToPositionWithOffset(he.scrollItem, he.scrollOffset); } } else if (file.isDirectory()) { HistoryEntry he = new HistoryEntry(); he.scrollItem = layoutManager.findLastVisibleItemPosition(); View topView = layoutManager.findViewByPosition(he.scrollItem); if (topView != null) { he.scrollOffset = topView.getTop(); } he.dir = currentDir; he.title = actionBar.getTitle(); history.add(he); if (!listFiles(file)) { history.remove(he); return; } actionBar.setTitle(item.title); } else { onItemClick(view, item); } }); listView.setOnItemLongClickListener((view, position) -> { ListItem item; if (listView.getAdapter() == listAdapter) { item = listAdapter.getItem(position); } else { item = searchAdapter.getItem(position); } return onItemClick(view, item); }); shadow = new View(context); shadow.setBackgroundResource(R.drawable.header_shadow_reverse); shadow.setTranslationY(AndroidUtilities.dp(48)); sizeNotifierFrameLayout.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 3, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 48)); frameLayout2 = new FrameLayout(context); frameLayout2.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); frameLayout2.setVisibility(View.INVISIBLE); frameLayout2.setTranslationY(AndroidUtilities.dp(48)); sizeNotifierFrameLayout.addView(frameLayout2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); frameLayout2.setOnTouchListener((v, event) -> true); if (commentTextView != null) { commentTextView.onDestroy(); } commentTextView = new EditTextEmoji(context, sizeNotifierFrameLayout, null, EditTextEmoji.STYLE_DIALOG); InputFilter[] inputFilters = new InputFilter[1]; inputFilters[0] = new InputFilter.LengthFilter(MessagesController.getInstance(UserConfig.selectedAccount).maxCaptionLength); commentTextView.setFilters(inputFilters); commentTextView.setHint(LocaleController.getString("AddCaption", R.string.AddCaption)); commentTextView.onResume(); editText = commentTextView.getEditText(); editText.setMaxLines(1); editText.setSingleLine(true); frameLayout2.addView(commentTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 84, 0)); if (chatActivity == null) { commentTextView.setVisibility(View.GONE); } writeButtonContainer = new FrameLayout(context); writeButtonContainer.setVisibility(View.INVISIBLE); writeButtonContainer.setScaleX(0.2f); writeButtonContainer.setScaleY(0.2f); writeButtonContainer.setAlpha(0.0f); writeButtonContainer.setContentDescription(LocaleController.getString("Send", R.string.Send)); sizeNotifierFrameLayout.addView(writeButtonContainer, LayoutHelper.createFrame(60, 60, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, 12, 10)); writeButton = new ImageView(context); Drawable writeButtonDrawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(56), Theme.getColor(Theme.key_dialogFloatingButton), Theme.getColor(Build.VERSION.SDK_INT >= 21 ? Theme.key_dialogFloatingButtonPressed : Theme.key_dialogFloatingButton)); if (Build.VERSION.SDK_INT < 21) { Drawable shadowDrawable = context.getResources().getDrawable(R.drawable.floating_shadow_profile).mutate(); shadowDrawable.setColorFilter(new PorterDuffColorFilter(0xff000000, PorterDuff.Mode.SRC_IN)); CombinedDrawable combinedDrawable = new CombinedDrawable(shadowDrawable, writeButtonDrawable, 0, 0); combinedDrawable.setIconSize(AndroidUtilities.dp(56), AndroidUtilities.dp(56)); writeButtonDrawable = combinedDrawable; } writeButton.setBackgroundDrawable(writeButtonDrawable); if (allowPhoto) { writeButton.setImageResource(R.drawable.baseline_send_24); } else { writeButton.setImageResource(R.drawable.baseline_check_24); } writeButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogFloatingIcon), PorterDuff.Mode.SRC_IN)); writeButton.setScaleType(ImageView.ScaleType.CENTER); if (Build.VERSION.SDK_INT >= 21) { writeButton.setOutlineProvider(new ViewOutlineProvider() { @SuppressLint("NewApi") @Override public void getOutline(View view, Outline outline) { outline.setOval(0, 0, AndroidUtilities.dp(56), AndroidUtilities.dp(56)); } }); } writeButtonContainer.addView(writeButton, LayoutHelper.createFrame(Build.VERSION.SDK_INT >= 21 ? 56 : 60, Build.VERSION.SDK_INT >= 21 ? 56 : 60, Gravity.LEFT | Gravity.TOP, Build.VERSION.SDK_INT >= 21 ? 2 : 0, 0, 0, 0)); writeButton.setOnClickListener(v -> { if (chatActivity != null && chatActivity.isInScheduleMode()) { AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), chatActivity.getDialogId(), this::sendSelectedFiles); } else { sendSelectedFiles(true, 0); } }); writeButton.setOnLongClickListener(view -> { if (chatActivity == null) { return false; } TLRPC.Chat chat = chatActivity.getCurrentChat(); TLRPC.User user = chatActivity.getCurrentUser(); if (chatActivity.getCurrentEncryptedChat() != null) { return false; } if (sendPopupLayout == null) { sendPopupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getParentActivity()); sendPopupLayout.setAnimationEnabled(false); sendPopupLayout.setOnTouchListener(new View.OnTouchListener() { private android.graphics.Rect popupRect = new android.graphics.Rect(); @Override public boolean onTouch(View v, MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { v.getHitRect(popupRect); if (!popupRect.contains((int) event.getX(), (int) event.getY())) { sendPopupWindow.dismiss(); } } } return false; } }); sendPopupLayout.setDispatchKeyEventListener(keyEvent -> { if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } }); sendPopupLayout.setShowedFromBotton(false); itemCells = new ActionBarMenuSubItem[2]; for (int a = 0; a < 3; a++) { if (a == 2 && UserObject.isUserSelf(user)) { continue; } int num = a; itemCells[a] = new ActionBarMenuSubItem(getParentActivity()); if (num == 0) { itemCells[a].setTextAndIcon(LocaleController.getString("Translate", R.string.Translate), R.drawable.ic_translate); } else if (num == 1) { if (UserObject.isUserSelf(user)) { itemCells[a].setTextAndIcon(LocaleController.getString("SetReminder", R.string.SetReminder), R.drawable.baseline_date_range_24); } else { itemCells[a].setTextAndIcon(LocaleController.getString("ScheduleMessage", R.string.ScheduleMessage), R.drawable.baseline_date_range_24); } } else if (num == 2) { itemCells[a].setTextAndIcon(LocaleController.getString("SendWithoutSound", R.string.SendWithoutSound), R.drawable.input_notify_off); } itemCells[a].setMinimumWidth(AndroidUtilities.dp(196)); sendPopupLayout.addView(itemCells[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, 0, 48 * a, 0, 0)); int chatId; if (chat != null) { chatId = chat.id; } else if (user != null) { chatId = user.id; } else { chatId = -1; } itemCells[a].setOnClickListener(v -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } if (num == 0) { translateComment(TranslateDb.getChatLanguage(chatId, TranslatorKt.getCode2Locale(NekoConfig.translateInputLang))); } else if (num == 1) { AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), chatActivity.getDialogId(), this::sendSelectedFiles); } else if (num == 2) { sendSelectedFiles(true, 0); } }); itemCells[a].setOnLongClickListener(v -> { if (num == 0) { Translator.showTargetLangSelect(itemCells[num], true, (locale) -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } translateComment(locale); TranslateDb.saveChatLanguage(chatId, locale); return Unit.INSTANCE; }); return true; } return false; }); } sendPopupWindow = new ActionBarPopupWindow(sendPopupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); sendPopupWindow.setAnimationEnabled(false); sendPopupWindow.setAnimationStyle(R.style.PopupContextAnimation2); sendPopupWindow.setOutsideTouchable(true); sendPopupWindow.setClippingEnabled(true); sendPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); sendPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); sendPopupWindow.getContentView().setFocusableInTouchMode(true); } sendPopupLayout.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); sendPopupWindow.setFocusable(true); int[] location = new int[2]; view.getLocationInWindow(location); sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + AndroidUtilities.dp(8), location[1] - sendPopupLayout.getMeasuredHeight() - AndroidUtilities.dp(2)); sendPopupWindow.dimBehind(); view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); return false; }); textPaint.setTextSize(AndroidUtilities.dp(12)); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); selectedCountView = new View(context) { @Override protected void onDraw(Canvas canvas) { String text = String.format("%d", Math.max(1, selectedFiles.size())); int textSize = (int) Math.ceil(textPaint.measureText(text)); int size = Math.max(AndroidUtilities.dp(16) + textSize, AndroidUtilities.dp(24)); int cx = getMeasuredWidth() / 2; int cy = getMeasuredHeight() / 2; textPaint.setColor(Theme.getColor(Theme.key_dialogRoundCheckBoxCheck)); paint.setColor(Theme.getColor(Theme.key_dialogBackground)); rect.set(cx - size / 2, 0, cx + size / 2, getMeasuredHeight()); canvas.drawRoundRect(rect, AndroidUtilities.dp(12), AndroidUtilities.dp(12), paint); paint.setColor(Theme.getColor(Theme.key_dialogRoundCheckBox)); rect.set(cx - size / 2 + AndroidUtilities.dp(2), AndroidUtilities.dp(2), cx + size / 2 - AndroidUtilities.dp(2), getMeasuredHeight() - AndroidUtilities.dp(2)); canvas.drawRoundRect(rect, AndroidUtilities.dp(10), AndroidUtilities.dp(10), paint); canvas.drawText(text, cx - textSize / 2, AndroidUtilities.dp(16.2f), textPaint); } }; selectedCountView.setAlpha(0.0f); selectedCountView.setScaleX(0.2f); selectedCountView.setScaleY(0.2f); sizeNotifierFrameLayout.addView(selectedCountView, LayoutHelper.createFrame(42, 24, Gravity.RIGHT | Gravity.BOTTOM, 0, 0, -2, 9)); listRoots(); updateSearchButton(); updateEmptyView(); updateCountButton(0); return fragmentView; } private void translateComment(Locale target) { TranslateDb db = TranslateDb.forLocale(target); String origin = commentTextView.getText().toString(); if (db.contains(origin)) { String translated = db.query(origin); commentTextView.getEditText().setText(translated); return; } Translator.translate(target, origin, new Translator.Companion.TranslateCallBack() { final AtomicBoolean cancel = new AtomicBoolean(); AlertDialog status = AlertUtil.showProgress(getParentActivity()); { status.setOnCancelListener((__) -> { cancel.set(true); }); status.show(); } @Override public void onSuccess(@NotNull String translation) { status.dismiss(); commentTextView.getEditText().setText(translation); } @Override public void onFailed(boolean unsupported, @NotNull String message) { status.dismiss(); AlertUtil.showTransFailedDialog(getParentActivity(), unsupported, message, () -> { status = AlertUtil.showProgress(getParentActivity()); status.show(); Translator.translate(origin, this); }); } }); } @Override protected void onPanTranslationUpdate(int y) { if (listView == null) { return; } if (commentTextView.isPopupShowing()) { fragmentView.setTranslationY(y); listView.setTranslationY(0); emptyView.setTranslationY(0); } else { listView.setTranslationY(y); emptyView.setTranslationY(y); } } private boolean onItemClick(View view, ListItem item) { if (item == null || item.file == null || item.file.isDirectory()) { return false; } String path = item.file.getAbsolutePath(); boolean add; if (selectedFiles.containsKey(path)) { selectedFiles.remove(path); add = false; } else { if (!item.file.canRead()) { showErrorBox(LocaleController.getString("AccessError", R.string.AccessError)); return false; } if (canSelectOnlyImageFiles && item.thumb == null) { showErrorBox(LocaleController.formatString("PassportUploadNotImage", R.string.PassportUploadNotImage)); return false; } if (sizeLimit != 0) { if (item.file.length() > sizeLimit) { showErrorBox(LocaleController.formatString("FileUploadLimit", R.string.FileUploadLimit, AndroidUtilities.formatFileSize(sizeLimit))); return false; } } if (maxSelectedFiles >= 0 && selectedFiles.size() >= maxSelectedFiles) { showErrorBox(LocaleController.formatString("PassportUploadMaxReached", R.string.PassportUploadMaxReached, LocaleController.formatPluralString("Files", maxSelectedFiles))); return false; } if (item.file.length() == 0) { return false; } selectedFiles.put(path, item); add = true; } scrolling = false; if (view instanceof SharedDocumentCell) { ((SharedDocumentCell) view).setChecked(add, true); } updateCountButton(add ? 1 : 2); return true; } public void setChatActivity(ChatActivity parentFragment) { chatActivity = parentFragment; } public void setMaxSelectedFiles(int value) { maxSelectedFiles = value; } public void setCanSelectOnlyImageFiles(boolean value) { canSelectOnlyImageFiles = true; } private boolean showCommentTextView(boolean show, boolean animated) { if (commentTextView == null) { return false; } if (show == (frameLayout2.getTag() != null)) { return false; } if (animatorSet != null) { animatorSet.cancel(); } frameLayout2.setTag(show ? 1 : null); if (commentTextView.getEditText().isFocused()) { AndroidUtilities.hideKeyboard(commentTextView.getEditText()); } commentTextView.hidePopup(true); if (show) { frameLayout2.setVisibility(View.VISIBLE); writeButtonContainer.setVisibility(View.VISIBLE); } if (animated) { animatorSet = new AnimatorSet(); ArrayList animators = new ArrayList<>(); animators.add(ObjectAnimator.ofFloat(writeButtonContainer, View.SCALE_X, show ? 1.0f : 0.2f)); animators.add(ObjectAnimator.ofFloat(writeButtonContainer, View.SCALE_Y, show ? 1.0f : 0.2f)); animators.add(ObjectAnimator.ofFloat(writeButtonContainer, View.ALPHA, show ? 1.0f : 0.0f)); animators.add(ObjectAnimator.ofFloat(selectedCountView, View.SCALE_X, show ? 1.0f : 0.2f)); animators.add(ObjectAnimator.ofFloat(selectedCountView, View.SCALE_Y, show ? 1.0f : 0.2f)); animators.add(ObjectAnimator.ofFloat(selectedCountView, View.ALPHA, show ? 1.0f : 0.0f)); animators.add(ObjectAnimator.ofFloat(frameLayout2, View.TRANSLATION_Y, show ? 0 : AndroidUtilities.dp(48))); animators.add(ObjectAnimator.ofFloat(shadow, View.TRANSLATION_Y, show ? 0 : AndroidUtilities.dp(48))); animatorSet.playTogether(animators); animatorSet.setInterpolator(new DecelerateInterpolator()); animatorSet.setDuration(180); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (animation.equals(animatorSet)) { if (!show) { frameLayout2.setVisibility(View.INVISIBLE); writeButtonContainer.setVisibility(View.INVISIBLE); } animatorSet = null; } } @Override public void onAnimationCancel(Animator animation) { if (animation.equals(animatorSet)) { animatorSet = null; } } }); animatorSet.start(); } else { writeButtonContainer.setScaleX(show ? 1.0f : 0.2f); writeButtonContainer.setScaleY(show ? 1.0f : 0.2f); writeButtonContainer.setAlpha(show ? 1.0f : 0.0f); selectedCountView.setScaleX(show ? 1.0f : 0.2f); selectedCountView.setScaleY(show ? 1.0f : 0.2f); selectedCountView.setAlpha(show ? 1.0f : 0.0f); frameLayout2.setTranslationY(show ? 0 : AndroidUtilities.dp(48)); shadow.setTranslationY(show ? 0 : AndroidUtilities.dp(48)); if (!show) { frameLayout2.setVisibility(View.INVISIBLE); writeButtonContainer.setVisibility(View.INVISIBLE); } } return true; } public void updateCountButton(int animated) { int count = selectedFiles.size(); if (count == 0) { selectedCountView.setPivotX(0); selectedCountView.setPivotY(0); showCommentTextView(false, animated != 0); } else { selectedCountView.invalidate(); if (!showCommentTextView(true, animated != 0) && animated != 0) { selectedCountView.setPivotX(AndroidUtilities.dp(21)); selectedCountView.setPivotY(AndroidUtilities.dp(12)); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(selectedCountView, View.SCALE_X, animated == 1 ? 1.1f : 0.9f, 1.0f), ObjectAnimator.ofFloat(selectedCountView, View.SCALE_Y, animated == 1 ? 1.1f : 0.9f, 1.0f)); animatorSet.setInterpolator(new OvershootInterpolator()); animatorSet.setDuration(180); animatorSet.start(); } else { selectedCountView.setPivotX(0); selectedCountView.setPivotY(0); } } } private void sendSelectedPhotos(HashMap photos, ArrayList order, boolean notify, int scheduleDate) { if (photos.isEmpty() || delegate == null || sendPressed) { return; } sendPressed = true; ArrayList media = new ArrayList<>(); for (int a = 0; a < order.size(); a++) { Object object = photos.get(order.get(a)); SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); media.add(info); if (object instanceof MediaController.PhotoEntry) { MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) object; if (photoEntry.isVideo) { info.path = photoEntry.path; info.videoEditedInfo = photoEntry.editedInfo; } else if (photoEntry.imagePath != null) { info.path = photoEntry.imagePath; } else if (photoEntry.path != null) { info.path = photoEntry.path; } info.isVideo = photoEntry.isVideo; info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null; info.entities = photoEntry.entities; info.masks = !photoEntry.stickers.isEmpty() ? new ArrayList<>(photoEntry.stickers) : null; info.ttl = photoEntry.ttl; } } delegate.didSelectPhotos(media, notify, scheduleDate); } private void sendSelectedFiles(boolean notify, int scheduleDate) { if (selectedFiles.size() == 0 || delegate == null || sendPressed) { return; } sendPressed = true; ArrayList files = new ArrayList<>(selectedFiles.keySet()); delegate.didSelectFiles(DocumentSelectActivity.this, files, commentTextView.getText().toString(), notify, scheduleDate); finishFragment(); } public void loadRecentFiles() { try { File[] files = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).listFiles(); for (int a = 0; a < files.length; a++) { File file = files[a]; if (file.isDirectory()) { continue; } ListItem item = new ListItem(); item.title = file.getName(); item.file = file; String fname = file.getName(); String[] sp = fname.split("\\."); item.ext = sp.length > 1 ? sp[sp.length - 1] : "?"; item.subtitle = AndroidUtilities.formatFileSize(file.length()); fname = fname.toLowerCase(); if (fname.endsWith(".jpg") || fname.endsWith(".png") || fname.endsWith(".gif") || fname.endsWith(".jpeg")) { item.thumb = file.getAbsolutePath(); } recentItems.add(item); } sortRecentItems(); } catch (Exception e) { FileLog.e(e); } } private void sortRecentItems() { Collections.sort(recentItems, (o1, o2) -> { if (sortByName) { String lm = o1.file.getName(); String rm = o2.file.getName(); return lm.compareToIgnoreCase(rm); } else { long lm = o1.file.lastModified(); long rm = o2.file.lastModified(); if (lm == rm) { return 0; } else if (lm > rm) { return -1; } else { return 1; } } }); } private void sortFileItems() { if (currentDir == null) { return; } Collections.sort(items, (lhs, rhs) -> { if (lhs.file == null) { return -1; } else if (rhs.file == null) { return 1; } else if (lhs.file == null && rhs.file == null) { return 0; } boolean isDir1 = lhs.file.isDirectory(); boolean isDir2 = rhs.file.isDirectory(); if (isDir1 != isDir2) { return isDir1 ? -1 : 1; } else if (isDir1 && isDir2 || sortByName) { return lhs.file.getName().compareToIgnoreCase(rhs.file.getName()); } else { long lm = lhs.file.lastModified(); long rm = rhs.file.lastModified(); if (lm == rm) { return 0; } else if (lm > rm) { return -1; } else { return 1; } } }); } @Override public void onResume() { super.onResume(); if (commentTextView != null) { commentTextView.onResume(); } if (listAdapter != null) { listAdapter.notifyDataSetChanged(); } if (searchAdapter != null) { searchAdapter.notifyDataSetChanged(); } } private void updateEmptyView() { if (searching) { emptyTitleTextView.setText(LocaleController.getString("NoFilesFound", R.string.NoFilesFound)); emptyView.setGravity(Gravity.CENTER_HORIZONTAL); emptyView.setPadding(0, AndroidUtilities.dp(60), 0, 0); emptySubtitleTextView.setPadding(AndroidUtilities.dp(40), 0, AndroidUtilities.dp(40), 0); } else { emptyTitleTextView.setText(LocaleController.getString("NoFilesFound", R.string.NoFilesFound)); emptySubtitleTextView.setText(LocaleController.getString("NoFilesInfo", R.string.NoFilesInfo)); emptyView.setGravity(Gravity.CENTER); emptyView.setPadding(0, 0, 0, 0); emptySubtitleTextView.setPadding(AndroidUtilities.dp(40), 0, AndroidUtilities.dp(40), AndroidUtilities.dp(128)); } listView.setEmptyView(emptyView); } private void updateSearchButton() { if (searchItem == null) { return; } searchItem.setVisibility(hasFiles ? View.VISIBLE : View.GONE); if (history.isEmpty()) { searchItem.setSearchFieldHint(LocaleController.getString("SearchRecentFiles", R.string.SearchRecentFiles)); } else { searchItem.setSearchFieldHint(LocaleController.getString("Search", R.string.Search)); } } private boolean canClosePicker() { if (history.size() > 0) { HistoryEntry he = history.remove(history.size() - 1); actionBar.setTitle(he.title); if (he.dir != null) { listFiles(he.dir); } else { listRoots(); } updateSearchButton(); layoutManager.scrollToPositionWithOffset(he.scrollItem, he.scrollOffset); return false; } return true; } @Override public boolean onBackPressed() { if (commentTextView != null && commentTextView.isPopupShowing()) { commentTextView.hidePopup(true); return false; } if (!canClosePicker()) { return false; } return super.onBackPressed(); } public void setDelegate(DocumentSelectActivityDelegate delegate) { this.delegate = delegate; } private boolean listFiles(File dir) { hasFiles = false; if (!dir.canRead()) { if (dir.getAbsolutePath().startsWith(Environment.getExternalStorageDirectory().toString()) || dir.getAbsolutePath().startsWith("/sdcard") || dir.getAbsolutePath().startsWith("/mnt/sdcard")) { if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) && !Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { currentDir = dir; items.clear(); String state = Environment.getExternalStorageState(); AndroidUtilities.clearDrawableAnimation(listView); scrolling = true; listAdapter.notifyDataSetChanged(); return true; } } showErrorBox(LocaleController.getString("AccessError", R.string.AccessError)); return false; } File[] files; try { files = dir.listFiles(); } catch (Exception e) { showErrorBox(e.getLocalizedMessage()); return false; } if (files == null) { showErrorBox(LocaleController.getString("UnknownError", R.string.UnknownError)); return false; } currentDir = dir; items.clear(); for (int a = 0; a < files.length; a++) { File file = files[a]; if (file.getName().indexOf('.') == 0) { continue; } ListItem item = new ListItem(); item.title = file.getName(); item.file = file; if (file.isDirectory()) { item.icon = R.drawable.files_folder; item.subtitle = LocaleController.getString("Folder", R.string.Folder); } else { hasFiles = true; String fname = file.getName(); String[] sp = fname.split("\\."); item.ext = sp.length > 1 ? sp[sp.length - 1] : "?"; item.subtitle = AndroidUtilities.formatFileSize(file.length()); fname = fname.toLowerCase(); if (fname.endsWith(".jpg") || fname.endsWith(".png") || fname.endsWith(".gif") || fname.endsWith(".jpeg")) { item.thumb = file.getAbsolutePath(); } } items.add(item); } ListItem item = new ListItem(); item.title = ".."; if (history.size() > 0) { HistoryEntry entry = history.get(history.size() - 1); if (entry.dir == null) { item.subtitle = LocaleController.getString("Folder", R.string.Folder); } else { item.subtitle = entry.dir.toString(); } } else { item.subtitle = LocaleController.getString("Folder", R.string.Folder); } item.icon = R.drawable.files_folder; item.file = null; items.add(0, item); sortFileItems(); updateSearchButton(); AndroidUtilities.clearDrawableAnimation(listView); scrolling = true; listAdapter.notifyDataSetChanged(); return true; } private void showErrorBox(String error) { if (getParentActivity() == null) { return; } new AlertDialog.Builder(getParentActivity()).setTitle(LocaleController.getString("NekoX", R.string.NekoX)).setMessage(error).setPositiveButton(LocaleController.getString("OK", R.string.OK), null).show(); } @SuppressLint("NewApi") private void listRoots() { currentDir = null; hasFiles = false; items.clear(); HashSet paths = new HashSet<>(); String defaultPath = Environment.getExternalStorageDirectory().getPath(); boolean isDefaultPathRemovable = Environment.isExternalStorageRemovable(); String defaultPathState = Environment.getExternalStorageState(); if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { ListItem ext = new ListItem(); if (Environment.isExternalStorageRemovable()) { ext.title = LocaleController.getString("SdCard", R.string.SdCard); ext.icon = R.drawable.files_internal; ext.subtitle = LocaleController.getString("ExternalFolderInfo", R.string.ExternalFolderInfo); } else { ext.title = LocaleController.getString("InternalStorage", R.string.InternalStorage); ext.icon = R.drawable.files_storage; ext.subtitle = LocaleController.getString("InternalFolderInfo", R.string.InternalFolderInfo); } ext.file = Environment.getExternalStorageDirectory(); items.add(ext); paths.add(defaultPath); } BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader("/proc/mounts")); String line; while ((line = bufferedReader.readLine()) != null) { if (line.contains("vfat") || line.contains("/mnt")) { if (BuildVars.LOGS_ENABLED) { FileLog.d(line); } StringTokenizer tokens = new StringTokenizer(line, " "); String unused = tokens.nextToken(); String path = tokens.nextToken(); if (paths.contains(path)) { continue; } if (line.contains("/dev/block/vold")) { if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) { if (!new File(path).isDirectory()) { int index = path.lastIndexOf('/'); if (index != -1) { String newPath = "/storage/" + path.substring(index + 1); if (new File(newPath).isDirectory()) { path = newPath; } } } paths.add(path); try { ListItem item = new ListItem(); if (path.toLowerCase().contains("sd")) { item.title = LocaleController.getString("SdCard", R.string.SdCard); } else { item.title = LocaleController.getString("ExternalStorage", R.string.ExternalStorage); } item.subtitle = LocaleController.getString("ExternalFolderInfo", R.string.ExternalFolderInfo); item.icon = R.drawable.files_internal; item.file = new File(path); items.add(item); } catch (Exception e) { FileLog.e(e); } } } } } } catch (Exception e) { FileLog.e(e); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (Exception e) { FileLog.e(e); } } } /* fs.title = "/"; fs.subtitle = LocaleController.getString("SystemRoot", R.string.SystemRoot); fs.icon = R.drawable.files_folder; fs.file = new File("/"); items.add(fs); */ ListItem fs = new ListItem(); try { File telegramPath = ApplicationLoader.applicationContext.getExternalFilesDir(null); if (telegramPath.exists()) { fs = new ListItem(); fs.title = "Telegram"; fs.subtitle = LocaleController.getString("AppFolderInfo", R.string.AppFolderInfo); fs.icon = R.drawable.files_folder; fs.file = telegramPath; items.add(fs); } } catch (Exception e) { FileLog.e(e); } if (allowPhoto) { fs = new ListItem(); fs.title = LocaleController.getString("Gallery", R.string.Gallery); fs.subtitle = LocaleController.getString("GalleryInfo", R.string.GalleryInfo); fs.icon = R.drawable.files_gallery; fs.file = null; items.add(fs); } if (allowMusic) { fs = new ListItem(); fs.title = LocaleController.getString("AttachMusic", R.string.AttachMusic); fs.subtitle = LocaleController.getString("MusicInfo", R.string.MusicInfo); fs.icon = R.drawable.files_music; fs.file = null; items.add(fs); } if (!recentItems.isEmpty()) { hasFiles = true; } AndroidUtilities.clearDrawableAnimation(listView); scrolling = true; listAdapter.notifyDataSetChanged(); } private String getRootSubtitle(String path) { try { StatFs stat = new StatFs(path); long total = (long) stat.getBlockCount() * (long) stat.getBlockSize(); long free = (long) stat.getAvailableBlocks() * (long) stat.getBlockSize(); if (total == 0) { return ""; } return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total)); } catch (Exception e) { FileLog.e(e); } return path; } private class ListAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; public ListAdapter(Context context) { mContext = context; } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { return holder.getItemViewType() != 0; } @Override public int getItemCount() { int count = items.size(); if (history.isEmpty() && !recentItems.isEmpty()) { count += recentItems.size() + 1; } return count; } public ListItem getItem(int position) { int itemsSize = items.size(); if (position < itemsSize) { return items.get(position); } else if (history.isEmpty() && !recentItems.isEmpty() && position != itemsSize && position != itemsSize + 1) { position -= items.size() + 2; if (position < recentItems.size()) { return recentItems.get(position); } } return null; } @Override public int getItemViewType(int position) { int itemsSize = items.size(); if (position == itemsSize) { return 2; } else if (position == itemsSize + 1) { return 0; } return 1; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case 0: HeaderCell headerCell = new HeaderCell(mContext); headerCell.setText(LocaleController.getString("RecentFiles", R.string.RecentFiles)); view = headerCell; break; case 1: view = new SharedDocumentCell(mContext, true); break; case 2: default: view = new ShadowSectionCell(mContext); Drawable drawable = Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); combinedDrawable.setFullsize(true); view.setBackgroundDrawable(combinedDrawable); break; } return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() == 1) { ListItem item = getItem(position); SharedDocumentCell documentCell = (SharedDocumentCell) holder.itemView; if (item.icon != 0) { documentCell.setTextAndValueAndTypeAndThumb(item.title, item.subtitle, null, null, item.icon, position != items.size() - 1); } else { String type = item.ext.toUpperCase().substring(0, Math.min(item.ext.length(), 4)); documentCell.setTextAndValueAndTypeAndThumb(item.title, item.subtitle, type, item.thumb, 0, false); } if (item.file != null) { documentCell.setChecked(selectedFiles.containsKey(item.file.toString()), !scrolling); } else { documentCell.setChecked(false, !scrolling); } } } } public class SearchAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; private ArrayList searchResult = new ArrayList<>(); private Runnable searchRunnable; private int reqId = 0; private int lastReqId; public SearchAdapter(Context context) { mContext = context; } public void search(final String query) { if (searchRunnable != null) { AndroidUtilities.cancelRunOnUIThread(searchRunnable); searchRunnable = null; } if (TextUtils.isEmpty(query)) { if (!searchResult.isEmpty()) { searchResult.clear(); } if (listView.getAdapter() != listAdapter) { listView.setAdapter(listAdapter); } notifyDataSetChanged(); } else { AndroidUtilities.runOnUIThread(searchRunnable = () -> { final ArrayList copy = new ArrayList<>(items); if (history.isEmpty()) { copy.addAll(0, recentItems); } Utilities.searchQueue.postRunnable(() -> { String search1 = query.trim().toLowerCase(); if (search1.length() == 0) { updateSearchResults(new ArrayList<>(), query); return; } String search2 = LocaleController.getInstance().getTranslitString(search1); if (search1.equals(search2) || search2.length() == 0) { search2 = null; } String[] search = new String[1 + (search2 != null ? 1 : 0)]; search[0] = search1; if (search2 != null) { search[1] = search2; } ArrayList resultArray = new ArrayList<>(); for (int a = 0; a < copy.size(); a++) { ListItem entry = copy.get(a); if (entry.file == null || entry.file.isDirectory()) { continue; } for (int b = 0; b < search.length; b++) { String q = search[b]; boolean ok = false; if (entry.title != null) { ok = entry.title.toLowerCase().contains(q); } if (ok) { resultArray.add(entry); break; } } } updateSearchResults(resultArray, query); }); }, 300); } } private void updateSearchResults(final ArrayList result, String query) { AndroidUtilities.runOnUIThread(() -> { if (searching) { if (listView.getAdapter() != searchAdapter) { listView.setAdapter(searchAdapter); updateEmptyView(); } emptySubtitleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("NoFilesFoundInfo", R.string.NoFilesFoundInfo, query))); } searchWas = true; searchResult = result; notifyDataSetChanged(); }); } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { return true; } @Override public int getItemCount() { return searchResult.size(); } public ListItem getItem(int position) { if (position < searchResult.size()) { return searchResult.get(position); } return null; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = new SharedDocumentCell(mContext, true); return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ListItem item = getItem(position); SharedDocumentCell documentCell = (SharedDocumentCell) holder.itemView; if (item.icon != 0) { documentCell.setTextAndValueAndTypeAndThumb(item.title, item.subtitle, null, null, item.icon, false); } else { String type = item.ext.toUpperCase().substring(0, Math.min(item.ext.length(), 4)); documentCell.setTextAndValueAndTypeAndThumb(item.title, item.subtitle, type, item.thumb, 0, false); } if (item.file != null) { documentCell.setChecked(selectedFiles.containsKey(item.file.toString()), !scrolling); } else { documentCell.setChecked(false, !scrolling); } } @Override public int getItemViewType(int i) { return 0; } } @Override public ArrayList getThemeDescriptions() { return CollectionUtil.toList(new ThemeDescription(sizeNotifierFrameLayout, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_dialogBackground), new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_dialogBackground), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_dialogTextBlack), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_dialogTextBlack), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_dialogButtonSelector), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCH, null, null, null, null, Theme.key_dialogTextBlack), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCHPLACEHOLDER, null, null, null, null, Theme.key_chat_messagePanelHint), new ThemeDescription(searchItem.getSearchField(), ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_dialogTextBlack), new ThemeDescription(emptyImageView, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_dialogEmptyImage), new ThemeDescription(emptyTitleTextView, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_dialogEmptyText), new ThemeDescription(emptySubtitleTextView, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_dialogEmptyText), new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_dialogBackground), new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGray), new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider), new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{SharedDocumentCell.class}, new String[]{"nameTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{SharedDocumentCell.class}, new String[]{"dateTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText3), new ThemeDescription(listView, ThemeDescription.FLAG_CHECKBOX, new Class[]{SharedDocumentCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_checkbox), new ThemeDescription(listView, ThemeDescription.FLAG_CHECKBOXCHECK, new Class[]{SharedDocumentCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_checkboxCheck), new ThemeDescription(listView, ThemeDescription.FLAG_IMAGECOLOR, new Class[]{SharedDocumentCell.class}, new String[]{"thumbImageView"}, null, null, null, Theme.key_files_folderIcon), new ThemeDescription(listView, ThemeDescription.FLAG_IMAGECOLOR | ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{SharedDocumentCell.class}, new String[]{"thumbImageView"}, null, null, null, Theme.key_files_folderIconBackground), new ThemeDescription(listView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{SharedDocumentCell.class}, new String[]{"extTextView"}, null, null, null, Theme.key_files_iconText), new ThemeDescription(writeButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_dialogFloatingIcon), new ThemeDescription(writeButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogFloatingButton), new ThemeDescription(writeButton, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Build.VERSION.SDK_INT >= 21 ? Theme.key_dialogFloatingButtonPressed : Theme.key_dialogFloatingButton)); } }