Coder Social home page Coder Social logo

azrael8576 / picquest Goto Github PK

View Code? Open in Web Editor NEW
13.0 1.0 0.0 97.33 MB

This is a modularized app designed for exploring high-quality image and video content, utilizing a single-activity MVI architecture. It's fully built with Jetpack Compose and Material 3, incorporating Paging3 and Jetpack Media3 ExoPlayer.

License: Apache License 2.0

Kotlin 100.00%
android flow jetpack-compose modularization-android mvi-architecture paging3 exoplayer

picquest's Introduction

PicQuest

Android CI GitHub release (with filter) License

Logo

"PicQuest" 結合「圖片(Pic)」和「探索(Quest)」的理念,不僅讓用戶探索和發現各種圖片,還提供高畫質影片,讓您的探索更加豐富多彩。

選擇您喜愛的方式來體驗:

  • 列表式佈局(List):傳統而簡潔,適合快速瀏覽。
  • 交錯格狀佈局(StaggeredGrid):創新而動感,為您的探索增添視覺趣味。

Screenshots

Demo-Light-Landscape

Demo-Dark-Portrait

Demo-Picture-in-Picture(PiP)

Tech stack

Architecture

  • MVI Architecture (Model - View - Intent)

UI

  • Jetpack Compose

Design System

  • Material 3

Asynchronous

  • Coroutines
  • Kotlin Flow

Network

  • Retrofit2 & OkHttp3: Construct the REST APIs and paging network data.
  • Paging3: Facilitates efficient loading and displaying of paged data as part of Android's Jetpack suite.

Dependency Injection (DI)

  • Hilt: for dependency injection.

Navigation

Data Storage

  • Proto DataStore: A Jetpack solution for storing key-value pairs or typed objects using protocol buffers. It leverages Kotlin coroutines and Flow for asynchronous and transactional data storage.

Image Loading

  • Coil: An image loading library for Android backed by Kotlin Coroutines.

Jetpack Media3

  • ExoPlayer: A key part of Jetpack Media3, offering advanced media playback capabilities.

Testing

  • Turbine: A small testing library for kotlinx.coroutines Flow.
  • Google Truth: Fluent assertions for Java and Android.
  • Roborazzi: A screenshot testing library for JVM.
  • Robolectric: Robolectric is the industry-standard unit testing framework for Android.

Backend

  • Pixabay API: Offers access to a vast multimedia content library, suitable for various applications.

Require

建構此 App 你可能需要以下工具:

  • Android Studio Giraffe | 2022.3.1
  • JDK JavaVersion.VERSION_17

常見類封裝

在此應用程式中,我們對於 MVI 架構中常見的使用情境進行了以下封裝:

  • BaseViewModel:提供 MutableStateFlow 供 UI 訂閱 UI State,並提供 dispatch() 抽象方法供子類別實現。

Note: 通過 dispatch() 統一處理事件分發,有助於 View 與 ViewModel 間的解耦,同時也更利於日誌分析與後續處理。

  • StateFlowStateExtensions.kt:封裝 UI StateFlow 流,提供更方便的操作方式。
  • DataSourceResult.kt:封裝數據源結果的密封類別,封裝可能是成功 (Success)、錯誤 (Error) 或正在加載 (Loading) 的狀態。

Build

該應用程序包含常用 demoDebugdemoRelease build variants。(prod variants 保留未來供生產環境所使用).

對於正常開發,請使用該 demoDebug variant。對於 UI 性能測試,請使用該 demoRelease variant。

Note: 詳見 Google 官方網誌文章 Why should you always test Compose performance in release?

DesignSystem

本專案採用 Material 3 Design ,使用自適應佈局來 Support different screen sizes

Architecture

本專案遵循了 Android 官方應用架構指南

MVI 最佳實踐

UI 事件決策樹:

以下圖表顯示尋找處理特定事件用途最佳方式時的決策樹。
image

UI 事件:

不要使用 Channels, SharedFlow 或其他回應式串流向 UI 公開 ViewModel 事件。

  1. 立即處理一次性的 ViewModel 事件,並將其降為 UI 狀態。
  2. 使用可觀察的數據持有類型來公開狀態。

Note: 關於不應使用上述 API 的理由和示例,

請參閱 Google 官方網誌文章 ViewModel: One-off event antipatterns

Modularization

Types of modules in PicQuest

image

Top tip:模組圖(如上所示)在模組化規劃期間有助於視覺化展示模組間的依賴性。

