Browse Source

Merge pull request #12098 from nextcloud/bugfix/choose-storage-location-ui-bug-11996

Bugfix Choose Storage Location UI bug 11996
Andy Scherzinger 1 year ago
parent
commit
96ee07f81a

+ 0 - 89
app/src/main/java/com/owncloud/android/ui/adapter/StoragePathAdapter.java

@@ -1,89 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Andy Scherzinger
- * Copyright (C) 2019 Andy Scherzinger
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.adapter;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.owncloud.android.databinding.StoragePathItemBinding;
-
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-public class StoragePathAdapter extends RecyclerView.Adapter<StoragePathAdapter.StoragePathViewHolder> {
-    private List<StoragePathItem> pathList;
-    private StoragePathAdapterListener storagePathAdapterListener;
-
-    public StoragePathAdapter(List<StoragePathItem> pathList, StoragePathAdapterListener storagePathAdapterListener) {
-        this.pathList = pathList;
-        this.storagePathAdapterListener = storagePathAdapterListener;
-    }
-
-    @NonNull
-    @Override
-    public StoragePathViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-        return new StoragePathAdapter.StoragePathViewHolder(
-            StoragePathItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
-        );
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull StoragePathViewHolder holder, int position) {
-        if (pathList != null && pathList.size() > position) {
-            StoragePathItem storagePathItem = pathList.get(position);
-
-            holder.binding.icon.setImageResource(storagePathItem.getIcon());
-            holder.binding.name.setText(storagePathItem.getName());
-        }
-    }
-
-    @Override
-    public int getItemCount() {
-        return pathList.size();
-    }
-
-    public interface StoragePathAdapterListener {
-        /**
-         * sets the chosen path.
-         *
-         * @param path chosen path
-         */
-        void chosenPath(String path);
-    }
-
-    class StoragePathViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
-        StoragePathItemBinding binding;
-
-        public StoragePathViewHolder(StoragePathItemBinding binding) {
-            super(binding.getRoot());
-            this.binding = binding;
-            this.binding.getRoot().setOnClickListener(this);
-        }
-
-        @Override
-        public void onClick(View view) {
-            storagePathAdapterListener.chosenPath(pathList.get(getAdapterPosition()).getPath());
-        }
-    }
-}

+ 80 - 0
app/src/main/java/com/owncloud/android/ui/adapter/StoragePathAdapter.kt

