mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
Created models, adapters and view models (#4441)
* created models, adapters and view models * Added Image Fragment * back button linked * Documentation and refractor * spaces * Butterknife annotation * DiffUtil * Added Examples * Extended Custom selector From Base Activity * made view model injectable
This commit is contained in:
parent
3463a19446
commit
dc26621185
28 changed files with 1060 additions and 41 deletions
|
|
@ -108,6 +108,9 @@
|
|||
<activity android:name=".quiz.QuizResultActivity"
|
||||
android:label="@string/result"/>
|
||||
|
||||
<activity android:name=".customselector.ui.selector.CustomSelectorActivity"
|
||||
android:label="CustomSelector"/>
|
||||
|
||||
<activity
|
||||
android:name=".category.CategoryDetailsActivity"
|
||||
android:label="@string/title_activity_featured_images"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import static android.view.View.VISIBLE;
|
|||
import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -29,13 +30,16 @@ import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
|
|||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -67,6 +71,11 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
TextView noContributionsYet;
|
||||
@BindView(R.id.fab_layout)
|
||||
LinearLayout fab_layout;
|
||||
@BindView(R.id.fab_custom_gallery)
|
||||
FloatingActionButton fabCustomGallery;
|
||||
|
||||
@Inject
|
||||
SystemThemeUtils systemThemeUtils;
|
||||
|
||||
@Inject
|
||||
ContributionController controller;
|
||||
|
|
@ -248,6 +257,12 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
});
|
||||
}
|
||||
|
||||
@OnClick(R.id.fab_custom_gallery)
|
||||
void launchCustomSelector(){
|
||||
Intent intent = new Intent(getActivity(), CustomSelectorActivity.class);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void animateFAB(final boolean isFabOpen) {
|
||||
this.isFabOpen = !isFabOpen;
|
||||
if (fabPlus.isShown()) {
|
||||
|
|
@ -255,14 +270,18 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
|
|||
fabPlus.startAnimation(rotate_backward);
|
||||
fabCamera.startAnimation(fab_close);
|
||||
fabGallery.startAnimation(fab_close);
|
||||
fabCustomGallery.startAnimation(fab_close);
|
||||
fabCamera.hide();
|
||||
fabGallery.hide();
|
||||
fabCustomGallery.hide();
|
||||
} else {
|
||||
fabPlus.startAnimation(rotate_forward);
|
||||
fabCamera.startAnimation(fab_open);
|
||||
fabGallery.startAnimation(fab_open);
|
||||
fabCustomGallery.startAnimation(fab_open);
|
||||
fabCamera.show();
|
||||
fabGallery.show();
|
||||
fabCustomGallery.show();
|
||||
}
|
||||
this.isFabOpen = !isFabOpen;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.free.nrw.commons.customselector.listeners
|
||||
|
||||
import fr.free.nrw.commons.customselector.model.Folder
|
||||
|
||||
interface FolderClickListener {
|
||||
fun onFolderClick(folder : Folder)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package fr.free.nrw.commons.customselector.listeners
|
||||
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
interface ImageLoaderListener {
|
||||
fun onImageLoaded(images: ArrayList<Image>)
|
||||
fun onFailed(throwable: Throwable)
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.free.nrw.commons.customselector.listeners
|
||||
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
interface ImageSelectListener {
|
||||
fun onSelectedImagesChanged(selectedImages: ArrayList<Image>)
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package fr.free.nrw.commons.customselector.model
|
||||
|
||||
sealed class CallbackStatus {
|
||||
/**
|
||||
IDLE : The callback is idle , doing nothing.
|
||||
*/
|
||||
object IDLE : CallbackStatus()
|
||||
|
||||
/**
|
||||
FETCHING : Fetching images.
|
||||
*/
|
||||
object FETCHING : CallbackStatus()
|
||||
|
||||
/**
|
||||
SUCCESS : Success fetching images.
|
||||
*/
|
||||
object SUCCESS : CallbackStatus()
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package fr.free.nrw.commons.customselector.model
|
||||
|
||||
data class Folder(
|
||||
/**
|
||||
bucketId : Unique directory id, eg 540528482
|
||||
*/
|
||||
var bucketId: Long,
|
||||
|
||||
/**
|
||||
name : bucket/folder name, eg Camera
|
||||
*/
|
||||
var name: String,
|
||||
|
||||
/**
|
||||
images : folder images, list of all images under this folder.
|
||||
*/
|
||||
var images: ArrayList<Image> = arrayListOf<Image>()
|
||||
|
||||
|
||||
) {
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one.
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
if (javaClass != other?.javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as Folder
|
||||
|
||||
if (bucketId != other.bucketId) {
|
||||
return false
|
||||
}
|
||||
if (name != other.name) {
|
||||
return false
|
||||
}
|
||||
if (images != other.images) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package fr.free.nrw.commons.customselector.model
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
data class Image(
|
||||
/**
|
||||
id : Unique image id, primary key of image in device, eg 104950
|
||||
*/
|
||||
var id: Long,
|
||||
|
||||
/**
|
||||
name : Name of the image with extension, eg CommonsLogo.jpeg
|
||||
*/
|
||||
var name: String,
|
||||
|
||||
/**
|
||||
uri : Uri of the image, points to image location or name, eg content://media/external/images/camera/10495 (Android 10)
|
||||
*/
|
||||
var uri: Uri,
|
||||
|
||||
/**
|
||||
path : System path of the image, eg storage/emulated/0/camera/CommonsLogo.jpeg
|
||||
*/
|
||||
var path: String,
|
||||
|
||||
/**
|
||||
bucketId : bucketId of folder, eg 540528482
|
||||
*/
|
||||
var bucketId: Long = 0,
|
||||
|
||||
/**
|
||||
bucketName : name of folder, eg Camera
|
||||
*/
|
||||
var bucketName: String = "",
|
||||
|
||||
/**
|
||||
sha1 : sha1 of original image.
|
||||
*/
|
||||
var sha1: String = ""
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
default parcelable constructor.
|
||||
*/
|
||||
constructor(parcel: Parcel):
|
||||
this(parcel.readLong(),
|
||||
parcel.readString()!!,
|
||||
parcel.readParcelable(Uri::class.java.classLoader)!!,
|
||||
parcel.readString()!!,
|
||||
parcel.readLong(),
|
||||
parcel.readString()!!,
|
||||
parcel.readString()!!
|
||||
)
|
||||
|
||||
/**
|
||||
Write to parcel method.
|
||||
*/
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeLong(id)
|
||||
parcel.writeString(name)
|
||||
parcel.writeParcelable(uri, flags)
|
||||
parcel.writeString(path)
|
||||
parcel.writeLong(bucketId)
|
||||
parcel.writeString(bucketName)
|
||||
parcel.writeString(sha1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe the kinds of special objects contained in this Parcelable
|
||||
*/
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this one.
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
if(javaClass != other?.javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as Image
|
||||
|
||||
if(id != other.id) {
|
||||
return false;
|
||||
}
|
||||
if(name != other.name) {
|
||||
return false;
|
||||
}
|
||||
if(uri != other.uri) {
|
||||
return false;
|
||||
}
|
||||
if(path != other.path) {
|
||||
return false;
|
||||
}
|
||||
if(bucketId != other.bucketId) {
|
||||
return false;
|
||||
}
|
||||
if(bucketName != other.bucketName) {
|
||||
return false;
|
||||
}
|
||||
if(sha1 != other.sha1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Parcelable companion object
|
||||
*/
|
||||
companion object CREATOR : Parcelable.Creator<Image> {
|
||||
override fun createFromParcel(parcel: Parcel): Image {
|
||||
return Image(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<Image?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package fr.free.nrw.commons.customselector.model
|
||||
|
||||
data class Result(
|
||||
/**
|
||||
* CallbackStatus : stores the result status
|
||||
*/
|
||||
val status:CallbackStatus,
|
||||
|
||||
/**
|
||||
* Images : images retrieved
|
||||
*/
|
||||
val images: ArrayList<Image>) {
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package fr.free.nrw.commons.customselector.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
|
||||
import fr.free.nrw.commons.customselector.model.Folder
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageLoader
|
||||
|
||||
class FolderAdapter(
|
||||
/**
|
||||
* Application context.
|
||||
*/
|
||||
context: Context,
|
||||
|
||||
/**
|
||||
* Folder Click listener for click events.
|
||||
*/
|
||||
private val itemClickListener: FolderClickListener
|
||||
) : RecyclerViewAdapter<FolderAdapter.FolderViewHolder?>(context) {
|
||||
|
||||
/**
|
||||
* Image Loader for loading images.
|
||||
*/
|
||||
private val imageLoader = ImageLoader()
|
||||
|
||||
/**
|
||||
* List of folders.
|
||||
*/
|
||||
private var folders: MutableList<Folder> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Create view holder, returns View holder item.
|
||||
*/
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FolderViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.item_custom_selector_folder, parent, false)
|
||||
return FolderViewHolder(itemView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind view holder, setup the item view, title, count and click listener
|
||||
*/
|
||||
override fun onBindViewHolder(holder: FolderViewHolder, position: Int) {
|
||||
val folder = folders[position]
|
||||
val count = folder.images.size
|
||||
val previewImage = folder.images[0]
|
||||
holder.name.text = folder.name
|
||||
holder.count.text= count.toString()
|
||||
holder.itemView.setOnClickListener{
|
||||
itemClickListener.onFolderClick(folder)
|
||||
}
|
||||
|
||||
//todo load image thumbnail.
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the data set.
|
||||
*/
|
||||
fun init(newFolders: List<Folder>) {
|
||||
val oldFolderList: MutableList<Folder> = folders
|
||||
val newFolderList = newFolders.toMutableList()
|
||||
val diffResult = DiffUtil.calculateDiff(
|
||||
FoldersDiffCallback(oldFolderList, newFolderList)
|
||||
)
|
||||
folders = newFolderList
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns item count.
|
||||
*/
|
||||
override fun getItemCount(): Int {
|
||||
return folders.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Folder view holder.
|
||||
*/
|
||||
class FolderViewHolder(itemView:View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
/**
|
||||
* Folder thumbnail image view.
|
||||
*/
|
||||
val image: ImageView = itemView.findViewById(R.id.folder_thumbnail)
|
||||
|
||||
/**
|
||||
* Folder/album name
|
||||
*/
|
||||
val name: TextView = itemView.findViewById(R.id.folder_name)
|
||||
|
||||
/**
|
||||
* Item count in Folder/Item
|
||||
*/
|
||||
val count: TextView = itemView.findViewById(R.id.folder_count)
|
||||
}
|
||||
|
||||
/**
|
||||
* DiffUtilCallback.
|
||||
*/
|
||||
class FoldersDiffCallback(
|
||||
var oldFolders: MutableList<Folder>,
|
||||
var newFolders: MutableList<Folder>
|
||||
) : DiffUtil.Callback() {
|
||||
/**
|
||||
* Returns the size of the old list.
|
||||
*/
|
||||
override fun getOldListSize(): Int {
|
||||
return oldFolders.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the new list.
|
||||
*/
|
||||
override fun getNewListSize(): Int {
|
||||
return newFolders.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the DiffUtil to decide whether two object represent the same Item.
|
||||
*/
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return oldFolders.get(oldItemPosition).bucketId == newFolders.get(newItemPosition).bucketId
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the DiffUtil when it wants to check whether two items have the same data.
|
||||
* DiffUtil uses this information to detect if the contents of an item has changed.
|
||||
*/
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return oldFolders.get(oldItemPosition).equals(newFolders.get(newItemPosition))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package fr.free.nrw.commons.customselector.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import fr.free.nrw.commons.R
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.Group
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
class ImageAdapter(
|
||||
/**
|
||||
* Application Context.
|
||||
*/
|
||||
context: Context,
|
||||
|
||||
/**
|
||||
* Image select listener for click events on image.
|
||||
*/
|
||||
private var imageSelectListener: ImageSelectListener ):
|
||||
|
||||
RecyclerViewAdapter<ImageAdapter.ImageViewHolder>(context) {
|
||||
|
||||
/**
|
||||
* Currently selected images.
|
||||
*/
|
||||
private var selectedImages = arrayListOf<Image>()
|
||||
|
||||
/**
|
||||
* List of all images in adapter.
|
||||
*/
|
||||
private var images: ArrayList<Image> = ArrayList()
|
||||
|
||||
/**
|
||||
* create View holder.
|
||||
*/
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.item_custom_selector_image,parent, false)
|
||||
return ImageViewHolder(itemView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind View holder, load image, selected view, click listeners.
|
||||
*/
|
||||
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
|
||||
val image=images[position]
|
||||
// todo load image thumbnail, set selected view.
|
||||
holder.itemView.setOnClickListener {
|
||||
selectOrRemoveImage(image, position)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click event on an image, update counter on images.
|
||||
*/
|
||||
private fun selectOrRemoveImage(image:Image, position:Int){
|
||||
// todo select the image if not selected and remove it if already selected
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the data set.
|
||||
*/
|
||||
fun init(newImages:List<Image>) {
|
||||
val oldImageList:ArrayList<Image> = images
|
||||
val newImageList:ArrayList<Image> = ArrayList(newImages)
|
||||
val diffResult = DiffUtil.calculateDiff(
|
||||
ImagesDiffCallback(oldImageList, newImageList)
|
||||
)
|
||||
images = newImageList
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of items in the data set held by the adapter.
|
||||
*
|
||||
* @return The total number of items in this adapter.
|
||||
*/
|
||||
override fun getItemCount(): Int {
|
||||
return images.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Image view holder.
|
||||
*/
|
||||
class ImageViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
|
||||
val image: ImageView = itemView.findViewById(R.id.image_thumbnail)
|
||||
val selectedNumber: TextView = itemView.findViewById(R.id.selected_count)
|
||||
val uploadedGroup: Group = itemView.findViewById(R.id.uploaded_group)
|
||||
val selectedGroup: Group = itemView.findViewById(R.id.selected_group)
|
||||
}
|
||||
|
||||
/**
|
||||
* DiffUtilCallback.
|
||||
*/
|
||||
class ImagesDiffCallback(
|
||||
var oldImageList: ArrayList<Image>,
|
||||
var newImageList: ArrayList<Image>
|
||||
) : DiffUtil.Callback(){
|
||||
|
||||
/**
|
||||
* Returns the size of the old list.
|
||||
*/
|
||||
override fun getOldListSize(): Int {
|
||||
return oldImageList.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the new list.
|
||||
*/
|
||||
override fun getNewListSize(): Int {
|
||||
return newImageList.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the DiffUtil to decide whether two object represent the same Item.
|
||||
*/
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return newImageList[newItemPosition].id == oldImageList[oldItemPosition].id
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the DiffUtil when it wants to check whether two items have the same data.
|
||||
* DiffUtil uses this information to detect if the contents of an item has changed.
|
||||
*/
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
|
||||
return oldImageList[oldItemPosition].equals(newImageList[newItemPosition])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package fr.free.nrw.commons.customselector.ui.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* Generic Recycler view adapter.
|
||||
*/
|
||||
abstract class RecyclerViewAdapter<T : RecyclerView.ViewHolder?>(val context: Context): RecyclerView.Adapter<T>() {
|
||||
val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
import fr.free.nrw.commons.customselector.model.Folder
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
class CustomSelectorActivity : BaseActivity(), FolderClickListener, ImageSelectListener {
|
||||
|
||||
/**
|
||||
* View model.
|
||||
*/
|
||||
private lateinit var viewModel: CustomSelectorViewModel
|
||||
|
||||
/**
|
||||
* View Model Factory.
|
||||
*/
|
||||
@Inject lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory
|
||||
|
||||
/**
|
||||
* onCreate Activity, sets theme, initialises the view model, setup view.
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_custom_selector)
|
||||
|
||||
viewModel = ViewModelProvider(this,customSelectorViewModelFactory).get(CustomSelectorViewModel::class.java)
|
||||
|
||||
setupViews()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up view, default folder view.
|
||||
*/
|
||||
private fun setupViews() {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.fragment_container, FolderFragment.newInstance())
|
||||
.commit()
|
||||
fetchData()
|
||||
setUpToolbar()
|
||||
|
||||
// todo : open image fragment depending on the last user visit.
|
||||
}
|
||||
|
||||
/**
|
||||
* Start data fetch in view model.
|
||||
*/
|
||||
private fun fetchData() {
|
||||
viewModel.fetchImages()
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the title of the toolbar.
|
||||
*/
|
||||
private fun changeTitle(title:String) {
|
||||
val titleText = findViewById<TextView>(R.id.title)
|
||||
if(titleText != null) {
|
||||
titleText.text = title
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the toolbar, back listener, done listener.
|
||||
*/
|
||||
private fun setUpToolbar() {
|
||||
val back : ImageButton = findViewById(R.id.back)
|
||||
back.setOnClickListener { onBackPressed() }
|
||||
|
||||
// todo done listener.
|
||||
}
|
||||
|
||||
/**
|
||||
* override on folder click, change the toolbar title on folder click.
|
||||
*/
|
||||
override fun onFolderClick(folder: Folder) {
|
||||
supportFragmentManager.beginTransaction()
|
||||
.add(R.id.fragment_container, ImageFragment.newInstance(folder.bucketId))
|
||||
.addToBackStack(null)
|
||||
.commit()
|
||||
changeTitle(folder.name)
|
||||
}
|
||||
|
||||
/**
|
||||
* override Selected Images Change, update view model selected images.
|
||||
*/
|
||||
override fun onSelectedImagesChanged(selectedImages: ArrayList<Image>) {
|
||||
// todo update selected images in view model.
|
||||
}
|
||||
|
||||
/**
|
||||
* Back pressed.
|
||||
* Change toolbar title.
|
||||
*/
|
||||
override fun onBackPressed() {
|
||||
super.onBackPressed()
|
||||
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
|
||||
if(fragment != null && fragment is FolderFragment){
|
||||
changeTitle(getString(R.string.custom_selector_title))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO
|
||||
* Permission check.
|
||||
* OnDone
|
||||
* Activity Result.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener
|
||||
import fr.free.nrw.commons.customselector.model.CallbackStatus
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
import fr.free.nrw.commons.customselector.model.Result
|
||||
|
||||
class CustomSelectorViewModel(val context: Context,var imageFileLoader: ImageFileLoader) : ViewModel() {
|
||||
|
||||
/**
|
||||
* Result Live Data
|
||||
*/
|
||||
val result = MutableLiveData(Result(CallbackStatus.IDLE, arrayListOf()))
|
||||
|
||||
/**
|
||||
* Fetch Images and supply to result.
|
||||
*/
|
||||
fun fetchImages() {
|
||||
result.postValue(Result(CallbackStatus.FETCHING, arrayListOf()))
|
||||
imageFileLoader.abortLoadImage()
|
||||
imageFileLoader.loadDeviceImages(object: ImageLoaderListener {
|
||||
|
||||
override fun onImageLoaded(images: ArrayList<Image>) {
|
||||
result.postValue(Result(CallbackStatus.SUCCESS, images))
|
||||
}
|
||||
|
||||
override fun onFailed(throwable: Throwable) {
|
||||
result.postValue(Result(CallbackStatus.SUCCESS, arrayListOf()))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* View Model Factory.
|
||||
*/
|
||||
class CustomSelectorViewModelFactory @Inject constructor(val context: Context,val imageFileLoader: ImageFileLoader) : ViewModelProvider.Factory {
|
||||
|
||||
override fun<CustomSelectorViewModel: ViewModel?> create(modelClass: Class<CustomSelectorViewModel>) : CustomSelectorViewModel {
|
||||
return CustomSelectorViewModel(context,imageFileLoader) as CustomSelectorViewModel
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.model.Result
|
||||
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
|
||||
import fr.free.nrw.commons.customselector.model.CallbackStatus
|
||||
import fr.free.nrw.commons.customselector.model.Folder
|
||||
import fr.free.nrw.commons.customselector.ui.adapter.FolderAdapter
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import kotlinx.android.synthetic.main.fragment_custom_selector.*
|
||||
import kotlinx.android.synthetic.main.fragment_custom_selector.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class FolderFragment : CommonsDaggerSupportFragment() {
|
||||
|
||||
/**
|
||||
* View Model for images.
|
||||
*/
|
||||
private var viewModel: CustomSelectorViewModel? = null
|
||||
|
||||
/**
|
||||
* View Model Factory.
|
||||
*/
|
||||
var customSelectorViewModelFactory: CustomSelectorViewModelFactory? = null
|
||||
@Inject set
|
||||
|
||||
|
||||
/**
|
||||
* Folder Adapter.
|
||||
*/
|
||||
private lateinit var folderAdapter: FolderAdapter
|
||||
|
||||
/**
|
||||
* Grid Layout Manager for recycler view.
|
||||
*/
|
||||
private lateinit var gridLayoutManager: GridLayoutManager
|
||||
|
||||
/**
|
||||
* Companion newInstance.
|
||||
*/
|
||||
companion object{
|
||||
fun newInstance(): FolderFragment {
|
||||
return FolderFragment()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCreate Fragment, get the view model.
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel = ViewModelProvider(requireActivity(),customSelectorViewModelFactory!!).get(CustomSelectorViewModel::class.java)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCreateView.
|
||||
* Inflate Layout, init adapter, init gridLayoutManager, setUp recycler view, observe the view model for result.
|
||||
*/
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val root = inflater.inflate(R.layout.fragment_custom_selector, container, false)
|
||||
folderAdapter = FolderAdapter(requireActivity(), activity as FolderClickListener)
|
||||
gridLayoutManager = GridLayoutManager(context, columnCount())
|
||||
with(root.selector_rv){
|
||||
this.layoutManager = gridLayoutManager
|
||||
setHasFixedSize(true)
|
||||
this.adapter = folderAdapter
|
||||
}
|
||||
viewModel?.result?.observe(viewLifecycleOwner, Observer {
|
||||
handleResult(it)
|
||||
})
|
||||
return root
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle view model result.
|
||||
* Get folders from images.
|
||||
* Load adapter.
|
||||
*/
|
||||
private fun handleResult(result: Result) {
|
||||
if(result.status is CallbackStatus.SUCCESS){
|
||||
val folders = arrayListOf<Folder>()
|
||||
for( i in 1..12) {
|
||||
folders.add(Folder(i.toLong(), "Folder$i",result.images))
|
||||
}
|
||||
folderAdapter.init(folders)
|
||||
folderAdapter.notifyDataSetChanged()
|
||||
selector_rv.visibility = View.VISIBLE
|
||||
}
|
||||
loader.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Column count ie span count for grid view adapter.
|
||||
*/
|
||||
private fun columnCount(): Int {
|
||||
return 2
|
||||
// todo change column count depending on the orientation of the device.
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageLoaderListener
|
||||
import fr.free.nrw.commons.customselector.model.Image
|
||||
|
||||
class ImageFileLoader(val context: Context) {
|
||||
|
||||
/**
|
||||
* Load Device Images.
|
||||
*/
|
||||
fun loadDeviceImages(listener: ImageLoaderListener) {
|
||||
var tempImage = Image(0, "temp", Uri.parse("http://www.google.com"), "path", 0, "bucket", "1223")
|
||||
var array: ArrayList<Image> = ArrayList()
|
||||
for(i in 1..100) {
|
||||
array.add(tempImage)
|
||||
}
|
||||
listener.onImageLoaded(array)
|
||||
|
||||
// todo load images from device using cursor.
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort loading images.
|
||||
*/
|
||||
fun abortLoadImage(){
|
||||
//todo Abort loading images.
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO
|
||||
* Runnable Thread for image loading.
|
||||
* Sha1 for image (original image).
|
||||
*
|
||||
*/
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
|
||||
import fr.free.nrw.commons.customselector.model.CallbackStatus
|
||||
import fr.free.nrw.commons.customselector.model.Result
|
||||
import fr.free.nrw.commons.customselector.ui.adapter.ImageAdapter
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import kotlinx.android.synthetic.main.fragment_custom_selector.*
|
||||
import kotlinx.android.synthetic.main.fragment_custom_selector.view.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class ImageFragment: CommonsDaggerSupportFragment() {
|
||||
|
||||
/**
|
||||
* Current bucketId.
|
||||
*/
|
||||
private var bucketId: Long? = null
|
||||
|
||||
/**
|
||||
* View model for images.
|
||||
*/
|
||||
private lateinit var viewModel: CustomSelectorViewModel
|
||||
|
||||
/**
|
||||
* View model Factory.
|
||||
*/
|
||||
lateinit var customSelectorViewModelFactory: CustomSelectorViewModelFactory
|
||||
@Inject set
|
||||
|
||||
/**
|
||||
* Image Adapter for recycle view.
|
||||
*/
|
||||
private lateinit var imageAdapter: ImageAdapter
|
||||
|
||||
/**
|
||||
* GridLayoutManager for recycler view.
|
||||
*/
|
||||
private lateinit var gridLayoutManager: GridLayoutManager
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* BucketId args name
|
||||
*/
|
||||
const val BUCKET_ID = "BucketId"
|
||||
|
||||
/**
|
||||
* newInstance from bucketId.
|
||||
*/
|
||||
fun newInstance(bucketId: Long): ImageFragment {
|
||||
val fragment = ImageFragment()
|
||||
val args = Bundle()
|
||||
args.putLong(BUCKET_ID, bucketId)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCreate
|
||||
* Get BucketId, view Model.
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
bucketId = arguments?.getLong(BUCKET_ID)
|
||||
viewModel = ViewModelProvider(requireActivity(),customSelectorViewModelFactory).get(CustomSelectorViewModel::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCreateView
|
||||
* Init imageAdapter, gridLayoutManger.
|
||||
* SetUp recycler view.
|
||||
*/
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
|
||||
val root = inflater.inflate(R.layout.fragment_custom_selector, container, false)
|
||||
imageAdapter = ImageAdapter(requireActivity(), activity as ImageSelectListener)
|
||||
gridLayoutManager = GridLayoutManager(context,getSpanCount())
|
||||
with(root.selector_rv){
|
||||
this.layoutManager = gridLayoutManager
|
||||
setHasFixedSize(true)
|
||||
this.adapter = imageAdapter
|
||||
}
|
||||
|
||||
viewModel.result.observe(viewLifecycleOwner, Observer{
|
||||
handleResult(it)
|
||||
})
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle view model result.
|
||||
*/
|
||||
private fun handleResult(result:Result){
|
||||
if(result.status is CallbackStatus.SUCCESS){
|
||||
val images = result.images
|
||||
if(images.isNotEmpty()) {
|
||||
imageAdapter.init(images)
|
||||
selector_rv.visibility = View.VISIBLE
|
||||
}
|
||||
else{
|
||||
selector_rv.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
loader.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* getSpanCount for GridViewManager.
|
||||
*/
|
||||
private fun getSpanCount(): Int {
|
||||
return 3
|
||||
// todo change span count depending on the device orientation and other factos.
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package fr.free.nrw.commons.customselector.ui.selector
|
||||
|
||||
/**
|
||||
* Image Loader class, loads images, depending on API results.
|
||||
*/
|
||||
class ImageLoader {
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import fr.free.nrw.commons.auth.LoginActivity;
|
|||
import fr.free.nrw.commons.auth.SignupActivity;
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.SearchActivity;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
|
|
@ -34,6 +35,9 @@ public abstract class ActivityBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract MainActivity bindContributionsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract CustomSelectorActivity bindCustomSelectorActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SettingsActivity bindSettingsActivity();
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import fr.free.nrw.commons.R;
|
|||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFileLoader;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.db.AppDatabase;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
|
|
@ -66,6 +67,11 @@ public class CommonsApplicationModule {
|
|||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ImageFileLoader providesImageFileLoader() {
|
||||
return new ImageFileLoader(this.applicationContext);
|
||||
}
|
||||
|
||||
@Provides
|
||||
public Context providesApplicationContext() {
|
||||
return this.applicationContext;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
|
|||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.FolderFragment;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFragment;
|
||||
import fr.free.nrw.commons.explore.ExploreFragment;
|
||||
import fr.free.nrw.commons.explore.ExploreListRootFragment;
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
|
||||
|
|
@ -49,6 +51,12 @@ public abstract class FragmentBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract MediaDetailFragment bindMediaDetailFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract FolderFragment bindFolderFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ImageFragment bindImageFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@
|
|||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_custom_selector"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar_layout"
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
/>
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar_layout"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
<ImageButton
|
||||
android:id="@+id/back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
|
|
@ -13,6 +13,8 @@
|
|||
android:layout_centerVertical="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/standard_gap"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:srcCompat="?attr/custom_selector_back" />
|
||||
|
||||
|
||||
|
|
@ -29,7 +31,7 @@
|
|||
android:text="@string/custom_selector_title"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title" />
|
||||
|
||||
<ImageView
|
||||
<ImageButton
|
||||
android:id="@+id/done"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
|
@ -37,6 +39,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_centerVertical="true"
|
||||
android:clickable="true"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/standard_gap"
|
||||
app:srcCompat="?attr/custom_selector_done"/>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,19 @@
|
|||
app:fabSize="mini"
|
||||
app:srcCompat="@drawable/ic_photo_white_24dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_custom_gallery"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:tint="@color/button_blue"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/main_background_light"
|
||||
app:useCompatPadding="true"
|
||||
app:elevation="@dimen/tiny_margin"
|
||||
app:fabSize="mini"
|
||||
app:srcCompat="@drawable/commons_logo"
|
||||
android:background="@drawable/commons"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab_plus"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/selector_rv"
|
||||
android:background="?attr/mainBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="@dimen/dimen_2"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
|
|
@ -11,7 +11,6 @@
|
|||
app:cardCornerRadius="@dimen/dimen_6"
|
||||
app:cardElevation="@dimen/dimen_2"
|
||||
android:id="@+id/view"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
|
|
@ -24,49 +23,47 @@
|
|||
android:id="@+id/folder_thumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"/>
|
||||
android:background="@color/black"
|
||||
android:alpha="0.15"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<View
|
||||
android:id="@+id/album_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/album_overlay"
|
||||
android:alpha="0.05"
|
||||
/>
|
||||
android:alpha="0.05" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/folder_details"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
>
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_name"
|
||||
android:id="@+id/folder_name"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_margin="@dimen/dimen_6"
|
||||
android:padding="@dimen/dimen_6"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:ellipsize="end"
|
||||
android:padding="@dimen/dimen_6"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"/>
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_count"
|
||||
android:id="@+id/folder_count"
|
||||
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_margin="5dp"
|
||||
android:padding="5dp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16sp"
|
||||
/>
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
@ -75,7 +72,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:constraint_referenced_ids="folder_details,album_overlay"/>
|
||||
app:constraint_referenced_ids="folder_details,album_overlay" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:padding="@dimen/dimen_2"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
android:layout_height="0dp"
|
||||
app:cardCornerRadius="@dimen/dimen_6"
|
||||
app:cardElevation="@dimen/dimen_2"
|
||||
android:id="@+id/view"
|
||||
app:cardUseCompatPadding="true"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
android:id="@+id/selected_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0.4"
|
||||
android:alpha="0.25"
|
||||
android:background="@color/divider_grey"
|
||||
/>
|
||||
|
||||
|
|
@ -38,18 +38,19 @@
|
|||
android:layout_width="@dimen/dimen_20"
|
||||
android:layout_height="@dimen/dimen_20"
|
||||
app:layout_constraintDimensionRatio="H,1:1"
|
||||
android:layout_margin="@dimen/dimen_10"
|
||||
android:gravity="center|center_vertical"
|
||||
android:includeFontPadding="false"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/black"
|
||||
android:layout_margin="@dimen/dimen_6"
|
||||
android:gravity="center|center_vertical"
|
||||
style="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="12"
|
||||
android:background="@drawable/circle_shape"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/selected_view"
|
||||
android:id="@+id/selected_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
|
|
@ -66,15 +67,15 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/uploaded_overlay_icon"
|
||||
android:layout_width="@dimen/dimen_140"
|
||||
android:layout_height="@dimen/dimen_140"
|
||||
android:layout_width="@dimen/dimen_72"
|
||||
android:layout_height="@dimen/dimen_72"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:srcCompat="@drawable/commons"
|
||||
/>
|
||||
|
||||
<androidx.constraintlayout.widget.Group
|
||||
android:id="@+id/uploaded"
|
||||
android:id="@+id/uploaded_group"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue