├── .gitignore ├── LICENSE ├── README.md ├── app ├── build.gradle.kts ├── libs │ ├── ffmpeg-core.aar │ └── ffmpeg-native.aar ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── projectmaterial │ │ └── videos │ │ ├── activity │ │ ├── CollectionActivity.java │ │ ├── SettingsActivity.java │ │ ├── VideoLibraryActivity.java │ │ └── base │ │ │ ├── BaseActivity.java │ │ │ └── BaseSettingsActivity.java │ │ ├── adapter │ │ ├── CollectionAdapter.java │ │ ├── FavoriteVideoAdapter.java │ │ ├── MenuItemAdapter.java │ │ ├── MoreInfoAdapter.java │ │ ├── VideoAdapter.java │ │ └── base │ │ │ └── BaseVideoAdapter.java │ │ ├── app │ │ └── VideoApplication.java │ │ ├── behavior │ │ └── FragmentContainerViewBehavior.java │ │ ├── bottomsheet │ │ ├── BottomSheetUtils.java │ │ ├── CollectionSortingDialogFragment.java │ │ └── VideoSortingDialogFragment.java │ │ ├── comparator │ │ ├── CollectionComparatorDate.java │ │ ├── CollectionComparatorName.java │ │ ├── CollectionComparatorVideoCount.java │ │ ├── VideoComparatorDate.java │ │ ├── VideoComparatorDuration.java │ │ ├── VideoComparatorName.java │ │ └── VideoComparatorSize.java │ │ ├── database │ │ ├── Collection.java │ │ └── Video.java │ │ ├── fragment │ │ ├── CollectionFragment.java │ │ ├── CollectionsFragment.java │ │ ├── FavoritesFragment.java │ │ ├── SettingsFragment.java │ │ ├── VideosFragment.java │ │ └── base │ │ │ └── BaseFragment.java │ │ ├── model │ │ ├── MenuItem.java │ │ └── MoreInfo.java │ │ ├── recyclerview │ │ ├── GridLayoutItemDecoration.java │ │ ├── GridSpacingItemDecoration.java │ │ ├── LinearLayoutItemDecoration.java │ │ ├── NonScrollableGridLayoutManager.java │ │ └── NonScrollableLinearLayoutManager.java │ │ ├── util │ │ ├── AdapterUtils.java │ │ ├── BitmapCache.java │ │ ├── BottomSheetDialogUtils.java │ │ ├── CollectionUtils.java │ │ ├── DialogUtils.java │ │ ├── GeneralUtils.java │ │ ├── LayoutType.java │ │ ├── MediaMetadataHelper.java │ │ ├── SortingCriteria.java │ │ ├── SortingOrder.java │ │ ├── SortingUtils.java │ │ ├── ThumbnailLoader.java │ │ ├── VideoOptionUtils.java │ │ └── VideoUtils.java │ │ ├── viewholder │ │ ├── CollectionViewHolder.java │ │ └── VideoViewHolder.java │ │ ├── viewmodel │ │ ├── CollectionViewModel.java │ │ ├── FavoriteVideoViewModel.java │ │ └── VideoViewModel.java │ │ ├── widget │ │ └── EmptyStateView.java │ │ └── worker │ │ └── VideoFetchWorker.java │ └── res │ ├── drawable-anydpi │ ├── ic_launcher_foreground.xml │ └── ic_launcher_monochrome.xml │ ├── drawable │ ├── bottom_nav_folders.xml │ ├── bottom_nav_starred.xml │ ├── bottom_nav_videos.xml │ ├── empty_state_view_collections.xml │ ├── empty_state_view_favorites.xml │ ├── empty_state_view_videos.xml │ ├── moreinfo_background.xml │ ├── placeholder_thumbnail.xml │ ├── placeholder_thumbnail_icon.xml │ ├── projectx_selectable_item_background.xml │ ├── quantum_ic_animated_images_vd_theme_24.xml │ ├── quantum_ic_arrow_back_vd_theme_24.xml │ ├── quantum_ic_calendar_clock_vd_theme_24.xml │ ├── quantum_ic_check_vd_theme_24.xml │ ├── quantum_ic_delete_vd_theme_24.xml │ ├── quantum_ic_edit_vd_theme_24.xml │ ├── quantum_ic_folder_filled_vd_theme_24.xml │ ├── quantum_ic_folder_vd_theme_24.xml │ ├── quantum_ic_grid_view_vd_theme_24.xml │ ├── quantum_ic_info_vd_theme_24.xml │ ├── quantum_ic_more_vert_vd_theme_24.xml │ ├── quantum_ic_movie_filled_vd_theme_24.xml │ ├── quantum_ic_movie_vd_theme_24.xml │ ├── quantum_ic_settings_vd_theme_24.xml │ ├── quantum_ic_share_vd_theme_24.xml │ ├── quantum_ic_sort_vd_theme_24.xml │ ├── quantum_ic_star_filled_vd_theme_24.xml │ ├── quantum_ic_star_vd_theme_24.xml │ ├── quantum_ic_straighten_vd_theme_24.xml │ ├── quantum_ic_text_fields_vd_theme_24.xml │ ├── quantum_ic_view_agenda_vd_theme_24.xml │ ├── quantum_ic_weight_vd_theme_24.xml │ └── quantum_splashscreen_icon.xml │ ├── layout │ ├── activity_collection.xml │ ├── activity_main.xml │ ├── activity_settings.xml │ ├── bottom_sheet_moreinfo_collection.xml │ ├── bottom_sheet_moreinfo_video.xml │ ├── bottom_sheet_options_menu.xml │ ├── bottom_sheet_sorting_collection.xml │ ├── bottom_sheet_sorting_video.xml │ ├── collapsing_toolbar_base_layout.xml │ ├── collapsing_toolbar_content_layout.xml │ ├── dialog_rename.xml │ ├── empty_state_view.xml │ ├── fragment_collection.xml │ ├── fragment_collections.xml │ ├── fragment_favorites.xml │ ├── fragment_videos.xml │ ├── item_collection_grid.xml │ ├── item_collection_list.xml │ ├── item_filter.xml │ ├── item_video.xml │ ├── menu_item_view.xml │ └── moreinfo_item_view.xml │ ├── menu │ ├── menu_collections.xml │ ├── menu_favorites.xml │ ├── menu_navigation.xml │ └── menu_videos.xml │ ├── mipmap-anydpi │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── navigation │ └── main_nav.xml │ ├── values-en-rGB │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-night │ ├── colors.xml │ └── themes.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ ├── styles.xml │ └── themes.xml │ └── xml │ ├── file_paths.xml │ ├── locales_config.xml │ └── preferences.xml ├── build.gradle.kts ├── components ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── projectmaterial │ │ └── widget │ │ └── MaterialCheckedTextView.java │ └── res │ ├── color │ ├── m3_checked_text_background_color.xml │ ├── m3_checked_text_icon_tint.xml │ ├── m3_checked_text_ripple_color.xml │ └── m3_checked_text_text_color.xml │ ├── drawable │ └── quantum_ic_check_vd_theme_24.xml │ ├── layout │ └── m3_alert_select_dialog_singlechoice.xml │ └── values │ ├── attrs.xml │ ├── dimens.xml │ ├── styles.xml │ └── themes.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── preference ├── build.gradle.kts └── src │ └── main │ ├── java │ └── com │ │ └── projectmaterial │ │ └── preference │ │ ├── M3EditTextPreference.java │ │ ├── M3EditTextPreferenceDialogFragment.java │ │ ├── M3FooterPreference.java │ │ ├── M3ListPreferenceDialogFragment.java │ │ ├── M3MultiSelectListPreferenceDialogFragment.java │ │ ├── M3PreferenceDialogFragment.java │ │ ├── M3PreferenceFragment.java │ │ └── M3TopIntroPreference.java │ └── res │ ├── drawable │ └── quantum_ic_info_vd_theme_24.xml │ ├── layout-v33 │ ├── m3_preference.xml │ ├── m3_preference_footer.xml │ └── m3_preference_top_intro.xml │ ├── layout │ ├── m3_icon_frame.xml │ ├── m3_preference.xml │ ├── m3_preference_dialog_edittext.xml │ ├── m3_preference_footer.xml │ ├── m3_preference_top_intro.xml │ └── m3_preference_widget_switch_compat.xml │ └── values │ ├── attrs.xml │ ├── bools.xml │ ├── styles.xml │ ├── themes.xml │ └── tokens.xml └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