@@ -0,0 +1,80 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Andy Scherzinger
+ * Copyright (C) 2019 Andy Scherzinger
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.adapter
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.owncloud.android.databinding.StoragePathItemBinding
+import com.owncloud.android.ui.adapter.StoragePathAdapter.StoragePathViewHolder
+import com.owncloud.android.utils.theme.ViewThemeUtils
+
+class StoragePathAdapter(
+    private val pathList: List<StoragePathItem>?,
+    private val storagePathAdapterListener: StoragePathAdapterListener,
+    private val viewThemeUtils: ViewThemeUtils
+) : RecyclerView.Adapter<StoragePathViewHolder>() {
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StoragePathViewHolder {
+        return StoragePathViewHolder(
+            StoragePathItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        )
+    }
+
+    override fun onBindViewHolder(holder: StoragePathViewHolder, position: Int) {
+        if (pathList != null && pathList.size > position) {
+            val storagePathItem = pathList[position]
+            holder.binding.btnStoragePath.setIconResource(storagePathItem.icon)
+            holder.binding.btnStoragePath.text = storagePathItem.name
+            viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(holder.binding.btnStoragePath)
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return pathList?.size ?: 0
+    }
+
+    interface StoragePathAdapterListener {
+        /**
+         * sets the chosen path.
+         *
+         * @param path chosen path
+         */
+        fun chosenPath(path: String)
+    }
+
+    inner class StoragePathViewHolder(var binding: StoragePathItemBinding) :
+        RecyclerView.ViewHolder(
+            binding.root
+        ),
+        View.OnClickListener {
+        init {
+            binding.root.setOnClickListener(this)
+        }
+
+        override fun onClick(view: View) {
+            val path = pathList?.get(absoluteAdapterPosition)?.path
+            path?.let {
+                storagePathAdapterListener.chosenPath(it)
+            }
+        }
+    }
+}

+ 0 - 169
app/src/main/java/com/owncloud/android/ui/dialog/LocalStoragePathPickerDialogFragment.java

@@ -1,169 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Andy Scherzinger
- * Copyright (C) 2019 Andy Scherzinger
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
-package com.owncloud.android.ui.dialog;
-
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Environment;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.nextcloud.client.di.Injectable;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.StoragePathDialogBinding;
-import com.owncloud.android.ui.adapter.StoragePathAdapter;
-import com.owncloud.android.ui.adapter.StoragePathItem;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-import androidx.recyclerview.widget.LinearLayoutManager;
-
-/**
- * Picker dialog for choosing a (storage) path.
- */
-public class LocalStoragePathPickerDialogFragment extends DialogFragment
-    implements DialogInterface.OnClickListener, StoragePathAdapter.StoragePathAdapterListener, Injectable {
-
-    public static final String LOCAL_STORAGE_PATH_PICKER_FRAGMENT = "LOCAL_STORAGE_PATH_PICKER_FRAGMENT";
-
-    private static Set<String> internalStoragePaths = new HashSet<>();
-
-    @Inject ViewThemeUtils viewThemeUtils;
-
-    static {
-        internalStoragePaths.add("/storage/emulated/legacy");
-        internalStoragePaths.add("/storage/emulated/0");
-        internalStoragePaths.add("/mnt/sdcard");
-    }
-
-    private StoragePathDialogBinding binding;
-
-    public static LocalStoragePathPickerDialogFragment newInstance() {
-        return new LocalStoragePathPickerDialogFragment();
-    }
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        AlertDialog alertDialog = (AlertDialog) getDialog();
-
-        if (alertDialog != null) {
-            viewThemeUtils.platform.colorTextButtons(alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE));
-        }
-    }
-
-    @Override
-    public void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        if (!(getActivity() instanceof StoragePathAdapter.StoragePathAdapterListener)) {
-            throw new IllegalArgumentException("Calling activity must implement " +
-                                                   "StoragePathAdapter.StoragePathAdapterListener");
-        }
-
-        // Inflate the layout for the dialog
-        LayoutInflater inflater = requireActivity().getLayoutInflater();
-        binding = StoragePathDialogBinding.inflate(inflater, null, false);
-        View view = binding.getRoot();
-
-        StoragePathAdapter adapter = new StoragePathAdapter(getPathList(), this);
-
-        binding.storagePathRecyclerView.setAdapter(adapter);
-        binding.storagePathRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
-
-        // Build the dialog
-        MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(binding.getRoot().getContext());
-        builder.setView(view)
-            .setNegativeButton(R.string.common_cancel, this)
-            .setTitle(R.string.storage_choose_location);
-
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.getRoot().getContext(), builder);
-
-        return builder.create();
-    }
-
-    @Override
-    public void onDestroyView() {
-        binding = null;
-        super.onDestroyView();
-    }
-
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        if (which == AlertDialog.BUTTON_NEGATIVE) {
-            dismissAllowingStateLoss();
-        }
-    }
-
-    private List<StoragePathItem> getPathList() {
-        List<StoragePathItem> storagePathItems = new ArrayList<>();
-
-        for (FileStorageUtils.StandardDirectory standardDirectory : FileStorageUtils.StandardDirectory.getStandardDirectories()) {
-            addIfExists(storagePathItems, standardDirectory.getIcon(), getString(standardDirectory.getDisplayName()),
-                        Environment.getExternalStoragePublicDirectory(standardDirectory.getName()).getAbsolutePath());
-        }
-
-        String sdCard = getString(R.string.storage_internal_storage);
-        for (String dir : FileStorageUtils.getStorageDirectories(requireActivity())) {
-            if (internalStoragePaths.contains(dir)) {
-                addIfExists(storagePathItems, R.drawable.ic_sd_grey600, sdCard, dir);
-            } else {
-                addIfExists(storagePathItems, R.drawable.ic_sd_grey600, new File(dir).getName(), dir);
-            }
-        }
-
-        return storagePathItems;
-    }
-
-    private void addIfExists(List<StoragePathItem> storagePathItems, int icon, String name, String path) {
-        File file = new File(path);
-        if (file.exists() && file.canRead()) {
-            storagePathItems.add(new StoragePathItem(icon, name, path));
-        }
-    }
-
-    @Override
-    public void chosenPath(String path) {
-        if (getActivity() != null) {
-            ((StoragePathAdapter.StoragePathAdapterListener) getActivity()).chosenPath(path);
-        }
-        dismissAllowingStateLoss();
-    }
-}

+ 148 - 0
app/src/main/java/com/owncloud/android/ui/dialog/LocalStoragePathPickerDialogFragment.kt