PicQuest 主要包含以下幾種模組:

  • app 模組 - 此模組包含 app 級別的核心組件和 scaffolding 類,例如 MainActivityPqApp 以及 app 級別控制的導航。app 模組將會依賴所有的 feature 模組和必要的 core 模組。

  • feature: 模組 - 這些模組各自專注於某個特定功能或用戶的互動流程。每個模組都只聚焦於一個特定的功能職責。如果某個類別只被一個 feature 模組所需要,那麼它應只存在於該模組中;若非如此,則應該將其移至適當的 core 模組。每個 feature 模組應避免依賴其他 feature 模組,並只應依賴其所需的 core 模組。

  • core: 模組 - 這些模組是公共的函式庫模組,它們包含了眾多輔助功能的程式碼和那些需要在多個模組間共享的依賴項。這些模組可以依賴其他 core 模組,但絕不應依賴於feature模組或app模組。

  • 其他各種模組:例如 testing 模組,主要用於進行軟體測試。

Modules

採用上述模組化策略,PicQuest 應用程序具有以下模組:

Name Responsibilities Key classes and good examples
app 將所有必要元素整合在一起,確保應用程式的正確運作。
eg. UI scaffolding、navigation...等
PqApplication,
PqNavHost
TopLevelDestination
PqApp
PqAppState
feature:1,
feature:2
...
負責實現某個特定功能或用戶的互動流程的部分。這通常包含 UI 組件、UseCase 和 ViewModel,並從其他模組讀取資料。 PhotoSearchScreen,
...
core:data 負責從多個來源獲取應用程式的資料,並供其他功能模組共享。 SearchImagesRepository,
utils/ConnectivityManagerNetworkMonitor
core:common 包含被多個模組共享的通用類別。
eg. 工具類、擴展方法...等
network/PqDispatchers,
result/DataSourceResult,
manager/SnackbarManager,
extensions/StateFlowStateExtensions,
utils/UiText
...
core:domain 包含被多個模組共享的 UseCase。
core:model 提供整個應用程式所使用的模型類別。 UserData,
...
core:network 負責發送網絡請求,並處理來自遠程數據源的回應。 RetrofitPqNetwork
core:designsystem UI 依賴項。
eg. app theme、Core UI 元件樣式...等
PqTheme,
PqAppSnackbar
...
core:testing 測試依賴項、repositories 和 util 類。 MainDispatcherRule,
PqTestRunner,
...
core:datastore 儲存持久性數據 PqPreferencesDataSource,
UserPreferencesSerializer,
...

Testing

本專案主要採用 Test doubleRobot Testing Pattern 以及 Screenshot tests 作為測試策略,使測試更加健全且易於維護。

1. Test double

PicQuest 專案中,我們使用了 Hilt 來進行依賴注入。而在資料層,我們將元件定義成接口形式,並依照具體需求進行實現綁定。

策略亮點:

  • PicQuest未使用任何 mocking libraries,而選擇使用 Hilt 的測試 API,方便我們將正式版本輕鬆替換成測試版本。
  • 測試版本與正式版本保持相同的接口,但是測試版本的實現更為簡單且真實,且有特定的測試掛鉤。
  • 這種設計策略不僅降低了測試的脆弱性,還有效提高了代碼覆蓋率。

實例:

  • 在測試過程中,我們為每個 repository 提供測試版本。在測試 ViewModel 時,這些測試版的 repository 會被使用,進而透過測試掛鉤操控其狀態並確認測試結果。

2. Robot Testing Pattern

對於 UI Testing,PicQuest 採用了 Robot Testing Pattern,其核心目的是建立一個抽象層,以聲明性的方式進行 UI 交互。

策略特點:

  1. 易於理解:測試內容直觀,使用者可以快速理解而不必深入了解其背後的實現。
  2. 代碼重用:通過將測試進行模組化,能夠重複使用測試步驟,從而提高測試效率。
  3. 隔離實現細節:透過策略分層,確保了代碼遵循單一責任原則,這不僅提高了代碼的維護性,還使得測試和優化過程更為簡便。

3. Screenshot tests

PicQuest 使用 Roborazzi 進行特定畫面和組件的截圖測試。要運行這些測試,請執行 verifyRoborazziDemoDebugrecordRoborazziDemoDebug 任務。

Important

截圖是在 CI 上使用 Linux 記錄的,其他平台可能產生略有不同的圖像,使得測試失敗。

License

License

PicQuest is distributed under the terms of the Apache License (Version 2.0). See the license for more information.

picquest's People

Contributors

azrael8576 avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

picquest's Issues

[Bug]: Incorrect Pagination Parameter in Pixabay API Calls

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

There appears to be an issue with the pagination parameters in the Pixabay API calls within our application. The parameter 'perPage' is used instead of the correct 'per_page', which could lead to improper data retrieval or API call failures.

