This commit is contained in:
coco
2026-07-03 15:47:27 +08:00
commit 7846a45f2c
38575 changed files with 4736783 additions and 0 deletions
@@ -0,0 +1,24 @@
package com.dailystudio.devbricksx.gallery
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.dailystudio.devbricksx.gallery", appContext.packageName)
}
}
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name=".GalleryApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SampleGallery">
<activity
android:exported="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,17 @@
package com.dailystudio.devbricksx.gallery
import android.os.Bundle
import androidx.core.view.WindowCompat
import com.dailystudio.devbricksx.app.activity.DevBricksActivity
class MainActivity : DevBricksActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContentView(R.layout.activity_main)
}
}
@@ -0,0 +1,46 @@
package com.dailystudio.devbricksx.gallery.db
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import coil.load
import com.dailystudio.devbricksx.annotations.fragment.DataSource
import com.dailystudio.devbricksx.annotations.fragment.ListFragment
import com.dailystudio.devbricksx.annotations.view.Adapter
import com.dailystudio.devbricksx.annotations.view.ViewType
import com.dailystudio.devbricksx.gallery.core.R
import com.dailystudio.devbricksx.ui.AbsCardViewHolder
@ListFragment(
gridLayout = true,
columns = 2,
dataSource = DataSource.Flow,
layoutByName = "fragment_photos"
)
@Adapter(
viewHolder = PhotoItemViewHolder::class,
viewType = ViewType.Customized,
layoutByName = "layout_photo_item",
paged = true
)
class __PhotoItem
class PhotoItemViewHolder(itemView: View): AbsCardViewHolder<PhotoItem>(itemView) {
override fun bindMedia(item: PhotoItem, iconView: ImageView?) {
iconView?.load(item.thumbnailUrl)
}
override fun getMedia(item: PhotoItem): Drawable? {
return null
}
override fun getTitle(item: PhotoItem): CharSequence {
val context = itemView.context
return context.getString(
R.string.label_author, item.userName)
}
}
@@ -0,0 +1,19 @@
package com.dailystudio.devbricksx.gallery.fragment
import com.dailystudio.devbricksx.fragment.AbsAboutFragment
import com.dailystudio.devbricksx.gallery.core.R
class AboutFragment: AbsAboutFragment() {
override val appName: CharSequence?
get() = getString(R.string.app_name)
override val appDescription: CharSequence?
get() = getString(R.string.app_desc)
override val appIconResource: Int
get() = R.mipmap.ic_launcher
override val appThumbResource: Int
get() = R.drawable.app_thumb
}
@@ -0,0 +1,195 @@
package com.dailystudio.devbricksx.gallery.fragment
import android.app.SearchManager
import android.graphics.Color
import android.os.Bundle
import android.view.*
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.paging.*
import androidx.recyclerview.widget.RecyclerView
import com.dailystudio.devbricksx.development.Logger
import com.dailystudio.devbricksx.gallery.Constants
import com.dailystudio.devbricksx.gallery.api.UnsplashApiInterface
import com.dailystudio.devbricksx.gallery.db.PhotoItem
import com.dailystudio.devbricksx.gallery.db.PhotoItemMediator
import com.dailystudio.devbricksx.gallery.model.PhotoItemViewModelExt
import com.dailystudio.devbricksx.utils.ResourcesCompatUtils
import com.dailystudio.devbricksx.utils.SystemBarsUtils
import com.google.android.material.appbar.MaterialToolbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOn
import com.dailystudio.devbricksx.gallery.R as R
import com.dailystudio.devbricksx.gallery.core.R as coreR
class PhotoItemsListFragmentExt: PhotoItemsListFragment() {
private var topBar: MaterialToolbar? = null
lateinit var viewModel: PhotoItemViewModelExt
private var lastQuery: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(requireActivity())
.get(PhotoItemViewModelExt::class.java)
viewModel.photoQuery.observe(this) {
requireActivity().invalidateOptionsMenu()
}
}
override fun setupViews(fragmentView: View) {
super.setupViews(fragmentView)
val activity = requireActivity()
if (activity is AppCompatActivity) {
activity.setSupportActionBar(fragmentView.findViewById(R.id.topAppBar))
}
setHasOptionsMenu(true)
SystemBarsUtils.statusBarColor(
requireActivity(),
ResourcesCompatUtils.getColor(requireContext(), coreR.color.primaryColor)
)
topBar = fragmentView.findViewById(R.id.topAppBar)
topBar?.setTitle(coreR.string.app_name)
disableItemChangeDuration()
}
override fun createDataSource(): Flow<PagingData<PhotoItem>> {
return viewModel.filterPhotos(requireActivity().lifecycleScope)
}
override fun onItemClick(
recyclerView: RecyclerView,
itemView: View,
position: Int,
item: PhotoItem,
id: Long
) {
super.onItemClick(recyclerView, itemView, position, item, id)
Logger.debug("item: $item")
val action = PhotoItemsListFragmentExtDirections
.actionPhotoItemsListFragmentExtToPhotoViewFragment(
item.id,
item.thumbnailUrl,
item.downloadUrl,
item.uid,
item.color,
item.exif
)
findNavController().navigate(action)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.photoQuery.observe(viewLifecycleOwner) { query ->
Logger.debug("query request: $query")
if (lastQuery == query) {
Logger.info("skip duplicated query: $query [last: $lastQuery]")
return@observe
}
reload()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_main, menu)
val query = viewModel.photoQuery.value
val clearSearchMenuItem = menu.findItem(R.id.action_clear_search)
clearSearchMenuItem.isVisible = (viewModel.photoQuery.value != Constants.QUERY_ALL)
val queryText: TextView? = clearSearchMenuItem.actionView?.findViewById(R.id.query_text)
queryText?.text = query
queryText?.setOnClickListener {
queryPhotos(Constants.QUERY_ALL)
}
val searchMenuItem = menu.findItem(R.id.action_search);
val searchView: SearchView =
searchMenuItem.actionView as SearchView
val v: View = searchView.findViewById(androidx.appcompat.R.id.search_plate)
v.setBackgroundColor(Color.TRANSPARENT)
searchMenuItem.setOnActionExpandListener(object: MenuItem.OnActionExpandListener {
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
clearSearchMenuItem.isVisible = false
if (Constants.QUERY_ALL != query) {
searchView.post {
searchView.setQuery(query, false)
}
}
return true
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
clearSearchMenuItem.isVisible = (viewModel.photoQuery.value != Constants.QUERY_ALL)
return true
}
})
searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
val newQuery = query ?: Constants.QUERY_ALL
Logger.debug("final query: $newQuery")
queryPhotos(newQuery.lowercase())
searchMenuItem.collapseActionView()
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
return true
}
} )
val activity = requireActivity()
val searchManager =
activity.getSystemService(AppCompatActivity.SEARCH_SERVICE) as SearchManager
searchView.setSearchableInfo(
searchManager.getSearchableInfo(activity.componentName))
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_about -> {
findNavController().navigate(
R.id.action_photoItemsListFragmentExt_to_aboutFragment
)
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun queryPhotos(query: String) {
viewModel.searchPhotos(query)
}
}
@@ -0,0 +1,237 @@
package com.dailystudio.devbricksx.gallery.fragment
import android.Manifest
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.navArgs
import coil.load
import coil.transform.CircleCropTransformation
import com.dailystudio.devbricksx.development.Logger
import com.dailystudio.devbricksx.fragment.AbsPermissionsFragment
import com.dailystudio.devbricksx.gallery.Directories
import com.dailystudio.devbricksx.gallery.R as R
import com.dailystudio.devbricksx.gallery.core.R as coreR
import com.dailystudio.devbricksx.gallery.api.ImageApi
import com.dailystudio.devbricksx.gallery.model.UserItemViewModelExt
import com.dailystudio.devbricksx.utils.FileUtils
import com.dailystudio.devbricksx.utils.SystemBarsUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class PhotoViewFragment: AbsPermissionsFragment() {
override val autoCheckPermissions: Boolean
get() = false
private val args: PhotoViewFragmentArgs by navArgs()
private var photoView: ImageView? = null
private var bottomLayout: View? = null
private var nameView: TextView? = null
private var iconView: ImageView? = null
private var sourceView: TextView? = null
private var saveButton: Button? = null
private var downloadProgress: ProgressBar? = null
private lateinit var userModel: UserItemViewModelExt
private var hasDownloadRequest: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
userModel = ViewModelProvider(this)[UserItemViewModelExt::class.java]
Logger.debug("username: ${args.username}")
Logger.debug("thumb: ${args.thumbUrl}")
Logger.debug("download: ${args.downloadUrl}")
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
userModel.userByName(args.username).collect {
nameView?.text = it?.displayName
iconView?.load(it?.photoUrl) {
transformations(CircleCropTransformation())
}
}
}
}
lifecycleScope.launch(Dispatchers.IO) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
userModel.pullUser(args.username)
/*
* Improve Status Bar transformation during navigation animation
*/
delay(resources.getInteger(coreR.integer.animLength)/ 2L)
withContext(Dispatchers.Main) {
SystemBarsUtils.statusBarColor(requireActivity(),
Color.TRANSPARENT
)
}
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_photo_view, null)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
photoView = view.findViewById(R.id.photo)
photoView?.loadWithQuality(
args.downloadUrl,
args.thumbUrl,
)
bottomLayout = view.findViewById(R.id.bottom_layout)
bottomLayout?.let {
alignBottomLayout(it)
}
nameView = view.findViewById(R.id.user_name)
nameView?.text = args.username
iconView = view.findViewById(R.id.user_photo)
iconView?.backgroundTintList = (ColorStateList.valueOf(Color.parseColor(args.color)))
sourceView = view.findViewById(R.id.image_source)
sourceView?.text = getString(coreR.string.source_unsplash)
saveButton = view.findViewById(R.id.download)
saveButton?.setOnClickListener {
hasDownloadRequest = true
checkOrGrantPermissions()
}
downloadProgress = view.findViewById(R.id.progress)
}
private fun alignBottomLayout(layout: View) {
ViewCompat.setOnApplyWindowInsetsListener(layout) { view, windowInsets ->
val margin = resources.getDimensionPixelSize(
com.dailystudio.devbricksx.R.dimen.default_content_padding)
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
// Apply the insets as a margin to the view. Here the system is setting
// only the bottom, left, and right dimensions, but apply whichever insets are
// appropriate to your layout. You can also update the view padding
// if that's more appropriate.
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left + margin
bottomMargin = insets.bottom + margin
rightMargin = insets.right + margin
}
// Return CONSUMED if you don't want want the window insets to keep being
// passed down to descendant views.
WindowInsetsCompat.CONSUMED
}
}
override fun getPermissionsPromptViewId(): Int {
return -1;
}
override fun getRequiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT < 33) {
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
emptyArray()
}
}
override fun onPermissionsGranted(newlyGranted: Boolean) {
if (!hasDownloadRequest) {
return
}
lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
downloadProgress?.visibility = View.VISIBLE
downloadProgress?.progress = 0
saveButton?.visibility = View.INVISIBLE
}
val bytes = ImageApi.download(args.downloadUrl) {
// Logger.debug("progress: $it")
val progress = (it.bytesRead * 100 / it.contentLength).toInt()
lifecycleScope.launch(Dispatchers.Main) {
if (progress >= 100) {
downloadProgress?.visibility = View.GONE
saveButton?.visibility = View.VISIBLE
hasDownloadRequest = false
} else {
downloadProgress?.progress = progress
}
}
}
Logger.debug("${bytes?.size ?: 0} bytes downloaded")
bytes?.let {
val ret = saveImage(args.id, it)
Logger.debug("save image [${args.id}: ret = $ret")
}
}
}
override fun onPermissionsDenied() {
}
private fun saveImage(imageId: String, bytes: ByteArray): Boolean {
val saveFilePath = Directories.getImageDownloadPath(imageId)
Logger.debug("save image to: $saveFilePath")
return FileUtils.saveToFile(bytes, saveFilePath)
}
}
fun ImageView.loadWithQuality(
highQuality: String,
lowQuality: String,
placeholderRes: Int? = null,
errorRes: Int? = null
) {
load(lowQuality) {
placeholderRes?.let { placeholder(placeholderRes) }
listener(onSuccess = { _, _ ->
load(highQuality) {
placeholder(drawable) // If there was a way to not clear existing image before loading, this would not be required
errorRes?.let { error(errorRes) }
}
})
}
}
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/main_graph"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/photo"
android:scaleType="centerCrop"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/bottom_layout"
style="?attr/materialCardViewElevatedStyle"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:padding="@dimen/default_padding"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:background="@drawable/photo_badge"
android:padding="2dp"
android:id="@+id/user_photo"
android:layout_centerVertical="true"
android:layout_margin="@dimen/default_padding"
android:layout_width="@dimen/default_action_icon_size"
android:layout_height="@dimen/default_action_icon_size"/>
<Button
style="?attr/materialIconButtonStyle"
android:id="@+id/download"
app:icon="@drawable/ic_action_download"
app:iconSize="@dimen/photo_download_icon_size"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
style="?attr/materialIconButtonStyle"
android:id="@+id/progress"
android:visibility="gone"
android:layout_alignStart="@id/download"
android:layout_alignEnd="@id/download"
android:layout_alignTop="@id/download"
android:layout_alignBottom="@id/download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="vertical"
android:layout_toEndOf="@id/user_photo"
android:layout_toStartOf="@id/download"
android:layout_centerVertical="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/user_name"
style="@style/UserName"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/image_source"
style="@style/ImageSource"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
</RelativeLayout>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
app:liftOnScroll="false"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/topAppBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@android:id/list"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
style="@style/DefaultRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingStart="@dimen/photo_item_padding_h"
android:paddingEnd="@dimen/photo_item_padding_h"
android:paddingTop="@dimen/photo_item_padding_v"
android:paddingBottom="@dimen/photo_item_padding_h"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/list_item_root"
style="@style/DefaultCardView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:background="@color/black"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/card_media"
android:foreground="?attr/selectableItemBackground"
style="@style/ImmersiveCardViewMedia"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
<TextView
android:id="@+id/card_supporting_text"
style="@style/PhotoItemSupportingText"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_width="match_parent"
android:layout_height="@dimen/photo_item_supporting_height"/>
-->
<TextView
android:id="@+id/card_title"
style="@style/PhotoItemTitle"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="@dimen/photo_item_title_height"/>
</RelativeLayout>
</FrameLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.Chip
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/query_text"
style="@style/ToolbarChip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_search"
android:title="@string/menu_search"
android:icon="@drawable/ic_action_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
<item android:id="@+id/action_clear_search"
android:title="@string/menu_clear_search"
app:actionLayout="@layout/layout_query_tag"
app:showAsAction="always"/>
<item
android:title="@string/menu_about"
app:showAsAction="never"
android:id="@+id/action_about"/>
</menu>
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation"
app:startDestination="@id/photoItemsListFragmentExt">
<fragment
android:id="@+id/photoItemsListFragmentExt"
android:name="com.dailystudio.devbricksx.gallery.fragment.PhotoItemsListFragmentExt"
android:label="PhotoItemsListFragmentExt" >
<action
android:id="@+id/action_photoItemsListFragmentExt_to_photoViewFragment"
app:destination="@id/photoViewFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_photoItemsListFragmentExt_to_aboutFragment"
app:destination="@id/aboutFragment" />
</fragment>
<fragment
android:id="@+id/photoViewFragment"
android:name="com.dailystudio.devbricksx.gallery.fragment.PhotoViewFragment"
android:label="PhotoViewFragment" >
<argument
android:name="id"
app:argType="string" />
<argument
android:name="thumbUrl"
app:argType="string" />
<argument
android:name="downloadUrl"
app:argType="string" />
<argument
android:name="username"
app:argType="string" />[
<argument
android:name="color"
app:argType="string" />
<argument
android:name="exif"
app:argType="string" />
</fragment>
<dialog
android:id="@+id/aboutFragment"
android:name="com.dailystudio.devbricksx.gallery.fragment.AboutFragment"
android:label="AboutFragment" />
</navigation>
@@ -0,0 +1,3 @@
<!--?xml version="1.0" encoding="UTF-8"?-->
<resources>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
@@ -0,0 +1,2 @@
<resources>
</resources>
@@ -0,0 +1,51 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="PhotoItemText">
<item name="android:textStyle">italic</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">marquee</item>
</style>
<style name="PhotoItemTitle" parent="PhotoItemText">
<item name="android:textSize">@dimen/photo_item_title_font_size</item>
<item name="android:textColor">@color/light_white</item>
<item name="android:gravity">end|bottom</item>
<item name="android:background">@drawable/photo_item_title_background</item>
<item name="android:paddingStart">@dimen/photo_item_title_padding_h</item>
<item name="android:paddingEnd">@dimen/photo_item_title_padding_h</item>
<item name="android:paddingTop">0dp</item>
<item name="android:paddingBottom">@dimen/photo_item_title_padding_v</item>
</style>
<style name="PhotoItemSupportingText" parent="PhotoItemText">
<item name="android:textStyle">italic|bold</item>
<item name="android:textSize">@dimen/photo_item_supporting_font_size</item>
<item name="android:textColor">@color/primaryColor</item>
<item name="android:gravity">start|top</item>
<item name="android:background">@drawable/photo_item_supporting_background</item>
<item name="android:paddingStart">@dimen/photo_item_title_padding_h</item>
<item name="android:paddingEnd">@dimen/photo_item_title_padding_h</item>
<item name="android:paddingTop">@dimen/photo_item_title_padding_v</item>
<item name="android:paddingBottom">0dp</item>
</style>
<style name="UserName">
<item name="android:gravity">left|center_vertical</item>
<item name="android:textColor">@color/light_black</item>
<item name="android:textSize">@dimen/photo_user_name_font_size</item>
<item name="android:paddingStart">@dimen/default_label_padding</item>
<item name="android:paddingEnd">@dimen/default_label_padding</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>
<style name="ImageSource">
<item name="android:gravity">left|center_vertical</item>
<item name="android:textColor">@color/light_gray</item>
<item name="android:textSize">@dimen/photo_source_font_size</item>
<item name="android:textStyle">italic</item>
<item name="android:paddingStart">@dimen/default_label_padding</item>
<item name="android:paddingEnd">@dimen/default_label_padding</item>
</style>
</resources>
@@ -0,0 +1,17 @@
package com.dailystudio.devbricksx.gallery
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}