Videos

5 | 6 |

7 |

8 | 9 | 10 |

11 | 12 | An application designed to elegantly display and organize videos stored locally on your device, featuring a refined and intuitive user interface. 13 | 14 | ## Features 15 | - Modern Material 3 Design 16 | - Utilizes MediaStore and FFmpeg for swift metadata retrieval 17 | - Ability to rename, share, or delete directly within the app 18 | 19 | ## Screenshots 20 | | ![Screenshot 1](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_1.png) | ![Screenshot 2](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_2.png) | ![Screenshot 3](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_3.png) | 21 | |---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| 22 | | ![Screenshot 4](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_4.png) | ![Screenshot 5](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_5.png) | ![Screenshot 6](https://raw.githubusercontent.com/project-material/fastlane/refs/heads/main/screenshot_6.png) | 23 | 24 | ## License 25 | This project is licensed under the GNU General Public License v3.0. For further details, please refer to the [LICENSE](https://github.com/project-material/Videos/blob/main/LICENSE) file. 26 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("androidx.navigation.safeargs") 4 | } 5 | 6 | android { 7 | namespace = "com.projectmaterial.videos" 8 | compileSdk = 34 9 | buildToolsVersion = "34.0.4" 10 | 11 | defaultConfig { 12 | applicationId = "com.projectmaterial.videos" 13 | minSdk = 31 14 | targetSdk = 34 15 | versionCode = 1023 16 | versionName = "1.0.2-beta03" 17 | 18 | ndk { 19 | abiFilters += listOf("arm64-v8a") 20 | } 21 | 22 | vectorDrawables { 23 | useSupportLibrary = true 24 | } 25 | } 26 | 27 | dependenciesInfo { 28 | includeInApk = false 29 | } 30 | 31 | packaging { 32 | dex { 33 | useLegacyPackaging = false 34 | } 35 | 36 | jniLibs { 37 | useLegacyPackaging = false 38 | } 39 | 40 | resources { 41 | excludes += "META-INF/**" 42 | excludes += "com/**" 43 | excludes += "kotlin/**" 44 | excludes += "okhttp3/**" 45 | excludes += "**.properties" 46 | excludes += "**.bin" 47 | } 48 | } 49 | 50 | compileOptions { 51 | sourceCompatibility = JavaVersion.VERSION_17 52 | targetCompatibility = JavaVersion.VERSION_17 53 | } 54 | 55 | buildTypes { 56 | getByName("release") { 57 | isMinifyEnabled = true 58 | isShrinkResources = true 59 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 60 | } 61 | } 62 | } 63 | 64 | dependencies { 65 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar")))) 66 | implementation(project(":preference")) 67 | implementation("androidx.activity:activity:1.9.3") 68 | implementation("androidx.appcompat:appcompat:1.7.0") 69 | implementation("androidx.collection:collection:1.4.5") 70 | implementation("androidx.constraintlayout:constraintlayout:2.2.0") 71 | implementation("androidx.coordinatorlayout:coordinatorlayout:1.2.0") 72 | implementation("androidx.core:core:1.13.1") 73 | implementation("androidx.fragment:fragment:1.6.2") 74 | implementation("androidx.navigation:navigation-fragment:2.8.4") 75 | implementation("androidx.navigation:navigation-ui:2.8.4") 76 | implementation("androidx.preference:preference:1.2.1") 77 | implementation("androidx.recyclerview:recyclerview:1.3.2") 78 | implementation("androidx.work:work-runtime:2.9.1") 79 | implementation("com.github.bumptech.glide:glide:4.16.0") 80 | implementation("com.google.android.material:material:1.13.0-alpha08") 81 | } -------------------------------------------------------------------------------- /app/libs/ffmpeg-core.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-material/Videos/7ed3c9f8def8c197a2c4b5c6d74f1e2ebbe17190/app/libs/ffmpeg-core.aar -------------------------------------------------------------------------------- /app/libs/ffmpeg-native.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/project-material/Videos/7ed3c9f8def8c197a2c4b5c6d74f1e2ebbe17190/app/libs/ffmpeg-native.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -keep class wseemann.media.FFmpegMediaMetadataRetriever { 24 | *; 25 | } 26 | 27 | -keep class wseemann.media.** { *; } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 34 | 40 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/activity/CollectionActivity.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.MenuItem; 5 | import androidx.activity.EdgeToEdge; 6 | import androidx.annotation.NonNull; 7 | import androidx.annotation.Nullable; 8 | import androidx.appcompat.widget.Toolbar; 9 | import androidx.coordinatorlayout.widget.CoordinatorLayout; 10 | import com.google.android.material.snackbar.Snackbar; 11 | import com.projectmaterial.videos.R; 12 | import com.projectmaterial.videos.activity.base.BaseActivity; 13 | import com.projectmaterial.videos.fragment.CollectionFragment; 14 | import com.projectmaterial.videos.util.CollectionUtils; 15 | 16 | public class CollectionActivity extends BaseActivity { 17 | 18 | private CoordinatorLayout coordinator; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setCurrentActivity(this); 24 | setContentView(R.layout.activity_collection); 25 | 26 | String collectionKey = getIntent().getStringExtra("collection_key"); 27 | 28 | coordinator = findViewById(R.id.coordinator); 29 | coordinator.setBackgroundColor(getColor(coordinator, R.attr.colorSurface)); 30 | 31 | Toolbar toolbar = findViewById(R.id.toolbar); 32 | toolbar.setNavigationIcon(R.drawable.quantum_ic_arrow_back_vd_theme_24); 33 | toolbar.setTitle(CollectionUtils.getCollectionName(this, collectionKey)); 34 | setSupportActionBar(toolbar); 35 | 36 | getSupportFragmentManager() 37 | .beginTransaction() 38 | .replace(R.id.fragment_container, CollectionFragment.newInstance(collectionKey)) 39 | .commitNow(); 40 | } 41 | 42 | @Override 43 | public boolean onOptionsItemSelected(MenuItem item) { 44 | switch (item.getItemId()) { 45 | case android.R.id.home: 46 | finishAfterTransition(); 47 | return true; 48 | default: 49 | return super.onOptionsItemSelected(item); 50 | } 51 | } 52 | 53 | public void showSnackbar(@NonNull CharSequence message) { 54 | Snackbar.make(coordinator, message, Snackbar.LENGTH_SHORT).show(); 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/activity/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.activity; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Nullable; 5 | import com.projectmaterial.videos.R; 6 | import com.projectmaterial.videos.activity.base.BaseSettingsActivity; 7 | import com.projectmaterial.videos.fragment.SettingsFragment; 8 | 9 | public class SettingsActivity extends BaseSettingsActivity { 10 | 11 | private SettingsFragment settingsFragment; 12 | 13 | @Override 14 | protected void onCreate(@Nullable Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | settingsFragment = new SettingsFragment(); 17 | getSupportFragmentManager() 18 | .beginTransaction() 19 | .replace(R.id.fragment_container, settingsFragment) 20 | .commitNow(); 21 | } 22 | 23 | @Override 24 | protected void onStart() { 25 | super.onStart(); 26 | setTitle(R.string.settings); 27 | } 28 | 29 | @Override 30 | protected void restart() { 31 | if (settingsFragment.isPreferenceChanged()) { 32 | try { 33 | finish(); 34 | overrideTransition(); 35 | startActivity(getIntent()); 36 | overrideTransition(); 37 | } catch (Throwable e) { 38 | e.printStackTrace(); 39 | recreate(); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/activity/base/BaseSettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.activity.base; 2 | 3 | import android.content.res.Configuration; 4 | import android.content.res.Resources; 5 | import android.graphics.text.LineBreakConfig; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.text.Layout; 9 | import android.view.MenuItem; 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.Nullable; 12 | import androidx.appcompat.widget.Toolbar; 13 | import androidx.coordinatorlayout.widget.CoordinatorLayout; 14 | import com.google.android.material.appbar.AppBarLayout; 15 | import com.google.android.material.appbar.CollapsingToolbarLayout; 16 | import com.projectmaterial.videos.R; 17 | 18 | public class BaseSettingsActivity extends BaseActivity { 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_settings); 24 | 25 | AppBarLayout appBar = findViewById(R.id.app_bar); 26 | AppBarLayout.Behavior appBarBehavior = new AppBarLayout.Behavior(); 27 | appBarBehavior.setDragCallback(new AppBarLayout.Behavior.DragCallback() { 28 | @Override 29 | public boolean canDrag(@NonNull AppBarLayout appBarLayout) { 30 | Resources resources = appBarLayout.getResources(); 31 | Configuration configuration = resources.getConfiguration(); 32 | return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE; 33 | } 34 | }); 35 | 36 | CoordinatorLayout coordinator = findViewById(R.id.coordinator); 37 | coordinator.setBackgroundColor(getColor(coordinator, R.attr.colorSurface)); 38 | CoordinatorLayout.LayoutParams coordinatorParams = (CoordinatorLayout.LayoutParams) appBar.getLayoutParams(); 39 | coordinatorParams.setBehavior(appBarBehavior); 40 | 41 | CollapsingToolbarLayout collapsingToolbar = findViewById(R.id.collapsing_toolbar); 42 | collapsingToolbar.setLineSpacingMultiplier(1.1f); 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { 44 | collapsingToolbar.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL_FAST); 45 | collapsingToolbar.setStaticLayoutBuilderConfigurer(staticLayoutBuilderConfigurer -> { 46 | staticLayoutBuilderConfigurer.setLineBreakConfig(new LineBreakConfig.Builder() 47 | .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) 48 | .build()); 49 | }); 50 | } 51 | 52 | Toolbar toolbar = findViewById(R.id.toolbar); 53 | toolbar.setNavigationIcon(R.drawable.quantum_ic_arrow_back_vd_theme_24); 54 | setSupportActionBar(toolbar); 55 | } 56 | 57 | @Override 58 | public boolean onOptionsItemSelected(MenuItem item) { 59 | switch (item.getItemId()) { 60 | case android.R.id.home: 61 | finishAfterTransition(); 62 | return true; 63 | default: 64 | return super.onOptionsItemSelected(item); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/adapter/MenuItemAdapter.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.adapter; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | import androidx.annotation.NonNull; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | import com.projectmaterial.videos.R; 11 | import com.projectmaterial.videos.model.MenuItem; 12 | import java.util.List; 13 | 14 | public class MenuItemAdapter extends RecyclerView.Adapter { 15 | private final List menuItems; 16 | 17 | public MenuItemAdapter(List menuItems) { 18 | this.menuItems = menuItems; 19 | } 20 | 21 | @NonNull 22 | @Override 23 | public MenuItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 24 | LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 25 | View itemView = inflater.inflate(R.layout.menu_item_view, parent, false); 26 | return new MenuItemViewHolder(itemView); 27 | } 28 | 29 | @Override 30 | public void onBindViewHolder(@NonNull MenuItemViewHolder holder, int position) { 31 | MenuItem menuItem = menuItems.get(position); 32 | holder.icon.setImageResource(menuItem.getIconResId()); 33 | holder.text.setText(menuItem.getTitleResId()); 34 | holder.itemView.setOnClickListener(menuItem.getOnClickListener()); 35 | } 36 | 37 | @Override 38 | public int getItemCount() { 39 | return menuItems.size(); 40 | } 41 | 42 | static class MenuItemViewHolder extends RecyclerView.ViewHolder { 43 | private final ImageView icon; 44 | private final TextView text; 45 | 46 | public MenuItemViewHolder(@NonNull View itemView) { 47 | super(itemView); 48 | icon = itemView.findViewById(R.id.menu_icon); 49 | text = itemView.findViewById(R.id.menu_text); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/adapter/MoreInfoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.adapter; 2 | 3 | import android.content.ClipData; 4 | import android.content.ClipboardManager; 5 | import android.content.Context; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | import android.widget.Toast; 11 | import androidx.annotation.NonNull; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | import com.projectmaterial.videos.R; 14 | import com.projectmaterial.videos.model.MoreInfo; 15 | import java.util.List; 16 | 17 | public class MoreInfoAdapter extends RecyclerView.Adapter { 18 | private Context context; 19 | private final List moreInfoItems; 20 | 21 | static class ViewHolder extends RecyclerView.ViewHolder { 22 | private final TextView title; 23 | private final TextView description; 24 | 25 | ViewHolder(View itemView) { 26 | super(itemView); 27 | title = itemView.findViewById(R.id.moreinfo_item_title); 28 | description = itemView.findViewById(R.id.moreinfo_item_description); 29 | } 30 | } 31 | 32 | public MoreInfoAdapter(Context context, List moreInfoItems) { 33 | this.context = context; 34 | this.moreInfoItems = moreInfoItems; 35 | } 36 | 37 | @NonNull 38 | @Override 39 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 40 | View itemView = LayoutInflater.from(context).inflate(R.layout.moreinfo_item_view, parent, false); 41 | return new ViewHolder(itemView); 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 46 | MoreInfo moreInfo = moreInfoItems.get(position); 47 | holder.title.setText(moreInfo.getTitle()); 48 | holder.description.setText(moreInfo.getDescription()); 49 | if (moreInfo.isCopyable()) { 50 | holder.itemView.setOnLongClickListener(view -> { 51 | ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); 52 | ClipData clipData = ClipData.newPlainText(moreInfo.getTitle(), moreInfo.getDescription()); 53 | clipboardManager.setPrimaryClip(clipData); 54 | Toast.makeText(context, R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show(); 55 | return true; 56 | }); 57 | } 58 | } 59 | 60 | @Override 61 | public int getItemCount() { 62 | return moreInfoItems.size(); 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/adapter/base/BaseVideoAdapter.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.adapter.base; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.recyclerview.widget.RecyclerView.Adapter; 5 | import androidx.recyclerview.widget.RecyclerView.ViewHolder; 6 | import com.projectmaterial.videos.util.SortingCriteria; 7 | import com.projectmaterial.videos.util.SortingOrder; 8 | 9 | public abstract class BaseVideoAdapter extends Adapter { 10 | 11 | @Override 12 | public abstract void onBindViewHolder(@NonNull T holder, int position); 13 | 14 | public abstract SortingCriteria.Video getSortingCriteria(); 15 | 16 | public abstract SortingOrder getSortingOrder(); 17 | 18 | public abstract void updateSortingCriteria(@NonNull SortingCriteria.Video newSortingCriteria); 19 | 20 | public abstract void updateSortingOrder(@NonNull SortingOrder newSortingOrder); 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/behavior/FragmentContainerViewBehavior.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.behavior; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import androidx.annotation.NonNull; 7 | import androidx.coordinatorlayout.widget.CoordinatorLayout; 8 | import androidx.fragment.app.FragmentContainerView; 9 | import com.google.android.material.bottomnavigation.BottomNavigationView; 10 | 11 | public class FragmentContainerViewBehavior extends CoordinatorLayout.Behavior { 12 | 13 | public FragmentContainerViewBehavior() { 14 | super(); 15 | } 16 | 17 | public FragmentContainerViewBehavior(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | @Override 22 | public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull FragmentContainerView child, @NonNull View dependency) { 23 | return dependency instanceof BottomNavigationView; 24 | } 25 | 26 | @Override 27 | public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull FragmentContainerView child, @NonNull View dependency) { 28 | if (dependency instanceof BottomNavigationView) { 29 | int bottomNavHeight = dependency.getHeight(); 30 | child.setClipChildren(false); 31 | child.setClipToPadding(false); 32 | child.setPadding(0, 0, 0, bottomNavHeight); 33 | return true; 34 | } 35 | return false; 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/comparator/CollectionComparatorDate.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.comparator; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.projectmaterial.videos.database.Collection; 5 | import com.projectmaterial.videos.database.Video; 6 | import com.projectmaterial.videos.util.CollectionUtils; 7 | import com.projectmaterial.videos.util.VideoUtils; 8 | import java.util.Comparator; 9 | 10 | public class CollectionComparatorDate implements Comparator { 11 | 12 | public CollectionComparatorDate() {} 13 | 14 | @Override 15 | public int compare(@NonNull String collection1, @NonNull String collection2) { 16 | String date1 = CollectionUtils.getLastChangedTime(collection1); 17 | String date2 = CollectionUtils.getLastChangedTime(collection2); 18 | return date1.compareToIgnoreCase(date2); 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/comparator/CollectionComparatorName.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.comparator; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.NonNull; 5 | import com.projectmaterial.videos.util.CollectionUtils; 6 | import java.util.Comparator; 7 | 8 | public class CollectionComparatorName implements Comparator { 9 | private Context context; 10 | 11 | public CollectionComparatorName(@NonNull Context context) { 12 | this.context = context; 13 | } 14 | 15 | @Override 16 | public int compare(@NonNull String collection1, @NonNull String collection2) { 17 | String name1 = CollectionUtils.getCollectionName(context, collection1); 18 | String name2 = CollectionUtils.getCollectionName(context, collection2); 19 | return name1.compareToIgnoreCase(name2); 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/comparator/CollectionComparatorVideoCount.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.comparator; 2 | 3 | import android.content.Context; 4 | import androidx.annotation.NonNull; 5 | import com.projectmaterial.videos.database.Collection; 6 | import java.util.Comparator; 7 | import java.util.Map; 8 | 9 | public class CollectionComparatorVideoCount implements Comparator { 10 | private Map collectionMap; 11 | 12 | public CollectionComparatorVideoCount(@NonNull Map collectionMap) { 13 | this.collectionMap = collectionMap; 14 | } 15 | 16 | @Override 17 | public int compare(@NonNull String collection1, @NonNull String collection2) { 18 | int count1 = collectionMap.get(collection1); 19 | int count2 = collectionMap.get(collection2); 20 | return Integer.compare(count1, count2); 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/projectmaterial/videos/comparator/VideoComparatorDate.java: -------------------------------------------------------------------------------- 1 | package com.projectmaterial.videos.comparator; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.projectmaterial.videos.database.Video; 5 | import com.projectmaterial.videos.util.VideoUtils; 6 | import java.util.Comparator; 7 | 8 | public class VideoComparatorDate implements Comparator