Relevant logcat output

No response

[Bug]: Missing 'No Data Found' View in 'VideoLibraryScreen'

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

When a search query resulted in no data, the screen did not display the expected 'No Data Found' view.

Relevant logcat output

No response

[FR]: ContactMeScreen

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

Add ContactMeScreen

Describe the solution

Add ContactMeScreen

Additional context

No response

[Bug]: Project Fails to Sync on Initial Clone Due to Case Sensitivity in Project Name

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

On initially cloning the project, the Gradle sync failed with an error stating java.lang.IllegalStateException: Module entity with name: picquest should be available. This issue was due to case sensitivity in the project name.

I expected the project to sync successfully without any errors on initial cloning.

Relevant logcat output

Error Message: java.lang.IllegalStateException: Module entity with name: picquest should be available
Occurred during: Initial project sync after cloning the repository.

[Bug]: Video continues to play in background on leaving the screen

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

When I move the app to the background, the video continues to play.

expect:

  • I want video playback to automatically pause when the video screen is not in the foreground.

Relevant logcat output

No response

[Bug]: Unexpected Video Switching in VideoLibraryRoute on PiP Mode Activation

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

We have identified a bug in the VideoLibraryRoute composable function of our application, specifically related to the behavior of the VerticalPager when the app enters Picture-in-Picture (PiP) mode.

Expected Behavior:
Upon entering PiP mode, the currently playing video should remain active, and the VerticalPager should maintain its current position without triggering any new data fetches from the Paging library.

Actual Behavior:
When the app transitions into PiP mode, a change in window size is inadvertently causing the VerticalPager to perform unintended swipes. This triggers the Paging library to fetch new data, which in turn updates the PagerState. As a result, the currently active video in PiP mode switches to a different video, leading to a disruption in the user experience.

Relevant logcat output

No response

[FR]: Review and Update Module Dependencies

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

As the project evolves, it's essential to review and potentially restructure the dependencies between modules to ensure they are optimally aligned with the project's evolving architecture and goals.

Describe the solution

A systematic review of all module dependencies should be conducted, followed by updates to ensure compliance with the planned architecture. Additionally, updating related documentation to reflect these changes is crucial.

Additional context

No response

[FR]: Refactor VideoLibrary Screen for Enhanced PiP Mode Handling

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

I'm always frustrated when trying to move views to a separate activity or deeply customize them under Picture-in-Picture (PiP) mode. Currently, our VideoLibrary screen's UI is tightly coupled with the PiP mode UI, making it challenging to modify or extend the PiP functionality independently from the main screen.

Describe the solution

I propose a refactor of the VideoLibrary screen to decouple the UI for standard and PiP modes. This would involve:

Creating separate composable functions dedicated to PiP mode UI.
Implementing conditional rendering to switch between standard and PiP mode UI components based on the current mode.
Updating state management and navigation logic to smoothly transition between standard and PiP modes.
If necessary, refactoring the VideoLibraryViewModel to better support the mode switching.
Ensuring thorough testing for both modes to maintain functionality.
The goal is to enhance the flexibility and maintainability of our codebase, especially for future developments in PiP mode.

Additional context

No response

[FR]: Add Video Search Module

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

I can't search and find videos of interest in the app due to the absence of a search videos functionality.

Describe the solution

I would like a video search module to be added to the app, allowing users to search for videos using keywords. This module should enable users to quickly and efficiently find videos that align with their interests. The search functionality should be straightforward, focusing on keyword relevance to yield accurate results, enhancing user experience in discovering new and high-quality video content.

Additional context

No response

[Bug]: App Crash in Release Build with Proto3

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

Issue Description

Problem:
The application crashes only in release build when using proto3 modules. The crash logs show a java.lang.RuntimeException: Field isFirstTimeUser_ for g7.o not found. This issue is not observed in debug mode.

Expected Behavior:
The expected behavior was a stable release build without any crashes.

Relevant logcat output