@@ -0,0 +1,148 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Andy Scherzinger
+ * Copyright (C) 2019 Andy Scherzinger
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.dialog
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.os.Environment
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.button.MaterialButton
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.nextcloud.client.di.Injectable
+import com.owncloud.android.R
+import com.owncloud.android.databinding.StoragePathDialogBinding
+import com.owncloud.android.ui.adapter.StoragePathAdapter
+import com.owncloud.android.ui.adapter.StoragePathAdapter.StoragePathAdapterListener
+import com.owncloud.android.ui.adapter.StoragePathItem
+import com.owncloud.android.utils.FileStorageUtils
+import com.owncloud.android.utils.FileStorageUtils.StandardDirectory
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import java.io.File
+import javax.inject.Inject
+
+class LocalStoragePathPickerDialogFragment :
+    DialogFragment(),
+    DialogInterface.OnClickListener,
+    StoragePathAdapterListener,
+    Injectable {
+
+    @Inject
+    lateinit var viewThemeUtils: ViewThemeUtils
+
+    private lateinit var binding: StoragePathDialogBinding
+
+    override fun onStart() {
+        super.onStart()
+
+        val alertDialog = dialog as AlertDialog?
+
+        val positiveButton = alertDialog?.getButton(AlertDialog.BUTTON_POSITIVE) as MaterialButton?
+        positiveButton?.let {
+            viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton)
+        }
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        require(activity is StoragePathAdapterListener) {
+            "Calling activity must implement " +
+                "StoragePathAdapter.StoragePathAdapterListener"
+        }
+
+        // Inflate the layout for the dialog
+        val inflater = requireActivity().layoutInflater
+        binding = StoragePathDialogBinding.inflate(inflater, null, false)
+
+        val adapter = StoragePathAdapter(pathList, this, viewThemeUtils)
+        binding.storagePathRecyclerView.adapter = adapter
+        binding.storagePathRecyclerView.layoutManager = LinearLayoutManager(requireActivity())
+
+        // Build the dialog
+        val builder = MaterialAlertDialogBuilder(requireContext())
+        builder
+            .setView(binding.root)
+            .setPositiveButton(R.string.common_cancel, this)
+            .setTitle(R.string.storage_choose_location)
+
+        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder)
+
+        return builder.create()
+    }
+
+    override fun onClick(dialog: DialogInterface, which: Int) {
+        if (which == AlertDialog.BUTTON_POSITIVE) {
+            dismissAllowingStateLoss()
+        }
+    }
+
+    private val pathList: List<StoragePathItem>
+        get() {
+            val storagePathItems: MutableList<StoragePathItem> = ArrayList()
+            for (standardDirectory in StandardDirectory.getStandardDirectories()) {
+                addIfExists(
+                    storagePathItems,
+                    standardDirectory.icon,
+                    getString(standardDirectory.displayName),
+                    Environment.getExternalStoragePublicDirectory(standardDirectory.name).absolutePath
+                )
+            }
+            val sdCard = getString(R.string.storage_internal_storage)
+            for (dir in FileStorageUtils.getStorageDirectories(requireActivity())) {
+                if (internalStoragePaths.contains(dir)) {
+                    addIfExists(storagePathItems, R.drawable.ic_sd_grey600, sdCard, dir)
+                } else {
+                    addIfExists(storagePathItems, R.drawable.ic_sd_grey600, File(dir).name, dir)
+                }
+            }
+            return storagePathItems
+        }
+
+    private fun addIfExists(storagePathItems: MutableList<StoragePathItem>, icon: Int, name: String, path: String) {
+        val file = File(path)
+        if (file.exists() && file.canRead()) {
+            storagePathItems.add(StoragePathItem(icon, name, path))
+        }
+    }
+
+    override fun chosenPath(path: String) {
+        if (activity != null) {
+            (activity as StoragePathAdapterListener?)!!.chosenPath(path)
+        }
+        dismissAllowingStateLoss()
+    }
+
+    companion object {
+        const val LOCAL_STORAGE_PATH_PICKER_FRAGMENT = "LOCAL_STORAGE_PATH_PICKER_FRAGMENT"
+        private val internalStoragePaths: MutableSet<String> = HashSet()
+
+        init {
+            internalStoragePaths.add("/storage/emulated/legacy")
+            internalStoragePaths.add("/storage/emulated/0")
+            internalStoragePaths.add("/mnt/sdcard")
+        }
+
+        @JvmStatic
+        fun newInstance(): LocalStoragePathPickerDialogFragment {
+            return LocalStoragePathPickerDialogFragment()
+        }
+    }
+}

+ 10 - 34
app/src/main/res/layout/storage_path_item.xml

@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   Nextcloud Android client application
 
   Copyright (C) 2019 Andy Scherzinger
@@ -17,39 +16,16 @@
   You should have received a copy of the GNU Affero General Public
   License along with this program. If not, see <http://www.gnu.org/licenses/>.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.google.android.material.button.MaterialButton 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:id="@+id/btn_storage_path"
+    style="@style/Widget.Material3.Button.TextButton.Icon"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
-    android:paddingTop="@dimen/standard_half_padding"
+    android:gravity="center|start"
     android:paddingBottom="@dimen/standard_half_padding"
-    android:weightSum="1"
-    tools:ignore="UseCompoundDrawables">
-
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:background="@color/bg_default"
-        android:contentDescription="@string/user_icon"
-        android:paddingStart="@dimen/standard_padding"
-        android:paddingLeft="@dimen/standard_padding"
-        android:paddingEnd="@dimen/standard_padding"
-        android:paddingRight="@dimen/standard_padding"
-        android:src="@drawable/ic_user" />
-
-    <TextView
-        android:id="@+id/name"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="@dimen/standard_margin"
-        android:layout_weight="1"
-        android:ellipsize="end"
-        android:gravity="center_vertical"
-        android:singleLine="true"
-        android:textColor="@color/text_color"
-        android:textSize="@dimen/two_line_primary_text_size"
-        tools:text="DCIM" />
-</LinearLayout>
+    android:text="@string/menu_item_sort_by_name_z_a"
+    app:icon="@drawable/ic_user"
+    app:iconPadding="@dimen/standard_padding"
+    tools:text="DCIM" />