2023-12-07 18:15:04.684  8295-8295  AndroidRuntime          com.wei.picquest.demo                E  FATAL EXCEPTION: main
                                                                                                    Process: com.wei.picquest.demo, PID: 8295
                                                                                                    java.lang.RuntimeException: Field isFirstTimeUser_ for g7.o not found. Known fields are [public z6.x g7.o.e, public z6.x g7.o.f, public static final g7.o g7.o.g, public static volatile z6.u g7.o.h]
                                                                                                    	at z6.q0.E(SourceFile:1)
                                                                                                    	at z6.q0.w(SourceFile:1)
                                                                                                    	at z6.u0.a(SourceFile:207)
                                                                                                    	at g7.o.o(SourceFile:28)
                                                                                                    	at a3.i0.f(SourceFile:77)
                                                                                                    	at a3.i0.g(SourceFile:111)
                                                                                                    	at a3.i0.c(SourceFile:1)
                                                                                                    	at a3.i0.e(SourceFile:1)
                                                                                                    	at a3.q.m(SourceFile:81)
                                                                                                    	at a3.q.g0(SourceFile:13)
                                                                                                    	at a3.k.m(SourceFile:93)
                                                                                                    	at s8.a.l(SourceFile:1)
                                                                                                    	at k9.d0.run(SourceFile:1)
                                                                                                    	at l9.d.run(SourceFile:14)
                                                                                                    	at q9.i.run(SourceFile:1)
                                                                                                    	at q9.a.run(SourceFile:94)
                                                                                                    	Suppressed: p9.e: [n1{Cancelling}@1c7dce, Dispatchers.Main.immediate]

[FR]: Implement Picture-in-Picture Mode in Video Module

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

In the current implementation of the video module, there's a lack of support for Picture-in-Picture (PiP) mode which limits the user experience, especially for multitasking.

Describe the solution

I would like to see the video module enhanced with Picture-in-Picture (PiP) functionality. This feature should allow users to watch videos in a floating window while navigating through other parts of the application or even while using other apps.

Additional context

No response

[FR]: Refactor Kotlinify codebase (ktlint v.1.0.1)

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

  • Following this refactor, it could be great to envision a more "strict" code formatter like ktlint 1.0.1 (we are currently stuck at 0.48.1)

Describe the solution

  • Remove unnecessary nullable types
  • Replace no-op method bodies with Unit
  • Convert to expression body
  • Replace if with when
  • Remove braces from 'when' entries
  • Remove braces from if statement
  • Convert to single line lambda
  • oneline if/returns
  • Replace 'contains' call with 'in' operator

Additional context

No response

[FR]: Photo Screen

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

App Requirements: App can search by integration with image source and the display the result as list and grid (need swap option).

Describe the solution

Add Photo Library Module

Additional context

No response

[FR]: Enforce `resourcePrefix` on Android library modules

Is there an existing issue for this?

  • I have searched the existing issues

Describe the problem

I'm always frustrated when resources across different Android library modules conflict or become difficult to manage due to inconsistent naming conventions.

Describe the solution

    Enforce a `resourcePrefix` naming convention on Android library modules to:
    - Dynamically generate resource prefixes based on the module's path, ensuring unique prefixes across modules (e.g., for a module path `:core:module1`, use `core_module1_` as the prefix).
    - Ensure all resource names within a module start with its prefix, enhancing resource recognizability and preventing naming conflicts.
    - Improve project maintainability and resource management by making resource origins clearer and organizing resources systematically.

Additional context

No response

[Bug]: Picture-in-Picture Mode Aspect Ratio Error

Is there an existing issue for this?

  • I have searched the existing issues

Is there a StackOverflow question about this issue?

  • I have searched StackOverflow

What happened?

What happened?

  • Encountered an IllegalArgumentException in Picture-in-Picture (PiP) mode.
  • Aspect ratio calculated as approximately 2.58 from a width of 480 and a height of 186.
  • This aspect ratio is outside the Android system's allowed range for PiP mode (0.418410 to 2.390000).

Expected Behavior:

  • PiP mode should adjust the aspect ratio within the allowed range.
  • Avoid crashes when aspect ratio is outside the allowed range.

Relevant logcat output

java.lang.IllegalArgumentException: setPictureInPictureParams: Aspect ratio is too extreme (must be between 0.418410 and 2.390000).
                                     	at android.os.Parcel.createExceptionOrNull(Parcel.java:3015)
                                     	at android.os.Parcel.createException(Parcel.java:2995)
                                     	at android.os.Parcel.readException(Parcel.java:2978)
                                     	at android.os.Parcel.readException(Parcel.java:2920)
                                     	at android.app.IActivityClientController$Stub$Proxy.setPictureInPictureParams(IActivityClientController.java:1808)
                                     	at android.app.ActivityClient.setPictureInPictureParams(ActivityClient.java:352)
                                     	at android.app.Activity.setPictureInPictureParams(Activity.java:2926)
                                     	at com.wei.picquest.core.pip.PictureInPictureKt.updatedPipParams(PictureInPicture.kt:28)
                                     	at com.wei.picquest.feature.video.videolibrary.VideoLibraryScreenKt.calculateAndSetPiPParams(VideoLibraryScreen.kt:382)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.